go - How can this pattern result in a deadlock? -
i have (lru) cache object , encounter deadlock... how possible?
type cache struct { mutex *sync.mutex ... } func (this *cache) init() { // guaranteed called once, in main() this.mutex = &sync.mutex{} } func (this *cache) f1() { // pattern accessing mute, @ top of function of 'cache' needed. this.mutex.lock() defer this.mutex.unlock() ... } func (this *cache) f2() { this.mutex.lock() defer this.mutex.unlock() ... }
in every function mutex
appears, accessed pattern only. , yet... deadlock. how come possible?
note: code has been running on production server 10 months , first time that.
edit: f1() can call (indirectly) f2() deadlock based on answers. true in code doesn't happen, wonder
deadlock may occur if 1 method of cache
calls method, , both contain lock()
call.
see example:
func (this *cache) f1() { this.mutex.lock() defer this.mutex.unlock() this.f2() } func (this *cache) f2() { this.mutex.lock() defer this.mutex.unlock() } func main() { c := &cache{} c.init() c.f1() fmt.println("hello, playground") }
output (try on go playground):
fatal error: goroutines asleep - deadlock! goroutine 1 [semacquire]: sync.runtime_semacquiremutex(0x1040a12c, 0x8) /usr/local/go/src/runtime/sema.go:62 +0x40 sync.(*mutex).lock(0x1040a128, 0x10429f5c) /usr/local/go/src/sync/mutex.go:87 +0xa0 main.(*cache).f2(0x10429f94, 0x1100c0) /tmp/sandbox647646735/main.go:23 +0x40 main.(*cache).f1(0x10429f94, 0xdf6e0) /tmp/sandbox647646735/main.go:19 +0xa0 main.main() /tmp/sandbox647646735/main.go:30 +0x60
note there not need have direct call 1 method other, may transitive call. example cache.f1()
may call foo()
may "standalone" function, , if foo()
calls cache.f2()
, we're @ same deadlock.
improvements:
don't name receiver this
, not idiomatic. may call c
. read more here: in go naming receiver variable 'self' misleading or practice?
you may embed mutexes, making convenient use , eliminate need initialization. read more here: when embed mutex in struct in go?
type cache struct { sync.mutex } func (c *cache) f1() { c.lock() defer c.unlock() c.f2() } func (c *cache) f2() { c.lock() defer c.unlock() } func main() { c := &cache{} c.f1() fmt.println("hello, playground") }
of course causes deadlock. try on go playground. note inherently exposes mutex (as embedded type starts lowecae letter), able call lock()
, unlock()
methods. depends on case whether problem.
Comments
Post a Comment