{"id":13413088,"url":"https://github.com/arunsworld/nursery","last_synced_at":"2026-01-12T12:24:05.674Z","repository":{"id":140509418,"uuid":"223641632","full_name":"arunsworld/nursery","owner":"arunsworld","description":"Structured Concurrency in Go","archived":false,"fork":false,"pushed_at":"2021-07-08T15:59:22.000Z","size":31,"stargazers_count":62,"open_issues_count":1,"forks_count":6,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-04-22T13:32:01.716Z","etag":null,"topics":["concurrency","golang","golang-library","goroutines","nursery"],"latest_commit_sha":null,"homepage":null,"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/arunsworld.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2019-11-23T19:26:02.000Z","updated_at":"2023-09-21T14:01:47.000Z","dependencies_parsed_at":"2024-01-07T22:24:12.869Z","dependency_job_id":null,"html_url":"https://github.com/arunsworld/nursery","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/arunsworld/nursery","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arunsworld%2Fnursery","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arunsworld%2Fnursery/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arunsworld%2Fnursery/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arunsworld%2Fnursery/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/arunsworld","download_url":"https://codeload.github.com/arunsworld/nursery/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arunsworld%2Fnursery/sbom","scorecard":{"id":210166,"data":{"date":"2025-08-11","repo":{"name":"github.com/arunsworld/nursery","commit":"ecfe7a688cfd866de0da8ecff34de72b34d22f53"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Code-Review","score":0,"reason":"Found 0/13 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: Apache License 2.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-17T00:38:03.029Z","repository_id":140509418,"created_at":"2025-08-17T00:38:03.029Z","updated_at":"2025-08-17T00:38:03.029Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28338976,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-12T12:22:26.515Z","status":"ssl_error","status_checked_at":"2026-01-12T12:22:10.856Z","response_time":98,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["concurrency","golang","golang-library","goroutines","nursery"],"created_at":"2024-07-30T20:01:33.315Z","updated_at":"2026-01-12T12:24:05.646Z","avatar_url":"https://github.com/arunsworld.png","language":"Go","funding_links":[],"categories":["Goroutines","Goroutines `goroutines的管理和使用`","Relational Databases"],"sub_categories":["Search and Analytic Databases","检索及分析资料库","SQL 查询语句构建库","Advanced Console UIs"],"readme":"# nursery: structured concurrency in Go\n[![GoDoc](https://godoc.org/github.com/arunsworld/nursery?status.svg)](https://godoc.org/github.com/arunsworld/nursery)\n[![GoReportCard](https://goreportcard.com/badge/github.com/arunsworld/nursery)](https://goreportcard.com/badge/github.com/arunsworld/nursery)\n[![CircleCI](https://circleci.com/gh/arunsworld/nursery.svg?style=svg)](https://circleci.com/gh/arunsworld/nursery)\n\u003ca href='https://github.com/jpoles1/gopherbadger' target='_blank'\u003e![gopherbadger-tag-do-not-edit](https://img.shields.io/badge/Go%20Coverage-100%25-brightgreen.svg?longCache=true\u0026style=flat)\u003c/a\u003e\n\n```go\nRunConcurrently(\n    // Job 1\n    func(context.Context, chan error) {\n        time.Sleep(time.Millisecond * 10)\n        log.Println(\"Job 1 done...\")\n    },\n    // Job 2\n    func(context.Context, chan error) {\n        time.Sleep(time.Millisecond * 5)\n        log.Println(\"Job 2 done...\")\n    },\n)\nlog.Println(\"All jobs done...\")\n```\n\n## Installation\n```bash\ngo get -u github.com/arunsworld/nursery\n```\n\n[Notes on structured concurrency, or: Go statement considered harmful](https://vorpus.org/blog/notes-on-structured-concurrency-or-go-statement-considered-harmful/#nurseries-a-structured-replacement-for-go-statements) is an article that compares the dangers of goto with the go statement.\n\nWhile I don't necessarily agree with the entire content I can appreciate that even with Go's high-level abstraction of concurrency using Goroutines, Channels \u0026 the select statement it is possible to end up with unreadable code, deadlocks, leaked goroutines, race conditions and poor error handling.\n\nImplementing a higher-level abstraction for the use-cases mentioned is very straightforward in Go and this simple package provides just that.\n\nThe following functions are provided:\n* `RunConcurrently(jobs ...ConcurrentJob) error`: takes an array of `ConcurrentJob`s and runs them concurrently ensuring that all jobs are completed before the call terminates. If all jobs terminate cleanly error is nil; otherwise the first non-nil error is returned.\n* `RunConcurrentlyWithContext(parentCtx context.Context, jobs ...ConcurrentJob) error`: is the RunConcurrently behavior but additionally wraps a context that's passed in allowing cancellations of the parentCtx to get propagated.\n* `RunMultipleCopiesConcurrently(copies int, job ConcurrentJob) error`: makes copies of the given job and runs them concurrently. This is useful for cases where we want to execute multiple slow consumers taking jobs from a channel until the job is finished. The channel itself can be fed by a producer that is run concurrently with the job running the consumers. Each job's context is also passed an unique index with key `nursery.JobID` - a 0 based int - that maybe used as a job identity if required.\n* `RunMultipleCopiesConcurrentlyWithContext(ctx context.Context, copies int, job ConcurrentJob) error`: is the RunMultipleCopiesConcurrently behavior with a context that allows cancellation to be propagated to the jobs.\n* `RunUntilFirstCompletion(jobs ...ConcurrentJob) error`: takes an array of `ConcurrentJob`s and runs them concurrently but terminates after the completion of the earliest completing job. A key point here is that despite early termination it blocks until all jobs have terminated (ie. released any used resources). If all jobs terminate cleanly error is nil; otherwise the first non-nil error is returned.\n* `RunUntilFirstCompletionWithContext(parentCtx context.Context, jobs ...ConcurrentJob) error`: is the RunUntilFirstCompletion behavior but additionally wraps a context that's passed in allowing cancellations of the parentCtx to get propagated.\n* `RunConcurrentlyWithTimeout(timeout time.Duration, jobs ...ConcurrentJob) error`: is similar in behavior to `RunConcurrently` except it also takes a timeout and can cause the function to terminate earlier if timeout has expired. As before we wait for all jobs to have cleanly terminated.\n* `RunUntilFirstCompletionWithTimeout(timeout time.Duration, jobs ...ConcurrentJob) error`: is similar in behavior to `RunUntilFirstCompletion` with an additional timeout clause.\n\n`ConcurrentJob` is a simple function that takes a context and error channel. We need to ensure that we're listening to the `Done()` channel on context and if invoked to clean-up resources and bail out. Errors are to be published to the error channel for proper handling.\n\nNote: while this package simplifies the semantics of defining and executing concurrent code it cannot protect against bad concurrent programming such as using shared resources across jobs leading to data corruption or panics due to race conditions.\n\nYou may also be interested in reading [Structured Concurrency in Go](https://medium.com/@arunsworld/structured-concurrency-in-go-b800c7c4434e).\n\nThe library includes a utility function: `IsContextDone(context.Context)` to check if the passed in context is done or not. This can be used as a guard clause in a for loop within a ConcurrentJob using the passed in context to decide whether to stop processing and return or continue.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farunsworld%2Fnursery","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Farunsworld%2Fnursery","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farunsworld%2Fnursery/lists"}