@@ -21,7 +21,7 @@ var Opts = struct {
2121 // Would disable lock order based deadlock detection if DisableLockOrderDetection == true.
2222 DisableLockOrderDetection bool
2323 // Waiting for a lock for longer than DeadlockTimeout is considered a deadlock.
24- // Ignored is DeadlockTimeout <= 0.
24+ // Ignored if DeadlockTimeout <= 0.
2525 DeadlockTimeout time.Duration
2626 // OnPotentialDeadlock is called each time a potential deadlock is detected -- either based on
2727 // lock order or on lock wait time.
@@ -71,6 +71,9 @@ type WaitGroup struct {
7171 sync.WaitGroup
7272}
7373
74+ // NewCond is a sync.NewCond wrapper
75+ var NewCond = sync .NewCond
76+
7477// A Mutex is a drop-in replacement for sync.Mutex.
7578// Performs deadlock detection unless disabled in Opts.
7679type Mutex struct {
@@ -157,12 +160,12 @@ func (m *RWMutex) RLocker() sync.Locker {
157160 return (* rlocker )(m )
158161}
159162
160- func preLock (skip int , p interface {}) {
161- lo .preLock (skip , p )
163+ func preLock (stack [] uintptr , p interface {}) {
164+ lo .preLock (stack , p )
162165}
163166
164- func postLock (skip int , p interface {}) {
165- lo .postLock (skip , p )
167+ func postLock (stack [] uintptr , p interface {}) {
168+ lo .postLock (stack , p )
166169}
167170
168171func postUnlock (p interface {}) {
@@ -174,65 +177,88 @@ func lock(lockFn func(), ptr interface{}) {
174177 lockFn ()
175178 return
176179 }
177- preLock (4 , ptr )
180+ stack := callers (1 )
181+ preLock (stack , ptr )
178182 if Opts .DeadlockTimeout <= 0 {
179183 lockFn ()
180184 } else {
181185 ch := make (chan struct {})
182- go func () {
183- for {
184- t := time .NewTimer (Opts .DeadlockTimeout )
185- defer t .Stop () // This runs after the losure finishes, but it's OK.
186- select {
187- case <- t .C :
188- lo .mu .Lock ()
189- prev , ok := lo .cur [ptr ]
190- if ! ok {
191- lo .mu .Unlock ()
192- break // Nobody seems to be holding the lock, try again.
193- }
194- Opts .mu .Lock ()
195- fmt .Fprintln (Opts .LogBuf , header )
196- fmt .Fprintln (Opts .LogBuf , "Previous place where the lock was grabbed" )
197- fmt .Fprintf (Opts .LogBuf , "goroutine %v lock %p\n " , prev .gid , ptr )
198- printStack (Opts .LogBuf , prev .stack )
199- fmt .Fprintln (Opts .LogBuf , "Have been trying to lock it again for more than" , Opts .DeadlockTimeout )
200- fmt .Fprintf (Opts .LogBuf , "goroutine %v lock %p\n " , goid .Get (), ptr )
201- printStack (Opts .LogBuf , callers (2 ))
202- stacks := stacks ()
203- grs := bytes .Split (stacks , []byte ("\n \n " ))
204- for _ , g := range grs {
205- if goid .ExtractGID (g ) == prev .gid {
206- fmt .Fprintln (Opts .LogBuf , "Here is what goroutine" , prev .gid , "doing now" )
207- Opts .LogBuf .Write (g )
208- fmt .Fprintln (Opts .LogBuf )
209- }
210- }
211- lo .other (ptr )
212- if Opts .PrintAllCurrentGoroutines {
213- fmt .Fprintln (Opts .LogBuf , "All current goroutines:" )
214- Opts .LogBuf .Write (stacks )
215- }
216- fmt .Fprintln (Opts .LogBuf )
217- if buf , ok := Opts .LogBuf .(* bufio.Writer ); ok {
218- buf .Flush ()
219- }
220- Opts .mu .Unlock ()
221- lo .mu .Unlock ()
222- Opts .OnPotentialDeadlock ()
223- <- ch
224- return
225- case <- ch :
226- return
227- }
228- }
229- }()
186+ currentID := goid .Get ()
187+ go checkDeadlock (stack , ptr , currentID , ch )
230188 lockFn ()
231- postLock (4 , ptr )
189+ postLock (stack , ptr )
232190 close (ch )
233191 return
234192 }
235- postLock (4 , ptr )
193+ postLock (stack , ptr )
194+ }
195+
196+ var timersPool sync.Pool
197+
198+ func acquireTimer (d time.Duration ) * time.Timer {
199+ t , ok := timersPool .Get ().(* time.Timer )
200+ if ok {
201+ _ = t .Reset (d )
202+ return t
203+ }
204+ return time .NewTimer (Opts .DeadlockTimeout )
205+ }
206+
207+ func releaseTimer (t * time.Timer ) {
208+ if ! t .Stop () {
209+ <- t .C
210+ }
211+ timersPool .Put (t )
212+ }
213+
214+ func checkDeadlock (stack []uintptr , ptr interface {}, currentID int64 , ch <- chan struct {}) {
215+ t := acquireTimer (Opts .DeadlockTimeout )
216+ defer releaseTimer (t )
217+ for {
218+ select {
219+ case <- t .C :
220+ lo .mu .Lock ()
221+ prev , ok := lo .cur [ptr ]
222+ if ! ok {
223+ lo .mu .Unlock ()
224+ break // Nobody seems to be holding the lock, try again.
225+ }
226+ Opts .mu .Lock ()
227+ fmt .Fprintln (Opts .LogBuf , header )
228+ fmt .Fprintln (Opts .LogBuf , "Previous place where the lock was grabbed" )
229+ fmt .Fprintf (Opts .LogBuf , "goroutine %v lock %p\n " , prev .gid , ptr )
230+ printStack (Opts .LogBuf , prev .stack )
231+ fmt .Fprintln (Opts .LogBuf , "Have been trying to lock it again for more than" , Opts .DeadlockTimeout )
232+ fmt .Fprintf (Opts .LogBuf , "goroutine %v lock %p\n " , currentID , ptr )
233+ printStack (Opts .LogBuf , stack )
234+ stacks := stacks ()
235+ grs := bytes .Split (stacks , []byte ("\n \n " ))
236+ for _ , g := range grs {
237+ if goid .ExtractGID (g ) == prev .gid {
238+ fmt .Fprintln (Opts .LogBuf , "Here is what goroutine" , prev .gid , "doing now" )
239+ Opts .LogBuf .Write (g )
240+ fmt .Fprintln (Opts .LogBuf )
241+ }
242+ }
243+ lo .other (ptr )
244+ if Opts .PrintAllCurrentGoroutines {
245+ fmt .Fprintln (Opts .LogBuf , "All current goroutines:" )
246+ Opts .LogBuf .Write (stacks )
247+ }
248+ fmt .Fprintln (Opts .LogBuf )
249+ if buf , ok := Opts .LogBuf .(* bufio.Writer ); ok {
250+ buf .Flush ()
251+ }
252+ Opts .mu .Unlock ()
253+ lo .mu .Unlock ()
254+ Opts .OnPotentialDeadlock ()
255+ <- ch
256+ return
257+ case <- ch :
258+ return
259+ }
260+ t .Reset (Opts .DeadlockTimeout )
261+ }
236262}
237263
238264type lockOrder struct {
@@ -265,19 +291,17 @@ func newLockOrder() *lockOrder {
265291 }
266292}
267293
268- func (l * lockOrder ) postLock (skip int , p interface {}) {
269- stack := callers (skip )
294+ func (l * lockOrder ) postLock (stack []uintptr , p interface {}) {
270295 gid := goid .Get ()
271296 l .mu .Lock ()
272297 l .cur [p ] = stackGID {stack , gid }
273298 l .mu .Unlock ()
274299}
275300
276- func (l * lockOrder ) preLock (skip int , p interface {}) {
301+ func (l * lockOrder ) preLock (stack [] uintptr , p interface {}) {
277302 if Opts .DisableLockOrderDetection {
278303 return
279304 }
280- stack := callers (skip )
281305 gid := goid .Get ()
282306 l .mu .Lock ()
283307 for b , bs := range l .cur {
0 commit comments