codeberg.org/gruf/go-mutexes@v1.5.0/muctx/ctx.go (about) 1 package muctx 2 3 import ( 4 "context" 5 "sync/atomic" 6 _ "unsafe" 7 8 "codeberg.org/gruf/go-mutexes" 9 ) 10 11 // Lock represents a locked key in a mutex map, 12 // normally only returned as an unlock function 13 // but wrapped here to provide a storable type 14 // with metadata for context values. 15 type Lock struct { 16 unlk func() 17 lock *uint32 18 ltyp uint8 19 } 20 21 // RLockCtx will check given context for rlock, else acquire an rlock on the mutex at key in the map. 22 func RLockCtx(mm *mutexes.MutexMap, ctx context.Context, key string) (child context.Context, lock *Lock) { 23 return lockCtx(mm, ctx, key, lockTypeRead) 24 } 25 26 // LockCtx will check given context for lock, else acquire a lock on the mutex at key in the map. 27 func LockCtx(mm *mutexes.MutexMap, ctx context.Context, key string) (child context.Context, lock *Lock) { 28 return lockCtx(mm, ctx, key, lockTypeWrite) 29 } 30 31 func lockCtx(mm *mutexes.MutexMap, ctx context.Context, key string, lt uint8) (context.Context, *Lock) { 32 type ctxkey string 33 34 // Look for an existing Lock{} with key. 35 lock, ok := ctx.Value(ctxkey(key)).(*Lock) 36 if ok && lock.Locked() { 37 if lock.ltyp< == 0 { 38 panic("called (r)lock on ctx with different lock type for same key - deadlock!") 39 } 40 return ctx, lock.Clone() 41 } 42 43 // Alloc new Lock. 44 lock = new(Lock) 45 lock.lock = new(uint32) 46 *lock.lock = 1 // locked 47 lock.ltyp = lt 48 49 // Acquire mutex map lock for key. 50 lock.unlk = mutexmap_lock(mm, key, lt) 51 52 // Wrap parent context with our LockCtx value. 53 child := context.WithValue(ctx, ctxkey(key), lock) 54 return child, lock 55 } 56 57 // Locked returns whether Lock is currently locked. 58 func (ctx *Lock) Locked() bool { 59 return atomic.LoadUint32(ctx.lock) == 1 60 } 61 62 // Unlock will unlock the given Lock (only if it is not a read-only). 63 func (ctx *Lock) Unlock() bool { 64 if ctx.unlk != nil && 65 atomic.CompareAndSwapUint32(ctx.lock, 1, 0) { 66 ctx.unlk() 67 return true 68 } 69 return false 70 } 71 72 // Clone returns a read-only clone of Lock. 73 func (ctx *Lock) Clone() *Lock { 74 return &Lock{ 75 lock: ctx.lock, 76 ltyp: ctx.ltyp, 77 } 78 } 79 80 // IsRLock returns whether this Lock is a read lock. 81 func (ctx *Lock) IsRLock() bool { 82 return ctx.ltyp&lockTypeRead != 0 83 } 84 85 // IsLock returns whether this Lock is a write lock. 86 func (ctx *Lock) IsLock() bool { 87 return ctx.ltyp&lockTypeWrite != 0 88 } 89 90 const ( 91 // possible lock types. 92 lockTypeRead = uint8(1) << 0 93 lockTypeWrite = uint8(1) << 1 94 ) 95 96 //go:linkname mutexmap_lock codeberg.org/gruf/go-mutexes.(*MutexMap).lock 97 func mutexmap_lock(mm *mutexes.MutexMap, key string, lt uint8) func()