github.com/vmware/govmomi@v0.37.1/simulator/internal/object_lock.go (about) 1 /* 2 Copyright (c) 2014-2022 VMware, Inc. All Rights Reserved. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package internal 18 19 import ( 20 "fmt" 21 "sync" 22 ) 23 24 // SharedLockingContext is used to identify when locks can be shared. In 25 // practice, simulator code uses the simulator.Context for a request, but in 26 // principle this could be anything. 27 type SharedLockingContext interface{} 28 29 // ObjectLock implements a basic "reference-counted" mutex, where a single 30 // SharedLockingContext can "share" the lock across code paths or child tasks. 31 type ObjectLock struct { 32 lock sync.Locker 33 34 stateLock sync.Mutex 35 heldBy SharedLockingContext 36 count int64 37 } 38 39 // NewObjectLock creates a new ObjectLock. Pass new(sync.Mutex) if you don't 40 // have a custom sync.Locker. 41 func NewObjectLock(lock sync.Locker) *ObjectLock { 42 return &ObjectLock{ 43 lock: lock, 44 } 45 } 46 47 // try returns true if the lock has been acquired; false otherwise 48 func (l *ObjectLock) try(onBehalfOf SharedLockingContext) bool { 49 l.stateLock.Lock() 50 defer l.stateLock.Unlock() 51 52 if l.heldBy == onBehalfOf { 53 l.count = l.count + 1 54 return true 55 } 56 57 if l.heldBy == nil { 58 // we expect no contention for this lock (unless the object has a custom Locker) 59 l.lock.Lock() 60 l.count = 1 61 l.heldBy = onBehalfOf 62 return true 63 } 64 65 return false 66 } 67 68 // wait returns when there's a chance that try() might succeed. 69 // It is intended to be better than busy-waiting or sleeping. 70 func (l *ObjectLock) wait() { 71 l.lock.Lock() 72 l.lock.Unlock() 73 } 74 75 // Release decrements the reference count. The caller should pass their 76 // context, which is used to sanity check that the Unlock() call is valid. If 77 // this is the last reference to the lock for this SharedLockingContext, the lock 78 // is Unlocked and can be acquired by another SharedLockingContext. 79 func (l *ObjectLock) Release(onBehalfOf SharedLockingContext) { 80 l.stateLock.Lock() 81 defer l.stateLock.Unlock() 82 if l.heldBy != onBehalfOf { 83 panic(fmt.Sprintf("Attempt to unlock on behalf of %#v, but is held by %#v", onBehalfOf, l.heldBy)) 84 } 85 l.count = l.count - 1 86 if l.count == 0 { 87 l.heldBy = nil 88 l.lock.Unlock() 89 } 90 } 91 92 // Acquire blocks until it can acquire the lock for onBehalfOf 93 func (l *ObjectLock) Acquire(onBehalfOf SharedLockingContext) { 94 acquired := false 95 for !acquired { 96 if l.try(onBehalfOf) { 97 return 98 } else { 99 l.wait() 100 } 101 } 102 }