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 }