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  }