github.com/swiftstack/ProxyFS@v0.0.0-20210203235616-4017c267d62f/dlm/api.go (about)

     1  // Copyright (c) 2015-2021, NVIDIA CORPORATION.
     2  // SPDX-License-Identifier: Apache-2.0
     3  
     4  // Distributed Lock Manager (DLM) provides locking between threads on the same
     5  // system and between threads on different systems.
     6  //
     7  // Example use of the lock:
     8  /*
     9  type myStruct struct {
    10  	Acct     string
    11  	Volume   string
    12  	InodeNum uint64
    13  	myRwLock *RWLockStruct
    14  	}
    15  
    16  func my_function() {
    17  	var myval myStruct
    18  	myval.Acct = "Acct1"
    19  	myval.Volume = "Vol1"
    20  	myval.InodeNum = 11
    21  	myLockId := fmt.Sprintf("%s:%s:%v\n", myval.Acct, myval.Volume, myval.InodeNum)
    22  
    23  	myCookie := GenerateCallerID()
    24  	myval.myRwLock = &RWLockStruct{LockID: myLockId, Notify: nil, LockCallerID: myCookie}
    25  	myval.myRwLock.ReadLock()
    26  	myval.myRwLock.Unlock()
    27  	myval.myRwLock.WriteLock()
    28  	myval.myRwLock.Unlock()
    29  	err := myval.myRwLock.TryReadLock()
    30  	errno := blunder.Errno(err)
    31  
    32  	switch errno {
    33  	case 0:
    34  		log.Printf("Got TryReadLock")
    35  		myval.myRwLock.Unlock()
    36  	case EAGAIN: // give up other locks.
    37  	default: // something wrong..
    38  	}
    39  
    40  	err = myval.myRwLock.TryWriteLock()
    41  	errno := blunder.Errno(err)
    42  
    43  	switch errno {
    44  	case 0:
    45  		log.Printf("Got TryWriteLock")
    46  		myval.myRwLock.Unlock()
    47  	case EAGAIN: // give up other locks.
    48  	default: // something wrong..
    49  	}
    50  	}
    51  */
    52  package dlm
    53  
    54  import (
    55  	"fmt"
    56  
    57  	"github.com/swiftstack/ProxyFS/trackedlock"
    58  )
    59  
    60  type NotifyReason uint32
    61  type CallerID *string
    62  
    63  const (
    64  	ReasonWriteRequest NotifyReason = iota + 1 // Another thread in the cluster wants "Write" access
    65  	ReasonReadRequest                          // Another thread wants "Read" access
    66  )
    67  
    68  type LockHeldType uint32
    69  
    70  const (
    71  	ANYLOCK LockHeldType = iota + 1
    72  	READLOCK
    73  	WRITELOCK
    74  )
    75  
    76  type Notify interface {
    77  	NotifyNodeChange(reason NotifyReason) // DLM will call the last node which owns the lock before handing over
    78  	// the lock to another node. Useful for leases and data caching.
    79  }
    80  
    81  type RWLockStruct struct {
    82  	LockID       string
    83  	Notify       Notify
    84  	LockCallerID CallerID
    85  }
    86  
    87  // Lock for generating unique caller IDs
    88  // For now, this is just an in-memory thing.
    89  var callerIDLock trackedlock.Mutex
    90  var nextCallerID uint64 = 1000
    91  
    92  // GenerateCallerID() returns a cluster wide unique number useful in deadlock detection.
    93  func GenerateCallerID() (callerID CallerID) {
    94  
    95  	// TODO - we need to use a nonce value instead of this when we have clustering
    96  	callerIDLock.Lock()
    97  
    98  	callerIDStr := fmt.Sprintf("%d", nextCallerID)
    99  	callerID = CallerID(&callerIDStr)
   100  	nextCallerID++
   101  
   102  	callerIDLock.Unlock()
   103  
   104  	return callerID
   105  }
   106  
   107  // IsLockHeld() returns
   108  func IsLockHeld(lockID string, callerID CallerID, lockHeldType LockHeldType) (held bool) {
   109  	held = isLockHeld(lockID, callerID, lockHeldType)
   110  	return held
   111  }
   112  
   113  // GetLockID() returns the lock ID from the lock struct
   114  func (l *RWLockStruct) GetLockID() string {
   115  	return l.LockID
   116  }
   117  
   118  // CallerID returns the caller ID from the lock struct
   119  func (l *RWLockStruct) GetCallerID() CallerID {
   120  	return l.LockCallerID
   121  }
   122  
   123  // Returns whether the lock is held for reading
   124  func (l *RWLockStruct) IsReadHeld() bool {
   125  	held := isLockHeld(l.LockID, l.LockCallerID, READLOCK)
   126  	return held
   127  }
   128  
   129  // Returns whether the lock is held for writing
   130  func (l *RWLockStruct) IsWriteHeld() bool {
   131  	held := isLockHeld(l.LockID, l.LockCallerID, WRITELOCK)
   132  	return held
   133  }
   134  
   135  // WriteLock() blocks until the lock for the inode can be held exclusively.
   136  func (l *RWLockStruct) WriteLock() (err error) {
   137  	// TODO - what errors are possible here?
   138  	err = l.commonLock(exclusive, false)
   139  	return err
   140  }
   141  
   142  // ReadLock() blocks until the lock for the inode can be held shared.
   143  func (l *RWLockStruct) ReadLock() (err error) {
   144  	// TODO - what errors are possible here?
   145  	err = l.commonLock(shared, false)
   146  	return err
   147  }
   148  
   149  // TryWriteLock() attempts to grab the lock if is is free.  Otherwise, it returns EAGAIN.
   150  func (l *RWLockStruct) TryWriteLock() (err error) {
   151  	err = l.commonLock(exclusive, true)
   152  	return err
   153  }
   154  
   155  // TryReadLock() attempts to grab the lock if is is free or shared.  Otherwise, it returns EAGAIN.
   156  func (l *RWLockStruct) TryReadLock() (err error) {
   157  	err = l.commonLock(shared, true)
   158  	return err
   159  }
   160  
   161  // Unlock() releases the lock and signals any waiters that the lock is free.
   162  func (l *RWLockStruct) Unlock() (err error) {
   163  	// TODO what error is possible?
   164  	err = l.unlock()
   165  	return err
   166  }