gotosocial/vendor/codeberg.org/gruf/go-store/storage/lock.go

77 lines
1.6 KiB
Go
Raw Normal View History

package storage
import (
2022-01-24 16:35:13 +00:00
"sync"
"sync/atomic"
"syscall"
2021-11-13 11:29:08 +00:00
"codeberg.org/gruf/go-store/util"
)
2022-01-29 11:15:51 +00:00
// LockFile is our standard lockfile name.
const LockFile = "store.lock"
2022-01-24 16:35:13 +00:00
// Lock represents a filesystem lock to ensure only one storage instance open per path.
type Lock struct {
fd int
wg sync.WaitGroup
st uint32
}
2022-01-16 17:52:30 +00:00
// OpenLock opens a lockfile at path.
2022-01-24 16:35:13 +00:00
func OpenLock(path string) (*Lock, error) {
var fd int
// Open the file descriptor at path
err := util.RetryOnEINTR(func() (err error) {
fd, err = syscall.Open(path, defaultFileLockFlags, defaultFilePerms)
return
})
if err != nil {
return nil, err
}
2022-01-24 16:35:13 +00:00
// Get a flock on the file descriptor
err = util.RetryOnEINTR(func() error {
return syscall.Flock(fd, syscall.LOCK_EX|syscall.LOCK_NB)
})
if err != nil {
return nil, errSwapUnavailable(err)
}
return &Lock{fd: fd}, nil
}
2022-01-24 16:35:13 +00:00
// Add will add '1' to the underlying sync.WaitGroup.
func (f *Lock) Add() {
f.wg.Add(1)
}
2022-01-24 16:35:13 +00:00
// Done will decrememnt '1' from the underlying sync.WaitGroup.
func (f *Lock) Done() {
f.wg.Done()
}
2022-01-24 16:35:13 +00:00
// Close will attempt to close the lockfile and file descriptor.
func (f *Lock) Close() error {
var err error
if atomic.CompareAndSwapUint32(&f.st, 0, 1) {
// Wait until done
f.wg.Wait()
// Ensure gets closed
defer syscall.Close(f.fd)
// Call funlock on the file descriptor
err = util.RetryOnEINTR(func() error {
return syscall.Flock(f.fd, syscall.LOCK_UN|syscall.LOCK_NB)
})
}
return err
}
// Closed will return whether this lockfile has been closed (and unlocked).
func (f *Lock) Closed() bool {
return (atomic.LoadUint32(&f.st) == 1)
}