github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/pkg/fsutil/filelock.go (about) 1 // Copyright 2021 PingCAP, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package fsutil 15 16 import ( 17 "os" 18 "syscall" 19 20 "github.com/pingcap/errors" 21 cerrors "github.com/pingcap/tiflow/pkg/errors" 22 ) 23 24 // FileLock represents a file lock created by `flock`. 25 // For more on the system call `flock`, read: 26 // https://linux.die.net/man/2/flock 27 type FileLock struct { 28 fd *os.File 29 } 30 31 // NewFileLock creates a new file lock on the file described in filePath. 32 func NewFileLock(filePath string) (*FileLock, error) { 33 file, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE|syscall.O_NONBLOCK, 0o600) 34 if err != nil { 35 return nil, errors.Trace(err) 36 } 37 38 return &FileLock{fd: file}, nil 39 } 40 41 // Lock places a lock on the file 42 func (fl *FileLock) Lock() error { 43 fdInt := fl.fd.Fd() 44 err := syscall.Flock(int(fdInt), syscall.LOCK_EX|syscall.LOCK_NB) 45 if err != nil { 46 if err == syscall.EAGAIN { 47 return cerrors.ErrConflictingFileLocks.Wrap(err).GenWithStackByArgs(fl.fd.Name()) 48 } 49 return errors.Trace(err) 50 } 51 return nil 52 } 53 54 // Unlock unlocks the file lock 55 func (fl *FileLock) Unlock() error { 56 fdInt := fl.fd.Fd() 57 err := syscall.Flock(int(fdInt), syscall.LOCK_UN) 58 if err != nil { 59 return errors.Trace(err) 60 } 61 return nil 62 } 63 64 // Close closes the underlying file. 65 // NOTE: this function will unlock the lock as well. 66 func (fl *FileLock) Close() error { 67 err := fl.fd.Close() 68 if err != nil { 69 return errors.Trace(err) 70 } 71 return nil 72 } 73 74 // IsLocked checks whether the file is currently locked 75 func (fl *FileLock) IsLocked() (bool, error) { 76 fdInt := fl.fd.Fd() 77 err := syscall.Flock(int(fdInt), syscall.LOCK_SH|syscall.LOCK_NB) 78 if err == syscall.EAGAIN { 79 return true, nil 80 } else if err != nil { 81 return false, errors.Trace(err) 82 } else { 83 return false, nil 84 } 85 }