{"id":13613608,"url":"https://github.com/sasha-s/go-deadlock","last_synced_at":"2026-03-07T09:17:30.625Z","repository":{"id":43128649,"uuid":"63840208","full_name":"sasha-s/go-deadlock","owner":"sasha-s","description":"Online deadlock detection in go (golang)","archived":false,"fork":false,"pushed_at":"2025-10-16T06:49:08.000Z","size":68,"stargazers_count":1146,"open_issues_count":6,"forks_count":79,"subscribers_count":19,"default_branch":"master","last_synced_at":"2025-10-17T06:42:36.035Z","etag":null,"topics":["deadlock","golang","mutex","online-deadlock-detection"],"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/sasha-s.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,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2016-07-21T05:38:30.000Z","updated_at":"2025-10-17T03:53:44.000Z","dependencies_parsed_at":"2024-01-13T22:59:38.842Z","dependency_job_id":"e2e11ecb-81c0-4c9a-a309-12152f0411db","html_url":"https://github.com/sasha-s/go-deadlock","commit_stats":{"total_commits":59,"total_committers":14,"mean_commits":4.214285714285714,"dds":"0.44067796610169496","last_synced_commit":"5afde13977e624ab3bd64e5801f75f9e8eb1f41b"},"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/sasha-s/go-deadlock","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sasha-s%2Fgo-deadlock","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sasha-s%2Fgo-deadlock/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sasha-s%2Fgo-deadlock/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sasha-s%2Fgo-deadlock/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sasha-s","download_url":"https://codeload.github.com/sasha-s/go-deadlock/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sasha-s%2Fgo-deadlock/sbom","scorecard":{"id":801128,"data":{"date":"2025-08-11","repo":{"name":"github.com/sasha-s/go-deadlock","commit":"b089b4ff92afbdaae7a53c19c7fb811f69aef6dd"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.7,"checks":[{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","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":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/go.yml:22: update your workflow using https://app.stepsecurity.io/secureworkflow/sasha-s/go-deadlock/go.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/go.yml:25: update your workflow using https://app.stepsecurity.io/secureworkflow/sasha-s/go-deadlock/go.yml/master?enable=pin","Info:   0 out of   2 GitHub-owned GitHubAction dependencies pinned"],"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":"1 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":"Code-Review","score":3,"reason":"Found 9/30 approved changesets -- score normalized to 3","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":"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":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/go.yml:1","Info: no jobLevel write permissions found"],"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":"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":"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":"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"}},{"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":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 9 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-23T10:30:25.006Z","repository_id":43128649,"created_at":"2025-08-23T10:30:25.007Z","updated_at":"2025-08-23T10:30:25.007Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30210688,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-07T09:02:10.694Z","status":"ssl_error","status_checked_at":"2026-03-07T09:02:08.429Z","response_time":53,"last_error":"SSL_read: 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":["deadlock","golang","mutex","online-deadlock-detection"],"created_at":"2024-08-01T20:00:51.599Z","updated_at":"2026-03-07T09:17:30.609Z","avatar_url":"https://github.com/sasha-s.png","language":"Go","funding_links":[],"categories":["开源类库","Go","Open source library"],"sub_categories":["调试","Debugging"],"readme":"# Online deadlock detection in go (golang). [![Try it online](https://img.shields.io/badge/try%20it-online-blue.svg)](https://wandbox.org/permlink/hJc6QCZowxbNm9WW) [![Docs](https://godoc.org/github.com/sasha-s/go-deadlock?status.svg)](https://godoc.org/github.com/sasha-s/go-deadlock) [![codecov](https://codecov.io/gh/sasha-s/go-deadlock/branch/main/graph/badge.svg)](https://codecov.io/gh/sasha-s/go-deadlock) [![version](https://badge.fury.io/gh/sasha-s%2Fgo-deadlock.svg)](https://github.com/sasha-s/go-deadlock/releases)  [![Go Report Card](https://goreportcard.com/badge/github.com/sasha-s/go-deadlock)](https://goreportcard.com/report/github.com/sasha-s/go-deadlock) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)\n\n## Why\nDeadlocks happen and are painful to debug.\n\n## What\ngo-deadlock provides (RW)Mutex drop-in replacements for sync.(RW)Mutex.\nIt would not work if you create a spaghetti of channels.\nMutexes only.\n\n## Installation\n```sh\ngo get github.com/sasha-s/go-deadlock/...\n```\n\n## Usage\n```go\nimport \"github.com/sasha-s/go-deadlock\"\nvar mu deadlock.Mutex\n// Use normally, it works exactly like sync.Mutex does.\nmu.Lock()\n\ndefer mu.Unlock()\n// Or\nvar rw deadlock.RWMutex\nrw.RLock()\ndefer rw.RUnlock()\n```\n\n### Deadlocks\nOne of the most common sources of deadlocks is inconsistent lock ordering:\nsay, you have two mutexes A and B, and in some goroutines you have\n```go\nA.Lock() // defer A.Unlock() or similar.\n...\nB.Lock() // defer B.Unlock() or similar.\n```\nAnd in another goroutine the order of locks is reversed:\n```go\nB.Lock() // defer B.Unlock() or similar.\n...\nA.Lock() // defer A.Unlock() or similar.\n```\n\nAnother common sources of deadlocks is duplicate take a lock in a goroutine:\n```\nA.Rlock() or lock()\n\nA.lock() or A.RLock()\n```\n\nThis does not guarantee a deadlock (maybe the goroutines above can never be running at the same time), but it usually a design flaw at least.\n\ngo-deadlock can detect such cases (unless you cross goroutine boundary - say lock A, then spawn a goroutine, block until it is singals, and lock B inside of the goroutine), even if the deadlock itself happens very infrequently and is painful to reproduce!\n\nEach time go-deadlock sees a lock attempt for lock B, it records the order A before B, for each lock that is currently being held in the same goroutine, and it prints (and exits the program by default) when it sees the locking order being violated.\n\nIn addition, if it sees that we are waiting on a lock for a long time (opts.DeadlockTimeout, 30 seconds by default), it reports a potential deadlock, also printing the stacktrace for a goroutine that is currently holding the lock we are desperately trying to grab.\n\n\n## Sample output\n#### Inconsistent lock ordering:\n```\nPOTENTIAL DEADLOCK: Inconsistent locking. saw this ordering in one goroutine:\nhappened before\ninmem.go:623 bttest.(*server).ReadModifyWriteRow { r.mu.Lock() } \u003c\u003c\u003c\u003c\u003c\ninmem_test.go:118 bttest.TestConcurrentMutationsReadModifyAndGC.func4 { _, _ = s.ReadModifyWriteRow(ctx, rmw()) }\n\nhappened after\ninmem.go:629 bttest.(*server).ReadModifyWriteRow { tbl.mu.RLock() } \u003c\u003c\u003c\u003c\u003c\ninmem_test.go:118 bttest.TestConcurrentMutationsReadModifyAndGC.func4 { _, _ = s.ReadModifyWriteRow(ctx, rmw()) }\n\nin another goroutine: happened before\ninmem.go:799 bttest.(*table).gc { t.mu.RLock() } \u003c\u003c\u003c\u003c\u003c\ninmem_test.go:125 bttest.TestConcurrentMutationsReadModifyAndGC.func5 { tbl.gc() }\n\nhappend after\ninmem.go:814 bttest.(*table).gc { r.mu.Lock() } \u003c\u003c\u003c\u003c\u003c\ninmem_test.go:125 bttest.TestConcurrentMutationsReadModifyAndGC.func5 { tbl.gc() }\n```\n\n#### Waiting for a lock for a long time:\n\n```\nPOTENTIAL DEADLOCK:\nPrevious place where the lock was grabbed\ngoroutine 240 lock 0xc820160440\ninmem.go:799 bttest.(*table).gc { t.mu.RLock() } \u003c\u003c\u003c\u003c\u003c\ninmem_test.go:125 bttest.TestConcurrentMutationsReadModifyAndGC.func5 { tbl.gc() }\n\nHave been trying to lock it again for more than 40ms\ngoroutine 68 lock 0xc820160440\ninmem.go:785 bttest.(*table).mutableRow { t.mu.Lock() } \u003c\u003c\u003c\u003c\u003c\ninmem.go:428 bttest.(*server).MutateRow { r := tbl.mutableRow(string(req.RowKey)) }\ninmem_test.go:111 bttest.TestConcurrentMutationsReadModifyAndGC.func3 { s.MutateRow(ctx, req) }\n\n\nHere is what goroutine 240 doing now\ngoroutine 240 [select]:\ngithub.com/sasha-s/go-deadlock.lock(0xc82028ca10, 0x5189e0, 0xc82013a9b0)\n        /Users/sasha/go/src/github.com/sasha-s/go-deadlock/deadlock.go:163 +0x1640\ngithub.com/sasha-s/go-deadlock.(*Mutex).Lock(0xc82013a9b0)\n        /Users/sasha/go/src/github.com/sasha-s/go-deadlock/deadlock.go:54 +0x86\ngoogle.golang.org/cloud/bigtable/bttest.(*table).gc(0xc820160440)\n        /Users/sasha/go/src/google.golang.org/cloud/bigtable/bttest/inmem.go:814 +0x28d\ngoogle.golang.org/cloud/bigtable/bttest.TestConcurrentMutationsReadModifyAndGC.func5(0xc82015c760, 0xc820160440)      /Users/sasha/go/src/google.golang.org/cloud/bigtable/bttest/inmem_test.go:125 +0x48\ncreated by google.golang.org/cloud/bigtable/bttest.TestConcurrentMutationsReadModifyAndGC\n        /Users/sasha/go/src/google.golang.org/cloud/bigtable/bttest/inmem_test.go:126 +0xb6f\n```\n\n## Used in\n[cockroachdb: Potential deadlock between Gossip.SetStorage and Node.gossipStores](https://github.com/cockroachdb/cockroach/issues/7972)\n\n[bigtable/bttest: A race between GC and row mutations](https://code-review.googlesource.com#/c/5301/)\n\n## Need a mutex that works with net.context?\nI have [one](https://github.com/sasha-s/go-csync).\n\n## Grabbing an RLock twice from the same goroutine\nThis is, surprisingly, not a good idea!\n\nFrom [RWMutex](https://golang.org/pkg/sync/#RWMutex) docs:\n\n\u003eIf a goroutine holds a RWMutex for reading and another goroutine might call Lock, no goroutine should expect to be able to acquire a read lock until the initial read lock is released. In particular, this prohibits recursive read locking. This is to ensure that the lock eventually becomes available; a blocked Lock call excludes new readers from acquiring the lock.\n\n\nThe following code will deadlock \u0026mdash; [run the example on playground](https://play.golang.org/p/AkL-W63nq5f) or [try it online with go-deadlock on wandbox](https://wandbox.org/permlink/JwnL0GMySBju4SII):\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"sync\"\n)\n\nfunc main() {\n\tvar mu sync.RWMutex\n\n\tchrlockTwice := make(chan struct{}) // Used to control rlockTwice\n\trlockTwice := func() {\n\t\tmu.RLock()\n\t\tfmt.Println(\"first Rlock succeeded\")\n\t\t\u003c-chrlockTwice\n\t\t\u003c-chrlockTwice\n\t\tfmt.Println(\"trying to Rlock again\")\n\t\tmu.RLock()\n\t\tfmt.Println(\"second Rlock succeeded\")\n\t\tmu.RUnlock()\n\t\tmu.RUnlock()\n\t}\n\n\tchLock := make(chan struct{}) // Used to contol lock\n\tlock := func() {\n\t\t\u003c-chLock\n\t\tfmt.Println(\"about to Lock\")\n\t\tmu.Lock()\n\t\tfmt.Println(\"Lock succeeded\")\n\t\tmu.Unlock()\n\t\t\u003c-chLock\n\t}\n\n\tcontrol := func() {\n\t\tchrlockTwice \u003c- struct{}{}\n\t\tchLock \u003c- struct{}{}\n\n\t\tclose(chrlockTwice)\n\t\tclose(chLock)\n\t}\n\n\tgo control()\n\tgo lock()\n\trlockTwice()\n}\n```\n## Build Tags and Compatibility Modes\n\ngo-deadlock supports multiple build configurations for different use cases:\n\n* **Normal mode** (default): Full deadlock detection with timer pooling\n* **Synctest mode**: Compatible with Go's `testing/synctest` package - use either:\n  * `-tags=deadlock_synctest` (recommended for Go 1.25+)\n  * `-tags=goexperiment.synctest` (for experimental synctest)\n* **Disabled mode** (`-tags=deadlock_disable`): Zero overhead, no detection\n\n**Why synctest mode?** `sync.Mutex` is not durably blocking in synctest bubbles, but channels are. Synctest mode uses channel-based mutexes to ensure proper behavior with `testing/synctest`.\n\n### Quick Examples\n\n```bash\n# Normal development/testing\ngo test ./...\n\n# Testing with synctest (Go 1.25+, recommended)\nGODEBUG=asynctimerchan=0 go test -tags=deadlock_synctest ./...\n\n# Testing with experimental synctest\nGODEBUG=asynctimerchan=0 go test -tags=goexperiment.synctest ./...\n\n# Production build with zero overhead\ngo build -tags=deadlock_disable ./...\n```\n\n## Configuring go-deadlock\n\nHave a look at [Opts](https://pkg.go.dev/github.com/sasha-s/go-deadlock#pkg-variables).\n\n* `Opts.Disable`: disables deadlock detection altogether (runtime option; see also `deadlock_disable` build tag)\n* `Opts.DisableLockOrderDetection`: disables lock order based deadlock detection.\n* `Opts.DeadlockTimeout`: blocking on mutex for longer than DeadlockTimeout is considered a deadlock. ignored if negative\n* `Opts.OnPotentialDeadlock`: callback for then deadlock is detected\n* `Opts.MaxMapSize`: size of happens before // happens after table\n* `Opts.PrintAllCurrentGoroutines`:  dump stacktraces of all goroutines when inconsistent locking is detected, verbose\n* `Opts.LogBuf`: where to write deadlock info/stacktraces\n* `Opts.TimerPool`: controls timer pooling behavior (auto-configured based on build tags)\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsasha-s%2Fgo-deadlock","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsasha-s%2Fgo-deadlock","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsasha-s%2Fgo-deadlock/lists"}