• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..03-May-2022-

.travis.ymlH A D30-Nov-201789 119

LICENSEH A D30-Nov-201711.1 KiB202169

Readme.mdH A D30-Nov-20174.8 KiB11892

deadlock.goH A D30-Nov-20178.4 KiB326242

deadlock_test.goH A D30-Nov-20173.4 KiB190180

stacktraces.goH A D30-Nov-20172.2 KiB10895

Readme.md

1# Online deadlock detection in go (golang). [Docs](https://godoc.org/github.com/sasha-s/go-deadlock). [![Build Status](https://travis-ci.org/sasha-s/go-deadlock.svg?branch=master)](https://travis-ci.org/sasha-s/go-deadlock)
2## Why
3Deadlocks happen and are painful to debug.
4
5## What
6go-deadlock provides (RW)Mutex drop-in replacements for sync.(RW)Mutex.
7It would not work if you create a spaghetti of channels.
8Mutexes only.
9
10## Installation
11```sh
12go get github.com/sasha-s/go-deadlock/...
13```
14
15## Usage
16```go
17import "github.com/sasha-s/go-deadlock"
18var mu deadlock.Mutex
19// Use normally, it works exactly like sync.Mutex does.
20mu.Lock()
21
22defer mu.Unlock()
23// Or
24var rw deadlock.RWMutex
25rw.RLock()
26defer rw.RUnlock()
27```
28
29### Deadlocks
30One of the most common sources of deadlocks is inconsistent lock ordering:
31say, you have two mutexes A and B, and in some goroutines you have
32```go
33A.Lock() // defer A.Unlock() or similar.
34...
35B.Lock() // defer B.Unlock() or similar.
36```
37And in another goroutine the order of locks is reversed:
38```go
39B.Lock() // defer B.Unlock() or similar.
40...
41A.Lock() // defer A.Unlock() or similar.
42```
43
44Another common sources of deadlocs is duplicate take a lock in a goroutine:
45```
46A.Rlock() or lock()
47
48A.lock() or A.RLock()
49```
50
51This 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.
52
53go-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!
54
55Each 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.
56
57In 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.
58
59
60## Sample output
61####Inconsistent lock ordering:
62```
63POTENTIAL DEADLOCK: Inconsistent locking. saw this ordering in one goroutine:
64happened before
65inmem.go:623 bttest.(*server).ReadModifyWriteRow { r.mu.Lock() } <<<<<
66inmem_test.go:118 bttest.TestConcurrentMutationsReadModifyAndGC.func4 { _, _ = s.ReadModifyWriteRow(ctx, rmw()) }
67
68happened after
69inmem.go:629 bttest.(*server).ReadModifyWriteRow { tbl.mu.RLock() } <<<<<
70inmem_test.go:118 bttest.TestConcurrentMutationsReadModifyAndGC.func4 { _, _ = s.ReadModifyWriteRow(ctx, rmw()) }
71
72in another goroutine: happened before
73inmem.go:799 bttest.(*table).gc { t.mu.RLock() } <<<<<
74inmem_test.go:125 bttest.TestConcurrentMutationsReadModifyAndGC.func5 { tbl.gc() }
75
76happend after
77inmem.go:814 bttest.(*table).gc { r.mu.Lock() } <<<<<
78inmem_test.go:125 bttest.TestConcurrentMutationsReadModifyAndGC.func5 { tbl.gc() }
79```
80
81#### Waiting for a lock for a long time:
82
83```
84POTENTIAL DEADLOCK:
85Previous place where the lock was grabbed
86goroutine 240 lock 0xc820160440
87inmem.go:799 bttest.(*table).gc { t.mu.RLock() } <<<<<
88inmem_test.go:125 bttest.TestConcurrentMutationsReadModifyAndGC.func5 { tbl.gc() }
89
90Have been trying to lock it again for more than 40ms
91goroutine 68 lock 0xc820160440
92inmem.go:785 bttest.(*table).mutableRow { t.mu.Lock() } <<<<<
93inmem.go:428 bttest.(*server).MutateRow { r := tbl.mutableRow(string(req.RowKey)) }
94inmem_test.go:111 bttest.TestConcurrentMutationsReadModifyAndGC.func3 { s.MutateRow(ctx, req) }
95
96
97Here is what goroutine 240 doing now
98goroutine 240 [select]:
99github.com/sasha-s/go-deadlock.lock(0xc82028ca10, 0x5189e0, 0xc82013a9b0)
100        /Users/sasha/go/src/github.com/sasha-s/go-deadlock/deadlock.go:163 +0x1640
101github.com/sasha-s/go-deadlock.(*Mutex).Lock(0xc82013a9b0)
102        /Users/sasha/go/src/github.com/sasha-s/go-deadlock/deadlock.go:54 +0x86
103google.golang.org/cloud/bigtable/bttest.(*table).gc(0xc820160440)
104        /Users/sasha/go/src/google.golang.org/cloud/bigtable/bttest/inmem.go:814 +0x28d
105google.golang.org/cloud/bigtable/bttest.TestConcurrentMutationsReadModifyAndGC.func5(0xc82015c760, 0xc820160440)      /Users/sasha/go/src/google.golang.org/cloud/bigtable/bttest/inmem_test.go:125 +0x48
106created by google.golang.org/cloud/bigtable/bttest.TestConcurrentMutationsReadModifyAndGC
107        /Users/sasha/go/src/google.golang.org/cloud/bigtable/bttest/inmem_test.go:126 +0xb6f
108```
109
110## Used in
111[cockroachdb: Potential deadlock between Gossip.SetStorage and Node.gossipStores](https://github.com/cockroachdb/cockroach/issues/7972)
112
113[bigtable/bttest: A race between GC and row mutations](https://code-review.googlesource.com#/c/5301/)
114
115## Need a mutex that works with net.context?
116I have [one](https://github.com/sasha-s/go-csync).
117
118