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 }