54 lines
1 KiB
Go
54 lines
1 KiB
Go
package filelock
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"path"
|
|
"time"
|
|
)
|
|
|
|
var ErrLocked = errors.New("failed to get a lock in a timely manner")
|
|
|
|
type FileLock struct {
|
|
lockPath string
|
|
}
|
|
|
|
func New(filepath string) *FileLock {
|
|
dir, filename := path.Split(filepath)
|
|
lockPath := path.Join(dir, fmt.Sprintf(".%s.lock", filename))
|
|
return &FileLock{
|
|
lockPath: lockPath,
|
|
}
|
|
}
|
|
|
|
func (fl *FileLock) lockFileExists() bool {
|
|
if _, err := os.Stat(fl.lockPath); errors.Is(err, os.ErrNotExist) {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (fl *FileLock) Lock(timeout time.Duration) error {
|
|
start := time.Now().UTC()
|
|
end := start.Add(timeout)
|
|
|
|
for {
|
|
if !fl.lockFileExists() {
|
|
if _, err := os.Create(fl.lockPath); err != nil {
|
|
return fmt.Errorf("failed to create lock: %w", err)
|
|
}
|
|
}
|
|
if time.Now().After(end) {
|
|
return ErrLocked
|
|
}
|
|
time.Sleep(100 * time.Millisecond)
|
|
}
|
|
}
|
|
|
|
func (fl *FileLock) Unlock() error {
|
|
if err := os.Remove(fl.lockPath); err != nil {
|
|
return fmt.Errorf("failed to remove lock: %w", err)
|
|
}
|
|
return nil
|
|
}
|