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

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