github.com/cyverse/go-irodsclient@v0.13.2/fs/file_lock.go (about) 1 package fs 2 3 import ( 4 "fmt" 5 "strings" 6 "sync" 7 8 "golang.org/x/xerrors" 9 ) 10 11 // this is a file lock managed by go-irodsclient. Not same as fs file lock 12 13 // FileLock is a lock for a file 14 type FileLock struct { 15 path string 16 references int64 17 mutex sync.RWMutex // used to lock the file 18 } 19 20 // FileLocks manages file locks 21 type FileLocks struct { 22 mutex sync.Mutex 23 locks map[string]*FileLock 24 } 25 26 // NewFileLocks creates a new FileLocks 27 func NewFileLocks() *FileLocks { 28 return &FileLocks{ 29 mutex: sync.Mutex{}, 30 locks: map[string]*FileLock{}, 31 } 32 } 33 34 // LockFilesForPrefix locks all files starting with the given prefix, does not create a new lock, but increases reference 35 func (mgr *FileLocks) LockFilesForPrefix(pathPrefix string) []string { 36 fileLocks := []*FileLock{} 37 38 mgr.mutex.Lock() 39 40 prefix := fmt.Sprintf("%s/", pathPrefix) 41 for _, lock := range mgr.locks { 42 if strings.HasPrefix(lock.path, prefix) { 43 fileLocks = append(fileLocks, lock) 44 lock.references++ 45 } 46 } 47 48 mgr.mutex.Unlock() 49 50 lockedFilePaths := []string{} 51 for _, fileLock := range fileLocks { 52 fileLock.mutex.Lock() // write lock 53 lockedFilePaths = append(lockedFilePaths, fileLock.path) 54 } 55 56 return lockedFilePaths 57 } 58 59 // UnlockFiles unlocks multiple files 60 func (mgr *FileLocks) UnlockFiles(paths []string) error { 61 fileLocks := []*FileLock{} 62 63 mgr.mutex.Lock() 64 65 for _, path := range paths { 66 if lock, ok := mgr.locks[path]; ok { 67 // fileLock already exists 68 fileLocks = append(fileLocks, lock) 69 70 if lock.references <= 0 { 71 mgr.mutex.Unlock() 72 return xerrors.Errorf("file lock for path %s has invalid references %d", path, lock.references) 73 } 74 75 lock.references-- 76 77 if lock.references == 0 { 78 delete(mgr.locks, path) 79 } 80 } else { 81 mgr.mutex.Unlock() 82 return xerrors.Errorf("file lock for path %s does not exist", path) 83 } 84 } 85 86 mgr.mutex.Unlock() 87 88 // unlock in reverse order 89 for i := len(fileLocks) - 1; i >= 0; i-- { 90 fileLock := fileLocks[i] 91 fileLock.mutex.Unlock() // unlock write lock 92 } 93 94 return nil 95 } 96 97 // Lock locks a file 98 func (mgr *FileLocks) Lock(path string) { 99 var fileLock *FileLock 100 101 mgr.mutex.Lock() 102 103 if lock, ok := mgr.locks[path]; ok { 104 // fileLock already exists 105 fileLock = lock 106 fileLock.references++ 107 } else { 108 // create a new 109 fileLock = &FileLock{ 110 path: path, 111 references: 1, 112 mutex: sync.RWMutex{}, 113 } 114 mgr.locks[path] = fileLock 115 } 116 117 mgr.mutex.Unlock() 118 119 fileLock.mutex.Lock() // write lock 120 } 121 122 // RLock locks a file with read mode 123 func (mgr *FileLocks) RLock(path string) { 124 var fileLock *FileLock 125 126 mgr.mutex.Lock() 127 128 if lock, ok := mgr.locks[path]; ok { 129 // fileLock already exists 130 fileLock = lock 131 fileLock.references++ 132 } else { 133 // create a new 134 fileLock = &FileLock{ 135 path: path, 136 references: 1, 137 mutex: sync.RWMutex{}, 138 } 139 mgr.locks[path] = fileLock 140 } 141 142 mgr.mutex.Unlock() 143 144 fileLock.mutex.RLock() // read lock 145 } 146 147 // Unlock unlocks a file 148 func (mgr *FileLocks) Unlock(path string) error { 149 var fileLock *FileLock 150 151 mgr.mutex.Lock() 152 153 if lock, ok := mgr.locks[path]; ok { 154 // fileLock already exists 155 fileLock = lock 156 } else { 157 mgr.mutex.Unlock() 158 return xerrors.Errorf("file lock for path %s does not exist", path) 159 } 160 161 if fileLock.references <= 0 { 162 mgr.mutex.Unlock() 163 return xerrors.Errorf("file lock for path %s has invalid references %d", path, fileLock.references) 164 } 165 166 fileLock.references-- 167 168 if fileLock.references == 0 { 169 delete(mgr.locks, path) 170 } 171 172 mgr.mutex.Unlock() 173 174 fileLock.mutex.Unlock() 175 return nil 176 } 177 178 // RUnlock unlocks a file with read mode 179 func (mgr *FileLocks) RUnlock(path string) error { 180 var fileLock *FileLock 181 182 mgr.mutex.Lock() 183 184 if lock, ok := mgr.locks[path]; ok { 185 // fileLock already exists 186 fileLock = lock 187 } else { 188 mgr.mutex.Unlock() 189 return xerrors.Errorf("file lock for path %s does not exist", path) 190 } 191 192 if fileLock.references <= 0 { 193 mgr.mutex.Unlock() 194 return xerrors.Errorf("file lock for path %s has invalid references %d", path, fileLock.references) 195 } 196 197 fileLock.references-- 198 199 if fileLock.references == 0 { 200 delete(mgr.locks, path) 201 } 202 203 mgr.mutex.Unlock() 204 205 fileLock.mutex.RUnlock() 206 return nil 207 }