github.com/vmware/govmomi@v0.51.0/simulator/internal/object_lock.go (about) 1 // © Broadcom. All Rights Reserved. 2 // The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. 3 // SPDX-License-Identifier: Apache-2.0 4 5 package internal 6 7 import ( 8 "fmt" 9 "sync" 10 ) 11 12 // SharedLockingContext is used to identify when locks can be shared. In 13 // practice, simulator code uses the simulator.Context for a request, but in 14 // principle this could be anything. 15 type SharedLockingContext any 16 17 // ObjectLock implements a basic "reference-counted" mutex, where a single 18 // SharedLockingContext can "share" the lock across code paths or child tasks. 19 type ObjectLock struct { 20 lock sync.Locker 21 22 stateLock sync.Mutex 23 heldBy SharedLockingContext 24 count int64 25 } 26 27 // NewObjectLock creates a new ObjectLock. Pass new(sync.Mutex) if you don't 28 // have a custom sync.Locker. 29 func NewObjectLock(lock sync.Locker) *ObjectLock { 30 return &ObjectLock{ 31 lock: lock, 32 } 33 } 34 35 // try returns true if the lock has been acquired; false otherwise 36 func (l *ObjectLock) try(onBehalfOf SharedLockingContext) bool { 37 l.stateLock.Lock() 38 defer l.stateLock.Unlock() 39 40 if l.heldBy == onBehalfOf { 41 l.count = l.count + 1 42 return true 43 } 44 45 if l.heldBy == nil { 46 // we expect no contention for this lock (unless the object has a custom Locker) 47 l.lock.Lock() 48 l.count = 1 49 l.heldBy = onBehalfOf 50 return true 51 } 52 53 return false 54 } 55 56 // wait returns when there's a chance that try() might succeed. 57 // It is intended to be better than busy-waiting or sleeping. 58 func (l *ObjectLock) wait() { 59 l.lock.Lock() 60 l.lock.Unlock() 61 } 62 63 // Release decrements the reference count. The caller should pass their 64 // context, which is used to sanity check that the Unlock() call is valid. If 65 // this is the last reference to the lock for this SharedLockingContext, the lock 66 // is Unlocked and can be acquired by another SharedLockingContext. 67 func (l *ObjectLock) Release(onBehalfOf SharedLockingContext) { 68 l.stateLock.Lock() 69 defer l.stateLock.Unlock() 70 if l.heldBy != onBehalfOf { 71 panic(fmt.Sprintf("Attempt to unlock on behalf of %#v, but is held by %#v", onBehalfOf, l.heldBy)) 72 } 73 l.count = l.count - 1 74 if l.count == 0 { 75 l.heldBy = nil 76 l.lock.Unlock() 77 } 78 } 79 80 // Acquire blocks until it can acquire the lock for onBehalfOf 81 func (l *ObjectLock) Acquire(onBehalfOf SharedLockingContext) { 82 acquired := false 83 for !acquired { 84 if l.try(onBehalfOf) { 85 return 86 } else { 87 l.wait() 88 } 89 } 90 }