github.com/anacrolix/torrent@v1.61.0/deferrwl.go (about) 1 package torrent 2 3 import ( 4 "fmt" 5 "reflect" 6 7 g "github.com/anacrolix/generics" 8 "github.com/anacrolix/missinggo/v2/panicif" 9 "github.com/anacrolix/sync" 10 ) 11 12 // Runs deferred actions on Unlock. Note that actions are assumed to be the results of changes that 13 // would only occur with a write lock at present. The race detector should catch instances of defers 14 // without the write lock being held. 15 type lockWithDeferreds struct { 16 internal sync.RWMutex 17 unlockActions []func() 18 uniqueActions map[any]struct{} 19 // Currently unlocking, defers should not occur? 20 allowDefers bool 21 client *Client 22 } 23 24 func (me *lockWithDeferreds) Lock() { 25 me.internal.Lock() 26 panicif.True(me.allowDefers) 27 me.allowDefers = true 28 } 29 30 func (me *lockWithDeferreds) Unlock() { 31 // If this doesn't happen other clean up handlers will block on the lock. 32 defer me.internal.Unlock() 33 panicif.False(me.allowDefers) 34 me.allowDefers = false 35 me.client.unlockHandlers.run(me.client.slogger) 36 startLen := len(me.unlockActions) 37 var i int 38 for i = 0; i < len(me.unlockActions); i++ { 39 me.unlockActions[i]() 40 } 41 if i != len(me.unlockActions) { 42 panic(fmt.Sprintf("num deferred changed while running: %v -> %v", startLen, len(me.unlockActions))) 43 } 44 me.unlockActions = me.unlockActions[:0] 45 me.uniqueActions = nil 46 } 47 48 func (me *lockWithDeferreds) RLock() { 49 me.internal.RLock() 50 } 51 52 func (me *lockWithDeferreds) RUnlock() { 53 me.internal.RUnlock() 54 } 55 56 // Not allowed after unlock has started. 57 func (me *lockWithDeferreds) Defer(action func()) { 58 me.deferInner(action) 59 } 60 61 // Already guarded. 62 func (me *lockWithDeferreds) deferInner(action func()) { 63 panicif.False(me.allowDefers) 64 me.unlockActions = append(me.unlockActions, action) 65 } 66 67 // Protected from looping by once filter. 68 func (me *lockWithDeferreds) deferOnceInner(key any, action func()) { 69 panicif.False(me.allowDefers) 70 g.MakeMapIfNil(&me.uniqueActions) 71 if g.MapContains(me.uniqueActions, key) { 72 return 73 } 74 me.uniqueActions[key] = struct{}{} 75 me.deferInner(action) 76 } 77 78 // Protected from looping by once filter. Note that if arg is the receiver of action, it should 79 // match the receiver type (like being a pointer if the method takes a pointer receiver). 80 func (me *lockWithDeferreds) DeferUniqueUnaryFunc(arg any, action func()) { 81 me.deferOnceInner(unaryFuncKey(action, arg), action) 82 } 83 84 func unaryFuncKey(f func(), key any) funcAndArgKey { 85 return funcAndArgKey{ 86 funcStr: reflect.ValueOf(f).String(), 87 key: key, 88 } 89 } 90 91 type funcAndArgKey struct { 92 funcStr string 93 key any 94 }