github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/libkb/locktab.go (about) 1 // Copyright 2015 Keybase, Inc. All rights reserved. Use of 2 // this source code is governed by the included BSD license. 3 4 package libkb 5 6 import ( 7 "sync" 8 "time" 9 10 "golang.org/x/net/context" 11 ) 12 13 type NamedLock struct { 14 sync.Mutex 15 lctx VLogContext 16 refs int 17 name string 18 parent *LockTable 19 } 20 21 func (l *NamedLock) incref() { 22 l.refs++ 23 } 24 25 func (l *NamedLock) decref() { 26 l.refs-- 27 } 28 29 func (l *NamedLock) Release(ctx context.Context) { 30 l.lctx.GetVDebugLog().CLogf(ctx, VLog1, "+ LockTable.Release(%s)", l.name) 31 l.Unlock() 32 l.parent.Lock() 33 l.decref() 34 if l.refs == 0 { 35 l.lctx.GetVDebugLog().CLogf(ctx, VLog1, "| LockTable.unref(%s)", l.name) 36 delete(l.parent.locks, l.name) 37 } 38 l.parent.Unlock() 39 l.lctx.GetVDebugLog().CLogf(ctx, VLog1, "- LockTable.Unlock(%s)", l.name) 40 } 41 42 type LockTable struct { 43 sync.Mutex 44 locks map[string]*NamedLock 45 } 46 47 func NewLockTable() *LockTable { 48 return &LockTable{} 49 } 50 51 func (t *LockTable) init() { 52 if t.locks == nil { 53 t.locks = make(map[string]*NamedLock) 54 } 55 } 56 57 // AcquireOnName acquires s's lock. 58 // Never gives up. 59 func (t *LockTable) AcquireOnName(ctx context.Context, g VLogContext, s string) (ret *NamedLock) { 60 g.GetVDebugLog().CLogf(ctx, VLog1, "+ LockTable.Lock(%s)", s) 61 t.Lock() 62 t.init() 63 if ret = t.locks[s]; ret == nil { 64 ret = &NamedLock{lctx: g, refs: 0, name: s, parent: t} 65 t.locks[s] = ret 66 } 67 ret.incref() 68 t.Unlock() 69 ret.Lock() 70 g.GetVDebugLog().CLogf(ctx, VLog1, "- LockTable.Lock(%s)", s) 71 return ret 72 } 73 74 // AcquireOnNameWithContext acquires s's lock. 75 // Returns (ret, nil) if the lock was acquired. 76 // Returns (nil, err) if it was not. The error is from ctx.Err(). 77 func (t *LockTable) AcquireOnNameWithContext(ctx context.Context, g VLogContext, s string) (ret *NamedLock, err error) { 78 g.GetVDebugLog().CLogf(ctx, VLog1, "+ LockTable.Lock(%s)", s) 79 err = AcquireWithContext(ctx, t) 80 if err != nil { 81 g.GetVDebugLog().CLogf(ctx, VLog1, "- LockTable.Lock(%s) outer canceled: %v", s, err) 82 return nil, err 83 } 84 t.init() 85 if ret = t.locks[s]; ret == nil { 86 ret = &NamedLock{lctx: g, refs: 0, name: s, parent: t} 87 t.locks[s] = ret 88 } 89 ret.incref() 90 t.Unlock() 91 err = AcquireWithContext(ctx, ret) 92 if err != nil { 93 g.GetVDebugLog().CLogf(ctx, VLog1, "- LockTable.Lock(%s) inner canceled: %v", s, err) 94 return nil, err 95 } 96 g.GetVDebugLog().CLogf(ctx, VLog1, "- LockTable.Lock(%s)", s) 97 return ret, nil 98 } 99 100 // AcquireOnNameWithContextAndTimeout acquires s's lock. 101 // Returns (ret, nil) if the lock was acquired. 102 // Returns (nil, err) if it was not. The error is from ctx.Err() or context.DeadlineExceeded. 103 func (t *LockTable) AcquireOnNameWithContextAndTimeout(ctx context.Context, g VLogContext, s string, timeout time.Duration) (ret *NamedLock, err error) { 104 ctx2, cancel := context.WithTimeout(ctx, timeout) 105 defer cancel() 106 return t.AcquireOnNameWithContext(ctx2, g, s) 107 }