{"id":13413099,"url":"https://github.com/timandy/routine","last_synced_at":"2025-05-16T04:05:17.397Z","repository":{"id":38110100,"uuid":"450013749","full_name":"timandy/routine","owner":"timandy","description":"ThreadLocal for Golang.","archived":false,"fork":false,"pushed_at":"2025-03-25T01:42:36.000Z","size":296,"stargazers_count":257,"open_issues_count":1,"forks_count":27,"subscribers_count":6,"default_branch":"main","last_synced_at":"2025-04-08T14:11:10.353Z","etag":null,"topics":["fast-threadlocal","gls","go","goid","golang","goroutine","localstorage","threadlocal","tls"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/timandy.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-01-20T08:27:12.000Z","updated_at":"2025-04-08T13:21:45.000Z","dependencies_parsed_at":"2024-04-26T01:27:26.631Z","dependency_job_id":"260e0b36-0b9a-44d0-875a-35608b7fec0a","html_url":"https://github.com/timandy/routine","commit_stats":{"total_commits":309,"total_committers":5,"mean_commits":61.8,"dds":0.04530744336569581,"last_synced_commit":"ca9c2acf7fe94d3188594af217e0734e545dfbce"},"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/timandy%2Froutine","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/timandy%2Froutine/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/timandy%2Froutine/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/timandy%2Froutine/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/timandy","download_url":"https://codeload.github.com/timandy/routine/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254464895,"owners_count":22075570,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["fast-threadlocal","gls","go","goid","golang","goroutine","localstorage","threadlocal","tls"],"created_at":"2024-07-30T20:01:33.449Z","updated_at":"2025-05-16T04:05:12.388Z","avatar_url":"https://github.com/timandy.png","language":"Go","readme":"# routine\n\n[![Build Status](https://github.com/timandy/routine/actions/workflows/build.yml/badge.svg)](https://github.com/timandy/routine/actions)\n[![Codecov](https://codecov.io/gh/timandy/routine/branch/main/graph/badge.svg)](https://app.codecov.io/gh/timandy/routine)\n[![Go Report Card](https://goreportcard.com/badge/github.com/timandy/routine)](https://goreportcard.com/report/github.com/timandy/routine)\n[![Documentation](https://pkg.go.dev/badge/github.com/timandy/routine.svg)](https://pkg.go.dev/github.com/timandy/routine)\n[![Release](https://img.shields.io/github/release/timandy/routine.svg)](https://github.com/timandy/routine/releases)\n[![License](https://img.shields.io/github/license/timandy/routine.svg)](https://github.com/timandy/routine/blob/main/LICENSE)\n\n\u003e [中文版](README_zh.md)\n\n`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.\n\n# :house:Introduce\n\nFrom 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.\n\nIf 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`.\n\nOf 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.\n\nAnd the core goal of `routine` is to open up another way: Introduce `goroutine local storage` to the `Golang` world.\n\n# :loudspeaker:Update Notice\n\n:fire:**Version `1.1.5` introduces a new static mode.**\n\n- :rocket:Performance improved by over `20%`.\n\n- :rocket:Memory access is now safer.\n\n- :exclamation:The compile command requires additional parameters `-a -toolexec='routinex -v'`.\n\nFor more details, visit: [RoutineX Compiler](https://github.com/timandy/routinex)\n\n# :hammer_and_wrench:Usage \u0026 Demo\n\nThis chapter briefly introduces how to install and use the `routine` library.\n\n## Install\n\n```bash\ngo get github.com/timandy/routine\n```\n\n## Use `goid`\n\nThe following code simply demonstrates the use of `routine.Goid()`:\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/timandy/routine\"\n)\n\nfunc main() {\n\tgoid := routine.Goid()\n\tfmt.Printf(\"cur goid: %v\\n\", goid)\n\tgo func() {\n\t\tgoid := routine.Goid()\n\t\tfmt.Printf(\"sub goid: %v\\n\", goid)\n\t}()\n\n\t// Wait for the sub-coroutine to finish executing.\n\ttime.Sleep(time.Second)\n}\n```\n\nIn this example, the `main` function starts a new coroutine, so `Goid()` returns the main coroutine `1` and the child coroutine `6`:\n\n```text\ncur goid: 1\nsub goid: 6\n```\n\n## Use `ThreadLocal`\n\nThe following code briefly demonstrates `ThreadLocal`'s creation, setting, getting, spreading across coroutines, etc.:\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/timandy/routine\"\n)\n\nvar threadLocal = routine.NewThreadLocal[string]()\nvar inheritableThreadLocal = routine.NewInheritableThreadLocal[string]()\n\nfunc main() {\n\tthreadLocal.Set(\"hello world\")\n\tinheritableThreadLocal.Set(\"Hello world2\")\n\tfmt.Println(\"threadLocal:\", threadLocal.Get())\n\tfmt.Println(\"inheritableThreadLocal:\", inheritableThreadLocal.Get())\n\n\t// The child coroutine cannot read the previously assigned \"hello world\".\n\tgo func() {\n\t\tfmt.Println(\"threadLocal in goroutine:\", threadLocal.Get())\n\t\tfmt.Println(\"inheritableThreadLocal in goroutine:\", inheritableThreadLocal.Get())\n\t}()\n\n\t// 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.\n\troutine.Go(func() {\n\t\tfmt.Println(\"threadLocal in goroutine by Go:\", threadLocal.Get())\n\t\tfmt.Println(\"inheritableThreadLocal in goroutine by Go:\", inheritableThreadLocal.Get())\n\t})\n\n\t// You can also create a task via the WrapTask/WrapWaitTask/WrapWaitResultTask function, and all inheritable variables of the current coroutine can be automatically captured.\n\ttask := routine.WrapTask(func() {\n\t\tfmt.Println(\"threadLocal in task by WrapTask:\", threadLocal.Get())\n\t\tfmt.Println(\"inheritableThreadLocal in task by WrapTask:\", inheritableThreadLocal.Get())\n\t})\n\tgo task.Run()\n\n\t// Wait for the sub-coroutine to finish executing.\n\ttime.Sleep(time.Second)\n}\n```\n\nThe execution result is:\n\n```text\nthreadLocal: hello world\ninheritableThreadLocal: Hello world2\nthreadLocal in goroutine:\ninheritableThreadLocal in goroutine:\nthreadLocal in goroutine by Go:\ninheritableThreadLocal in goroutine by Go: Hello world2\nthreadLocal in task by WrapTask:\ninheritableThreadLocal in task by WrapTask: Hello world2\n```\n\n# :books:API\n\nThis chapter introduces in detail all the interfaces encapsulated by the `routine` library, as well as their core functions and implementation methods.\n\n## `Goid() uint64`\n\nGet the `goid` of the current `goroutine`.\n\nIt 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()`.\n\n## `NewThreadLocal[T any]() ThreadLocal[T]`\n\nCreate a new `ThreadLocal[T]` instance with the initial value stored with the default value of type `T`.\n\n## `NewThreadLocalWithInitial[T any](supplier Supplier[T]) ThreadLocal[T]`\n\nCreate a new `ThreadLocal[T]` instance with the initial value stored as the return value of the method `supplier()`.\n\n## `NewInheritableThreadLocal[T any]() ThreadLocal[T]`\n\nCreate a new `ThreadLocal[T]` instance with the initial value stored with the default value of type `T`.\nWhen a new coroutine is started via `Go()`, `GoWait()` or `GoWaitResult()`, the value of the current coroutine is copied to the new coroutine.\nWhen a new task is created via `WrapTask()`, `WrapWaitTask()` or `WrapWaitResultTask()`, the value of the current coroutine is captured to the new task.\n\n## `NewInheritableThreadLocalWithInitial[T any](supplier Supplier[T]) ThreadLocal[T]`\n\nCreate a new `ThreadLocal[T]` instance with the initial value stored as the return value of the method `supplier()`.\nWhen a new coroutine is started via `Go()`, `GoWait()` or `GoWaitResult()`, the value of the current coroutine is copied to the new coroutine.\nWhen a new task is created via `WrapTask()`, `WrapWaitTask()` or `WrapWaitResultTask()`, the value of the current coroutine is captured to the new task.\n\n## `WrapTask(fun Runnable) FutureTask[any]`\n\nCreate a new task and capture the `inheritableThreadLocals` from the current goroutine.\nThis function returns a `FutureTask` instance, but the return task will not run automatically.\nYou can run it in a sub-goroutine or goroutine-pool by `FutureTask.Run()` method, wait by `FutureTask.Get()` or `FutureTask.GetWithTimeout()` method.\nWhen 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.\n\n## `WrapWaitTask(fun CancelRunnable) FutureTask[any]`\n\nCreate a new task and capture the `inheritableThreadLocals` from the current goroutine.\nThis function returns a `FutureTask` instance, but the return task will not run automatically.\nYou can run it in a sub-goroutine or goroutine-pool by `FutureTask.Run()` method, wait by `FutureTask.Get()` or `FutureTask.GetWithTimeout()` method.\nWhen the returned task run `panic` will be caught, the `panic` will be trigger again when calling `FutureTask.Get()` or `FutureTask.GetWithTimeout()` method.\n\n## `WrapWaitResultTask[TResult any](fun CancelCallable[TResult]) FutureTask[TResult]`\n\nCreate a new task and capture the `inheritableThreadLocals` from the current goroutine.\nThis function returns a `FutureTask` instance, but the return task will not run automatically.\nYou 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.\nWhen the returned task run `panic` will be caught, the `panic` will be trigger again when calling `FutureTask.Get()` or `FutureTask.GetWithTimeout()` method.\n\n## `Go(fun Runnable)`\n\nStart a new coroutine and automatically copy all contextual `inheritableThreadLocals` data of the current coroutine to the new coroutine.\nAny `panic` while the child coroutine is executing will be caught and the stack automatically printed.\n\n## `GoWait(fun CancelRunnable) FutureTask[any]`\n\nStart a new coroutine and automatically copy all contextual `inheritableThreadLocals` data of the current coroutine to the new coroutine.\nYou can wait for the sub-coroutine to finish executing through the `FutureTask.Get()` or `FutureTask.GetWithTimeout()` method that returns a value.\nAny `panic` while the child coroutine is executing will be caught and thrown again when `FutureTask.Get()` or `FutureTask.GetWithTimeout()` is called.\n\n## `GoWaitResult[TResult any](fun CancelCallable[TResult]) FutureTask[TResult]`\n\nStart a new coroutine and automatically copy all contextual `inheritableThreadLocals` data of the current coroutine to the new coroutine.\nYou 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.\nAny `panic` while the child coroutine is executing will be caught and thrown again when `FutureTask.Get()` or `FutureTask.GetWithTimeout()` is called.\n\n[More API Documentation](https://pkg.go.dev/github.com/timandy/routine#section-documentation)\n\n# :wastebasket:Garbage Collection\n\n`routine` allocates a `thread` structure for each coroutine, which stores context variable information related to the coroutine.\n\nA pointer to this structure is stored on the `g.labels` field of the coroutine structure.\n\nWhen the coroutine finishes executing and exits, `g.labels` will be set to `nil`, no longer referencing the `thread` structure.\n\nThe `thread` structure will be collected at the next `GC`.\n\nIf the data stored in `thread` is not additionally referenced, these data will be collected together.\n\n# :globe_with_meridians:Support Grid\n\n|                | **`darwin`** | **`linux`** | **`windows`** | **`freebsd`** | **`js`** |                |\n|---------------:|:------------:|:-----------:|:-------------:|:-------------:|:--------:|:---------------|\n|      **`386`** |              |      ✅      |       ✅       |       ✅       |          | **`386`**      |\n|    **`amd64`** |      ✅       |      ✅      |       ✅       |       ✅       |          | **`amd64`**    |\n|    **`armv6`** |              |      ✅      |               |               |          | **`armv6`**    |\n|    **`armv7`** |              |      ✅      |               |               |          | **`armv7`**    |\n|    **`arm64`** |      ✅       |      ✅      |               |               |          | **`arm64`**    |\n|  **`loong64`** |              |      ✅      |               |               |          | **`loong64`**  |\n|     **`mips`** |              |      ✅      |               |               |          | **`mips`**     |\n|   **`mipsle`** |              |      ✅      |               |               |          | **`mipsle`**   |\n|   **`mips64`** |              |      ✅      |               |               |          | **`mips64`**   |\n| **`mips64le`** |              |      ✅      |               |               |          | **`mips64le`** |\n|    **`ppc64`** |              |      ✅      |               |               |          | **`ppc64`**    |\n|  **`ppc64le`** |              |      ✅      |               |               |          | **`ppc64le`**  |\n|  **`riscv64`** |              |      ✅      |               |               |          | **`riscv64`**  |\n|    **`s390x`** |              |      ✅      |               |               |          | **`s390x`**    |\n|     **`wasm`** |              |             |               |               |    ✅     | **`wasm`**     |\n|                | **`darwin`** | **`linux`** | **`windows`** | **`freebsd`** | **`js`** |                |\n\n✅: Supported\n\n# :pray:Thanks\n\nThanks to all [contributors](https://github.com/timandy/routine/graphs/contributors) for their contributions!\n\n# :scroll:*License*\n\n`routine` is released under the [Apache License 2.0](LICENSE).\n\n```\nCopyright 2021-2025 TimAndy\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    https://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n","funding_links":[],"categories":["Goroutines"],"sub_categories":["Search and Analytic Databases","检索及分析资料库"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftimandy%2Froutine","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftimandy%2Froutine","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftimandy%2Froutine/lists"}