{"id":26675615,"url":"https://github.com/linkdata/deadlock","last_synced_at":"2025-04-12T08:11:28.678Z","repository":{"id":148332410,"uuid":"618295984","full_name":"linkdata/deadlock","owner":"linkdata","description":"Deadlock detection for Go","archived":false,"fork":false,"pushed_at":"2025-02-18T14:45:42.000Z","size":97,"stargazers_count":16,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-26T03:18:48.179Z","etag":null,"topics":["deadlock","deadlock-detection","go","golang","mutex","online-deadlock-detection"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/linkdata.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}},"created_at":"2023-03-24T06:55:00.000Z","updated_at":"2025-02-18T14:45:46.000Z","dependencies_parsed_at":"2025-02-18T10:38:27.016Z","dependency_job_id":null,"html_url":"https://github.com/linkdata/deadlock","commit_stats":null,"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/linkdata%2Fdeadlock","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/linkdata%2Fdeadlock/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/linkdata%2Fdeadlock/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/linkdata%2Fdeadlock/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/linkdata","download_url":"https://codeload.github.com/linkdata/deadlock/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248537136,"owners_count":21120710,"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":["deadlock","deadlock-detection","go","golang","mutex","online-deadlock-detection"],"created_at":"2025-03-26T03:18:53.455Z","updated_at":"2025-04-12T08:11:28.658Z","avatar_url":"https://github.com/linkdata.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![build](https://github.com/linkdata/deadlock/actions/workflows/go.yml/badge.svg)](https://github.com/linkdata/deadlock/actions/workflows/go.yml)\n[![coverage](https://coveralls.io/repos/github/linkdata/deadlock/badge.svg?branch=main)](https://coveralls.io/github/linkdata/deadlock?branch=main)\n[![goreport](https://goreportcard.com/badge/github.com/linkdata/deadlock)](https://goreportcard.com/report/github.com/linkdata/deadlock)\n[![Docs](https://godoc.org/github.com/linkdata/deadlock?status.svg)](https://godoc.org/github.com/linkdata/deadlock)\n\n# Preface\n\nBased on https://github.com/sasha-s/go-deadlock.\n\nChanges from that package:\n* Uses build tags to eliminate all overhead when not enabled\n* Tests pass race checker and has full code coverage\n* Guards against github.com/petermattis/goid not supporting the current Go version\n* Diagnostic output matches `-race` style and uses `runtime.CallersFrames` to get correct line numbers\n* Adds `deadlock.Enabled` and `deadlock.Debug` constants\n* Adds `Try(R)Lock()` when using go 1.18+\n* Drops the dummy implementations for types other than `Mutex` and `RWMutex`\n\nAlso uses significantly less memory and CPU:\n\n```\nUsing this package:\nBenchmarkLockSingle-24           2716491               444.1 ns/op            32 B/op          1 allocs/op\nBenchmarkLockParallel-24         2132055               483.0 ns/op            43 B/op          1 allocs/op\n\nUsing https://github.com/sasha-s/go-deadlock@v0.3.3:\nBenchmarkLockSingle-24           1000000              1468 ns/op             593 B/op          3 allocs/op\nBenchmarkLockParallel-24          808564              1313 ns/op             593 B/op          3 allocs/op\n```\n\n## Installation\n\n```sh\ngo get github.com/linkdata/deadlock\n```\n\n## Usage\n\nThe package enables itself when either the `deadlock` or `race` build tag is set, and the\n`nodeadlock` build tag is *not* set. The easiest way is to simply use `deadlock.(RW)Mutex` and\nrun or test your code with the race detector.\n\n```go\nimport \"github.com/linkdata/deadlock\"\n\nvar mu deadlock.Mutex\nmu.Lock()\ndefer mu.Unlock()\n\nvar rw deadlock.RWMutex\nrw.RLock()\ndefer rw.RUnlock()\n```\n\n```sh\ngo run -race .\n```\n\n## Deadlocks\n\nTaking the same lock twice in the same goroutine will deadlock:\n```go\nA.RLock() // or A.Lock()\n...\nA.Lock() // or A.RLock()\n```\n\nThose cases will be reported immediately when they occur. Also, in case we wait for a lock for more than \n`deadlock.Opts.DeadlockTimeout` (30 seconds by default), we also report that as a potential deadlock.\nSetting the `DeadlockTimeout` to zero disables this detection.\n\n#### Sample output\n```\nPOTENTIAL DEADLOCK:\ngoroutine 624 have been trying to lock 0xc0009a20d8 for more than 20ms:\n  github.com/linkdata/deadlock.(*DeadlockMutex).Lock()\n      /home/user/src/deadlock/deadlock.go:26 +0x113\n  github.com/linkdata/deadlock.TestHardDeadlock.func2()\n      /home/user/src/deadlock/deadlock_test.go:154 +0x92\n\ngoroutine 622 previously locked it from:\n  github.com/linkdata/deadlock.(*DeadlockMutex).Lock()\n      /home/user/src/deadlock/deadlock.go:26 +0x164\n  github.com/linkdata/deadlock.TestHardDeadlock()\n      /home/user/src/deadlock/deadlock_test.go:150 +0xe6\n\ngoroutine 622 current stack:\ngoroutine 622 [sleep]:\ntime.Sleep(0xf4240)\n        /usr/local/go/src/runtime/time.go:195 +0x135\ngithub.com/linkdata/deadlock.spinWait(0xc000988340, 0x0?, 0x1)\n        /home/user/src/deadlock/deadlock_test.go:25 +0x3e\ngithub.com/linkdata/deadlock.TestHardDeadlock(0xc000988340)\n        /home/user/src/deadlock/deadlock_test.go:157 +0x265\ntesting.tRunner(0xc000988340, 0x6187e8)\n        /usr/local/go/src/testing/testing.go:1576 +0x217\ncreated by testing.(*T).Run\n        /usr/local/go/src/testing/testing.go:1629 +0x806\n```\n\n## Inconsistent lock ordering\n\nOne of the most common sources of deadlocks is inconsistent lock ordering.\nIf you have two mutexes A and B, and in one goroutine 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```\nThis does not guarantee a deadlock (maybe the goroutines above can never be running at the same time), but it is bad practice.\nDetection is enabled by default, but can be disabled by setting `deadlock.Opts.MaxMapSize` to zero.\n\n#### Sample output\n```\nPOTENTIAL DEADLOCK: Inconsistent locking:\nin one goroutine: happened before\n  github.com/linkdata/deadlock.(*DeadlockRWMutex).Lock()\n      /home/user/src/deadlock/deadlock.go:55 +0xa8\n  github.com/linkdata/deadlock.TestLockOrder.func2()\n      /home/user/src/deadlock/deadlock_test.go:120 +0x34\n\nhappened after\n  github.com/linkdata/deadlock.(*DeadlockMutex).Lock()\n      /home/user/src/deadlock/deadlock.go:26 +0x11a\n  github.com/linkdata/deadlock.TestLockOrder.func2()\n      /home/user/src/deadlock/deadlock_test.go:121 +0xa9\n\nin another goroutine: happened before\n  github.com/linkdata/deadlock.(*DeadlockMutex).Lock()\n      /home/user/src/deadlock/deadlock.go:26 +0xa5\n  github.com/linkdata/deadlock.TestLockOrder.func3()\n      /home/user/src/deadlock/deadlock_test.go:129 +0x34\n\nhappened after\n  github.com/linkdata/deadlock.(*DeadlockRWMutex).RLock()\n      /home/user/src/deadlock/deadlock.go:74 +0x11a\n  github.com/linkdata/deadlock.TestLockOrder.func3()\n      /home/user/src/deadlock/deadlock_test.go:130 +0xa6\n```\n\n## Debugging constants\n\nIt's often helpful to run extra runtime checks during development \nand testing, but you don't want to have that code around in a\nproduction environment. Since these are constants, if the constant is\nfalse, code that depends on it being true gets removed entirely.\n\nWe define two:\n\n* `deadlock.Debug` is true if either `race` or `debug` are set.\n* `deadlock.Enabled` is true if either `race` or `deadlock` are set and `nodeadlock` is *not* set.\n\n```go\nif deadlock.Debug {\n    // extra checks or logging go here\n}\n```\n\n## Configuring\n\nOptions are stored in the global variable `deadlock.Opts`. See [Options](https://pkg.go.dev/github.com/linkdata/deadlock#Options).\n\n* `Opts.DeadlockTimeout`: blocking on mutex for longer than DeadlockTimeout is considered a deadlock, ignored if zero\n* `Opts.OnPotentialDeadlock`: callback for when a deadlock is detected, or panic if nil\n* `Opts.MaxMapSize`: size of happens before // happens after table, disables inconsistent locking order detection if zero\n* `Opts.PrintAllCurrentGoroutines`: if true, dump stacktraces of all goroutines when inconsistent locking is detected\n* `Opts.LogBuf`: where to write deadlock info/stacktraces, default is `os.Stderr`\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flinkdata%2Fdeadlock","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flinkdata%2Fdeadlock","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flinkdata%2Fdeadlock/lists"}