github.com/cyverse/go-irodsclient@v0.13.2/fs/file_handle_map.go (about) 1 package fs 2 3 import ( 4 "fmt" 5 "strings" 6 "sync" 7 8 "github.com/rs/xid" 9 ) 10 11 // FileHandleMapEventHandler is a event handler for FileHandleMap 12 type FileHandleMapEventHandler func(path string, id string, empty bool) 13 14 // FileHandleMapEventHandlerWrap is wrapper event handler 15 type FileHandleMapEventHandlerWrap struct { 16 id string 17 path string 18 handler FileHandleMapEventHandler 19 } 20 21 // FileHandleMap manages File Handles opened 22 type FileHandleMap struct { 23 mutex sync.RWMutex 24 fileHandles map[string]*FileHandle // ID-handle mapping 25 filePathID map[string][]string // path-IDs mappings 26 closeEventHandlers map[string][]*FileHandleMapEventHandlerWrap // path-eventhandler mapping 27 eventHandlerIDPath map[string]string // eventhandlerID-path mappings 28 } 29 30 // NewFileHandleMap creates a new FileHandleMap 31 func NewFileHandleMap() *FileHandleMap { 32 return &FileHandleMap{ 33 mutex: sync.RWMutex{}, 34 fileHandles: map[string]*FileHandle{}, 35 filePathID: map[string][]string{}, 36 closeEventHandlers: map[string][]*FileHandleMapEventHandlerWrap{}, 37 eventHandlerIDPath: map[string]string{}, 38 } 39 } 40 41 // AddCloseEventHandler registers an event handler for file close 42 func (fileHandleMap *FileHandleMap) AddCloseEventHandler(path string, handler FileHandleMapEventHandler) string { 43 fileHandleMap.mutex.Lock() 44 defer fileHandleMap.mutex.Unlock() 45 46 handlerID := xid.New().String() 47 48 handlerWrap := FileHandleMapEventHandlerWrap{ 49 id: handlerID, 50 path: path, 51 handler: handler, 52 } 53 54 if handlers, ok := fileHandleMap.closeEventHandlers[path]; ok { 55 fileHandleMap.closeEventHandlers[path] = append(handlers, &handlerWrap) 56 } else { 57 fileHandleMap.closeEventHandlers[path] = []*FileHandleMapEventHandlerWrap{&handlerWrap} 58 } 59 60 fileHandleMap.eventHandlerIDPath[handlerID] = path 61 62 // if there's no files? 63 // raise event with empty id string 64 if _, ok := fileHandleMap.filePathID[path]; !ok { 65 handler(path, "", true) 66 } 67 68 return handlerID 69 } 70 71 // RemoveCloseEventHandler deregisters an event handler for file close 72 func (fileHandleMap *FileHandleMap) RemoveCloseEventHandler(handlerID string) { 73 fileHandleMap.mutex.Lock() 74 defer fileHandleMap.mutex.Unlock() 75 76 if path, ok := fileHandleMap.eventHandlerIDPath[handlerID]; ok { 77 delete(fileHandleMap.eventHandlerIDPath, handlerID) 78 79 newEventHandlers := []*FileHandleMapEventHandlerWrap{} 80 if handlers, ok2 := fileHandleMap.closeEventHandlers[path]; ok2 { 81 for _, handler := range handlers { 82 if handler.id != handlerID { 83 newEventHandlers = append(newEventHandlers, handler) 84 } 85 } 86 87 if len(newEventHandlers) > 0 { 88 fileHandleMap.closeEventHandlers[path] = newEventHandlers 89 } else { 90 delete(fileHandleMap.closeEventHandlers, path) 91 } 92 } 93 } 94 } 95 96 // Add registers a file handle 97 func (fileHandleMap *FileHandleMap) Add(handle *FileHandle) { 98 fileHandleMap.mutex.Lock() 99 defer fileHandleMap.mutex.Unlock() 100 101 fileHandleMap.fileHandles[handle.id] = handle 102 if ids, ok := fileHandleMap.filePathID[handle.entry.Path]; ok { 103 fileHandleMap.filePathID[handle.entry.Path] = append(ids, handle.id) 104 } else { 105 fileHandleMap.filePathID[handle.entry.Path] = []string{handle.id} 106 } 107 } 108 109 // Remove deletes a file handle registered using ID 110 func (fileHandleMap *FileHandleMap) Remove(id string) { 111 fileHandleMap.mutex.Lock() 112 defer fileHandleMap.mutex.Unlock() 113 114 handle := fileHandleMap.fileHandles[id] 115 if handle != nil { 116 delete(fileHandleMap.fileHandles, id) 117 118 emptyHandles := true 119 if ids, ok := fileHandleMap.filePathID[handle.entry.Path]; ok { 120 emptyHandles = false 121 newIDs := []string{} 122 for _, handleID := range ids { 123 if handleID != id { 124 newIDs = append(newIDs, handleID) 125 } 126 } 127 128 if len(newIDs) > 0 { 129 fileHandleMap.filePathID[handle.entry.Path] = newIDs 130 } else { 131 delete(fileHandleMap.filePathID, handle.entry.Path) 132 emptyHandles = true 133 } 134 } 135 136 if handlers, ok := fileHandleMap.closeEventHandlers[handle.entry.Path]; ok { 137 for _, handler := range handlers { 138 handler.handler(handle.entry.Path, id, emptyHandles) 139 } 140 } 141 } 142 } 143 144 // PopAll pops all file handles registered (clear) and returns 145 func (fileHandleMap *FileHandleMap) PopAll() []*FileHandle { 146 fileHandleMap.mutex.Lock() 147 defer fileHandleMap.mutex.Unlock() 148 149 handles := []*FileHandle{} 150 for _, handle := range fileHandleMap.fileHandles { 151 handles = append(handles, handle) 152 } 153 154 // clear 155 fileHandleMap.fileHandles = map[string]*FileHandle{} 156 fileHandleMap.filePathID = map[string][]string{} 157 158 return handles 159 } 160 161 // Clear clears all file handles registered 162 func (fileHandleMap *FileHandleMap) Clear() { 163 fileHandleMap.mutex.Lock() 164 defer fileHandleMap.mutex.Unlock() 165 166 fileHandleMap.fileHandles = map[string]*FileHandle{} 167 fileHandleMap.filePathID = map[string][]string{} 168 } 169 170 // List lists all file handles registered 171 func (fileHandleMap *FileHandleMap) List() []*FileHandle { 172 fileHandleMap.mutex.RLock() 173 defer fileHandleMap.mutex.RUnlock() 174 175 handles := []*FileHandle{} 176 for _, handle := range fileHandleMap.fileHandles { 177 handles = append(handles, handle) 178 } 179 180 return handles 181 } 182 183 // Get returns a file handle registered using ID 184 func (fileHandleMap *FileHandleMap) Get(id string) *FileHandle { 185 fileHandleMap.mutex.RLock() 186 defer fileHandleMap.mutex.RUnlock() 187 188 return fileHandleMap.fileHandles[id] 189 } 190 191 // Pop pops a file handle registered using ID and returns the handle 192 func (fileHandleMap *FileHandleMap) Pop(id string) *FileHandle { 193 fileHandleMap.mutex.Lock() 194 defer fileHandleMap.mutex.Unlock() 195 196 handle := fileHandleMap.fileHandles[id] 197 if handle != nil { 198 delete(fileHandleMap.fileHandles, id) 199 200 if ids, ok := fileHandleMap.filePathID[handle.entry.Path]; ok { 201 newIDs := []string{} 202 for _, handleID := range ids { 203 if handleID != id { 204 newIDs = append(newIDs, handleID) 205 } 206 } 207 208 if len(newIDs) > 0 { 209 fileHandleMap.filePathID[handle.entry.Path] = newIDs 210 } else { 211 delete(fileHandleMap.filePathID, handle.entry.Path) 212 } 213 } 214 } 215 216 return handle 217 } 218 219 // ListByPath returns file handles registered using path 220 func (fileHandleMap *FileHandleMap) ListByPath(path string) []*FileHandle { 221 fileHandleMap.mutex.RLock() 222 defer fileHandleMap.mutex.RUnlock() 223 224 handles := []*FileHandle{} 225 if ids, ok := fileHandleMap.filePathID[path]; ok { 226 for _, handleID := range ids { 227 if handle, ok2 := fileHandleMap.fileHandles[handleID]; ok2 { 228 handles = append(handles, handle) 229 } 230 } 231 } 232 return handles 233 } 234 235 // ListPathsInDir returns paths of file handles under given parent path 236 func (fileHandleMap *FileHandleMap) ListPathsInDir(parentPath string) []string { 237 fileHandleMap.mutex.RLock() 238 defer fileHandleMap.mutex.RUnlock() 239 240 prefix := parentPath 241 if len(prefix) > 1 && !strings.HasSuffix(prefix, "/") { 242 prefix = fmt.Sprintf("%s/", prefix) 243 } 244 245 paths := []string{} 246 // loop over all file handles opened 247 for path := range fileHandleMap.filePathID { 248 // check if it's sub dirs or files in the dir 249 if strings.HasPrefix(path, prefix) { 250 paths = append(paths, path) 251 } 252 } 253 254 return paths 255 } 256 257 // PopByPath pops file handles registered using path and returns the handles 258 func (fileHandleMap *FileHandleMap) PopByPath(path string) []*FileHandle { 259 fileHandleMap.mutex.Lock() 260 defer fileHandleMap.mutex.Unlock() 261 262 handles := []*FileHandle{} 263 if ids, ok := fileHandleMap.filePathID[path]; ok { 264 for _, handleID := range ids { 265 if handle, ok2 := fileHandleMap.fileHandles[handleID]; ok2 { 266 handles = append(handles, handle) 267 delete(fileHandleMap.fileHandles, handleID) 268 } 269 } 270 271 delete(fileHandleMap.filePathID, path) 272 } 273 274 return handles 275 }