github.com/vmware/govmomi@v0.37.2/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  }