github.com/xg0n/routine@v0.0.0-20240119033701-c364deb94aee/README.md (about)

     1  # routine
     2  
     3  [![Build Status](https://github.com/timandy/routine/actions/workflows/build.yml/badge.svg)](https://github.com/timandy/routine/actions)
     4  [![Codecov](https://codecov.io/gh/timandy/routine/branch/main/graph/badge.svg)](https://app.codecov.io/gh/timandy/routine)
     5  [![Go Report Card](https://goreportcard.com/badge/github.com/timandy/routine)](https://goreportcard.com/report/github.com/timandy/routine)
     6  [![Documentation](https://pkg.go.dev/badge/github.com/timandy/routine.svg)](https://pkg.go.dev/github.com/timandy/routine)
     7  [![Release](https://img.shields.io/github/release/timandy/routine.svg)](https://github.com/timandy/routine/releases)
     8  [![License](https://img.shields.io/github/license/timandy/routine.svg)](https://github.com/timandy/routine/blob/main/LICENSE)
     9  
    10  > [中文版](README_zh.md)
    11  
    12  `routine` encapsulates and provides some easy-to-use, non-competitive, high-performance `goroutine` context access interfaces, which can help you access coroutine context information more gracefully.
    13  
    14  # Introduce
    15  
    16  From the very beginning of its design, the `Golang` language has spared no effort to shield the concept of coroutine context from developers, including the acquisition of coroutine `goid`, the state of coroutine within the process, and the storage of coroutine context.
    17  
    18  If you have used other languages such as `C++`, `Java` and so on, then you must be familiar with `ThreadLocal`, but after starting to use `Golang`, you will be deeply confused and distressed by the lack of convenient functions like `ThreadLocal`.
    19  
    20  Of course, you can choose to use `Context`, which carries all the context information, appears in the first input parameter of all functions, and then shuttles around your system.
    21  
    22  And the core goal of `routine` is to open up another way: Introduce `goroutine local storage` to the `Golang` world.
    23  
    24  # Usage & Demo
    25  
    26  This chapter briefly introduces how to install and use the `routine` library.
    27  
    28  ## Install
    29  
    30  ```bash
    31  go get github.com/timandy/routine
    32  ```
    33  
    34  ## Use `goid`
    35  
    36  The following code simply demonstrates the use of `routine.Goid()`:
    37  
    38  ```go
    39  package main
    40  
    41  import (
    42  	"fmt"
    43  	"time"
    44  
    45  	"github.com/timandy/routine"
    46  )
    47  
    48  func main() {
    49  	goid := routine.Goid()
    50  	fmt.Printf("cur goid: %v\n", goid)
    51  	go func() {
    52  		goid := routine.Goid()
    53  		fmt.Printf("sub goid: %v\n", goid)
    54  	}()
    55  
    56  	// Wait for the sub-coroutine to finish executing.
    57  	time.Sleep(time.Second)
    58  }
    59  ```
    60  
    61  In this example, the `main` function starts a new coroutine, so `Goid()` returns the main coroutine `1` and the child coroutine `6`:
    62  
    63  ```text
    64  cur goid: 1
    65  sub goid: 6
    66  ```
    67  
    68  ## Use `ThreadLocal`
    69  
    70  The following code briefly demonstrates `ThreadLocal`'s creation, setting, getting, spreading across coroutines, etc.:
    71  
    72  ```go
    73  package main
    74  
    75  import (
    76  	"fmt"
    77  	"time"
    78  
    79  	"github.com/timandy/routine"
    80  )
    81  
    82  var threadLocal = routine.NewThreadLocal[string]()
    83  var inheritableThreadLocal = routine.NewInheritableThreadLocal[string]()
    84  
    85  func main() {
    86  	threadLocal.Set("hello world")
    87  	inheritableThreadLocal.Set("Hello world2")
    88  	fmt.Println("threadLocal:", threadLocal.Get())
    89  	fmt.Println("inheritableThreadLocal:", inheritableThreadLocal.Get())
    90  
    91  	// The child coroutine cannot read the previously assigned "hello world".
    92  	go func() {
    93  		fmt.Println("threadLocal in goroutine:", threadLocal.Get())
    94  		fmt.Println("inheritableThreadLocal in goroutine:", inheritableThreadLocal.Get())
    95  	}()
    96  
    97  	// However, a new sub-coroutine can be started via the Go/GoWait/GoWaitResult function, and all inheritable variables of the current coroutine can be passed automatically.
    98  	routine.Go(func() {
    99  		fmt.Println("threadLocal in goroutine by Go:", threadLocal.Get())
   100  		fmt.Println("inheritableThreadLocal in goroutine by Go:", inheritableThreadLocal.Get())
   101  	})
   102  
   103  	// You can also create a task via the WrapTask/WrapWaitTask/WrapWaitResultTask function, and all inheritable variables of the current coroutine can be automatically captured.
   104  	task := routine.WrapTask(func() {
   105  		fmt.Println("threadLocal in task by WrapTask:", threadLocal.Get())
   106  		fmt.Println("inheritableThreadLocal in task by WrapTask:", inheritableThreadLocal.Get())
   107  	})
   108  	go task.Run()
   109  
   110  	// Wait for the sub-coroutine to finish executing.
   111  	time.Sleep(time.Second)
   112  }
   113  ```
   114  
   115  The execution result is:
   116  
   117  ```text
   118  threadLocal: hello world
   119  inheritableThreadLocal: Hello world2
   120  threadLocal in goroutine:
   121  inheritableThreadLocal in goroutine:
   122  threadLocal in goroutine by Go:
   123  inheritableThreadLocal in goroutine by Go: Hello world2
   124  threadLocal in task by WrapTask:
   125  inheritableThreadLocal in task by WrapTask: Hello world2
   126  ```
   127  
   128  # API
   129  
   130  This chapter introduces in detail all the interfaces encapsulated by the `routine` library, as well as their core functions and implementation methods.
   131  
   132  ## `Goid() int64`
   133  
   134  Get the `goid` of the current `goroutine`.
   135  
   136  It can be obtained directly through assembly code under `386`, `amd64`, `armv6`, `armv7`, `arm64`, `loong64`, `mips`, `mipsle`, `mips64`, `mips64le`, `ppc64`, `ppc64le`, `riscv64`, `s390x`, `wasm` architectures. This operation has extremely high performance and the time-consuming is usually only one-fifth of `rand.Int()`.
   137  
   138  ## `NewThreadLocal[T any]() ThreadLocal[T]`
   139  
   140  Create a new `ThreadLocal[T]` instance with the initial value stored with the default value of type `T`.
   141  
   142  ## `NewThreadLocalWithInitial[T any](supplier Supplier[T]) ThreadLocal[T]`
   143  
   144  Create a new `ThreadLocal[T]` instance with the initial value stored as the return value of the method `supplier()`.
   145  
   146  ## `NewInheritableThreadLocal[T any]() ThreadLocal[T]`
   147  
   148  Create a new `ThreadLocal[T]` instance with the initial value stored with the default value of type `T`.
   149  When a new coroutine is started via `Go()`, `GoWait()` or `GoWaitResult()`, the value of the current coroutine is copied to the new coroutine.
   150  When a new task is created via `WrapTask()`, `WrapWaitTask()` or `WrapWaitResultTask()`, the value of the current coroutine is captured to the new task.
   151  
   152  ## `NewInheritableThreadLocalWithInitial[T any](supplier Supplier[T]) ThreadLocal[T]`
   153  
   154  Create a new `ThreadLocal[T]` instance with the initial value stored as the return value of the method `supplier()`.
   155  When a new coroutine is started via `Go()`, `GoWait()` or `GoWaitResult()`, the value of the current coroutine is copied to the new coroutine.
   156  When a new task is created via `WrapTask()`, `WrapWaitTask()` or `WrapWaitResultTask()`, the value of the current coroutine is captured to the new task.
   157  
   158  ## `WrapTask(fun Runnable) FutureTask[any]`
   159  
   160  Create a new task and capture the `inheritableThreadLocals` from the current goroutine.
   161  This function returns a `FutureTask` instance, but the return task will not run automatically.
   162  You can run it in a sub-goroutine or goroutine-pool by `FutureTask.Run()` method, wait by `FutureTask.Get()` or `FutureTask.GetWithTimeout()` method.
   163  When the returned task run `panic` will be caught and error stack will be printed, the `panic` will be trigger again when calling `FutureTask.Get()` or `FutureTask.GetWithTimeout()` method.
   164  
   165  ## `WrapWaitTask(fun CancelRunnable) FutureTask[any]`
   166  
   167  Create a new task and capture the `inheritableThreadLocals` from the current goroutine.
   168  This function returns a `FutureTask` instance, but the return task will not run automatically.
   169  You can run it in a sub-goroutine or goroutine-pool by `FutureTask.Run()` method, wait by `FutureTask.Get()` or `FutureTask.GetWithTimeout()` method.
   170  When the returned task run `panic` will be caught, the `panic` will be trigger again when calling `FutureTask.Get()` or `FutureTask.GetWithTimeout()` method.
   171  
   172  ## `WrapWaitResultTask[TResult any](fun CancelCallable[TResult]) FutureTask[TResult]`
   173  
   174  Create a new task and capture the `inheritableThreadLocals` from the current goroutine.
   175  This function returns a `FutureTask` instance, but the return task will not run automatically.
   176  You can run it in a sub-goroutine or goroutine-pool by `FutureTask.Run()` method, wait and get result by `FutureTask.Get()` or `FutureTask.GetWithTimeout()` method.
   177  When the returned task run `panic` will be caught, the `panic` will be trigger again when calling `FutureTask.Get()` or `FutureTask.GetWithTimeout()` method.
   178  
   179  ## `Go(fun Runnable)`
   180  
   181  Start a new coroutine and automatically copy all contextual `inheritableThreadLocals` data of the current coroutine to the new coroutine.
   182  Any `panic` while the child coroutine is executing will be caught and the stack automatically printed.
   183  
   184  ## `GoWait(fun CancelRunnable) FutureTask[any]`
   185  
   186  Start a new coroutine and automatically copy all contextual `inheritableThreadLocals` data of the current coroutine to the new coroutine.
   187  You can wait for the sub-coroutine to finish executing through the `FutureTask.Get()` or `FutureTask.GetWithTimeout()` method that returns a value.
   188  Any `panic` while the child coroutine is executing will be caught and thrown again when `FutureTask.Get()` or `FutureTask.GetWithTimeout()` is called.
   189  
   190  ## `GoWaitResult[TResult any](fun CancelCallable[TResult]) FutureTask[TResult]`
   191  
   192  Start a new coroutine and automatically copy all contextual `inheritableThreadLocals` data of the current coroutine to the new coroutine.
   193  You can wait for the sub-coroutine to finish executing and get the return value through the `FutureTask.Get()` or `FutureTask.GetWithTimeout()` method of the return value.
   194  Any `panic` while the child coroutine is executing will be caught and thrown again when `FutureTask.Get()` or `FutureTask.GetWithTimeout()` is called.
   195  
   196  [More API Documentation](https://pkg.go.dev/github.com/timandy/routine#section-documentation)
   197  
   198  # Garbage Collection
   199  
   200  `routine` allocates a `thread` structure for each coroutine, which stores context variable information related to the coroutine.
   201  
   202  A pointer to this structure is stored on the `g.labels` field of the coroutine structure.
   203  
   204  When the coroutine finishes executing and exits, `g.labels` will be set to `nil`, no longer referencing the `thread` structure.
   205  
   206  The `thread` structure will be collected at the next `GC`.
   207  
   208  If the data stored in `thread` is not additionally referenced, these data will be collected together.
   209  
   210  # Support Grid
   211  
   212  |                | **`darwin`** | **`linux`** | **`windows`** | **`freebsd`** | **`js`** |                |
   213  |---------------:|:------------:|:-----------:|:-------------:|:-------------:|:--------:|:---------------|
   214  |      **`386`** |              |      ✅      |       ✅       |       ✅       |          | **`386`**      |
   215  |    **`amd64`** |      ✅       |      ✅      |       ✅       |       ✅       |          | **`amd64`**    |
   216  |    **`armv6`** |              |      ✅      |               |               |          | **`armv6`**    |
   217  |    **`armv7`** |              |      ✅      |               |               |          | **`armv7`**    |
   218  |    **`arm64`** |      ✅       |      ✅      |               |               |          | **`arm64`**    |
   219  |  **`loong64`** |              |      ✅      |               |               |          | **`loong64`**  |
   220  |     **`mips`** |              |      ✅      |               |               |          | **`mips`**     |
   221  |   **`mipsle`** |              |      ✅      |               |               |          | **`mipsle`**   |
   222  |   **`mips64`** |              |      ✅      |               |               |          | **`mips64`**   |
   223  | **`mips64le`** |              |      ✅      |               |               |          | **`mips64le`** |
   224  |    **`ppc64`** |              |      ✅      |               |               |          | **`ppc64`**    |
   225  |  **`ppc64le`** |              |      ✅      |               |               |          | **`ppc64le`**  |
   226  |  **`riscv64`** |              |      ✅      |               |               |          | **`riscv64`**  |
   227  |    **`s390x`** |              |      ✅      |               |               |          | **`s390x`**    |
   228  |     **`wasm`** |              |             |               |               |    ✅     | **`wasm`**     |
   229  |                | **`darwin`** | **`linux`** | **`windows`** | **`freebsd`** | **`js`** |                |
   230  
   231  ✅: Supported
   232  
   233  # Thanks
   234  
   235  Thanks to all [contributors](https://github.com/timandy/routine/graphs/contributors) for their contributions!
   236  
   237  # *License*
   238  
   239  `routine` is released under the [Apache License 2.0](LICENSE).
   240  
   241  ```
   242  Copyright 2021-2024 TimAndy
   243  
   244  Licensed under the Apache License, Version 2.0 (the "License");
   245  you may not use this file except in compliance with the License.
   246  You may obtain a copy of the License at
   247  
   248      https://www.apache.org/licenses/LICENSE-2.0
   249  
   250  Unless required by applicable law or agreed to in writing, software
   251  distributed under the License is distributed on an "AS IS" BASIS,
   252  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   253  See the License for the specific language governing permissions and
   254  limitations under the License.
   255  ```