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 ```