prometheus/alert/file_locker.go (96 lines of code) (raw):

/* * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ package alert import ( "io/ioutil" "os" "sync" ) type FileLocker struct { fileLocks map[string]*sync.RWMutex selfMutex sync.Mutex } func NewFileLocker(fs DirectoryClient) (*FileLocker, error) { fileLocks := make(map[string]*sync.RWMutex) _, err := fs.Stat() if err != nil { _ = fs.Mkdir(0766) } files, err := fs.ReadDir() if err != nil { return nil, err } for _, f := range files { fullPath := fs.Dir() + "/" + f.Name() fileLocks[fullPath] = &sync.RWMutex{} } return &FileLocker{ fileLocks: fileLocks, }, nil } // Lock locks the mutex associated with the given filename for writing. If // mutex does not exist in map yet, create one. func (f *FileLocker) Lock(filename string) { mtx, ok := f.fileLocks[filename] if !ok { f.selfMutex.Lock() defer f.selfMutex.Unlock() mtx, ok := f.fileLocks[filename] if !ok { f.fileLocks[filename] = &sync.RWMutex{} f.fileLocks[filename].Lock() return } mtx.Lock() return } mtx.Lock() } // Unlock unlocks the mutex associated with the given filename for writing. // No-op if mutex does not exist in map func (f *FileLocker) Unlock(filename string) { if mutex, ok := f.fileLocks[filename]; ok { mutex.Unlock() } } // RLock locks the mutex associated with the given filename for reading. // No-op if mutex does not exist in map func (f *FileLocker) RLock(filename string) { mtx, ok := f.fileLocks[filename] if !ok { f.selfMutex.Lock() defer f.selfMutex.Unlock() mtx, ok := f.fileLocks[filename] if !ok { f.fileLocks[filename] = &sync.RWMutex{} f.fileLocks[filename].RLock() return } mtx.RLock() return } mtx.RLock() } // RUnlock unlocks the mutex associated with the given filename for reading. // No-op if mutex does not exist in map func (f *FileLocker) RUnlock(filename string) { if mutex, ok := f.fileLocks[filename]; ok { mutex.RUnlock() } } // DirectoryClient provides the necessary functions to read and modify a single // directory for the FileLocker to operate type DirectoryClient interface { Stat() (os.FileInfo, error) Mkdir(perm os.FileMode) error ReadDir() ([]os.FileInfo, error) Dir() string } type dirClient struct { rulesDir string } func (f *dirClient) Stat() (os.FileInfo, error) { return os.Stat(f.rulesDir) } func (f *dirClient) Mkdir(perm os.FileMode) error { return os.Mkdir(f.rulesDir, perm) } func (f *dirClient) ReadDir() ([]os.FileInfo, error) { return ioutil.ReadDir(f.rulesDir) } func (f *dirClient) Dir() string { return f.rulesDir } func NewDirectoryClient(rulesDir string) DirectoryClient { return &dirClient{ rulesDir: rulesDir, } }