github.com/swiftstack/proxyfs@v0.0.0-20201223034610-5434d919416e/fs/utils.go (about)

     1  package fs
     2  
     3  import (
     4  	"container/list"
     5  	"crypto/rand"
     6  	"fmt"
     7  	"math/big"
     8  	"time"
     9  
    10  	"github.com/swiftstack/ProxyFS/stats"
    11  )
    12  
    13  func (tryLockBackoffContext *tryLockBackoffContextStruct) backoff() {
    14  	var (
    15  		serializedBackoffListElement *list.Element
    16  	)
    17  
    18  	stats.IncrementOperations(&stats.InodeTryLockBackoffOps)
    19  
    20  	if 0 < tryLockBackoffContext.backoffsCompleted {
    21  		if tryLockBackoffContext.backoffsCompleted < globals.tryLockSerializationThreshhold {
    22  			// Just do normal delay backoff
    23  
    24  			stats.IncrementOperations(&stats.InodeTryLockDelayedBackoffOps)
    25  
    26  			backoffSleep()
    27  		} else {
    28  			stats.IncrementOperations(&stats.InodeTryLockSerializedBackoffOps)
    29  
    30  			// Push us onto the serializedBackoffList
    31  
    32  			globals.Lock()
    33  
    34  			serializedBackoffListElement = globals.serializedBackoffList.PushBack(tryLockBackoffContext)
    35  
    36  			if 1 == globals.serializedBackoffList.Len() {
    37  				// Nobody else is currently serialized... so just delay
    38  
    39  				globals.Unlock()
    40  
    41  				backoffSleep()
    42  			} else {
    43  				// We are not the only one on serializedBackoffList... so we must wait
    44  
    45  				tryLockBackoffContext.Add(1)
    46  
    47  				globals.Unlock()
    48  
    49  				tryLockBackoffContext.Wait()
    50  
    51  				// Now delay to get prior tryLockBackoffContext a chance to complete
    52  
    53  				backoffSleep()
    54  			}
    55  
    56  			// Now remove us from the front of serializedBackoffList
    57  
    58  			globals.Lock()
    59  
    60  			_ = globals.serializedBackoffList.Remove(serializedBackoffListElement)
    61  
    62  			// Now check if there is a tryLockBackoffContext "behind" us
    63  
    64  			serializedBackoffListElement = globals.serializedBackoffList.Front()
    65  
    66  			globals.Unlock()
    67  
    68  			if nil != serializedBackoffListElement {
    69  				// Awake the tryLockBackoffContext "behind" us
    70  
    71  				serializedBackoffListElement.Value.(*tryLockBackoffContextStruct).Done()
    72  			}
    73  		}
    74  	}
    75  
    76  	tryLockBackoffContext.backoffsCompleted++
    77  }
    78  
    79  func backoffSleep() {
    80  	var (
    81  		err          error
    82  		randomBigInt *big.Int
    83  		thisDelay    time.Duration
    84  	)
    85  
    86  	randomBigInt, err = rand.Int(rand.Reader, big.NewInt(int64(globals.tryLockBackoffMax)-int64(globals.tryLockBackoffMin)))
    87  	if nil != err {
    88  		err = fmt.Errorf("fs.backoffSleep() failed in call to rand.Int(): %v", err)
    89  		panic(err)
    90  	}
    91  
    92  	thisDelay = time.Duration(int64(globals.tryLockBackoffMin) + randomBigInt.Int64())
    93  
    94  	time.Sleep(thisDelay)
    95  }