diff options
| author | Ophestra <cat@gensokyo.uk> | 2025-10-25 21:28:49 +0900 |
|---|---|---|
| committer | Ophestra <cat@gensokyo.uk> | 2025-10-25 21:29:24 +0900 |
| commit | 470e545d2761b77f95147f61f633f4d629750ee7 (patch) | |
| tree | 5badf0a34ecaa1487e88ffe6aa0467f7d55cd4c4 /internal/app/state/multi.go | |
| parent | 8d3381821f2a8d257a4c55c0dd311019bd0e78eb (diff) | |
internal/app/state: use internal/lockedfile
This is a pretty solid implementation backed by robust tests, with a much cleaner interface.
Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'internal/app/state/multi.go')
| -rw-r--r-- | internal/app/state/multi.go | 84 |
1 files changed, 17 insertions, 67 deletions
diff --git a/internal/app/state/multi.go b/internal/app/state/multi.go index c9096ace..9824a8ee 100644 --- a/internal/app/state/multi.go +++ b/internal/app/state/multi.go @@ -9,12 +9,15 @@ import ( "path" "strconv" "sync" - "syscall" "hakurei.app/hst" + "hakurei.app/internal/lockedfile" "hakurei.app/message" ) +// multiLockFileName is the name of the file backing [lockedfile.Mutex] of a multiBackend. +const multiLockFileName = "lock" + // fine-grained locking and access type multiStore struct { base string @@ -44,19 +47,18 @@ func (s *multiStore) Do(identity int, f func(c Cursor)) (bool, error) { return false, &hst.AppError{Step: "create store segment directory", Err: err} } - // open locker file - if l, err := os.OpenFile(b.path+".lock", os.O_RDWR|os.O_CREATE, 0600); err != nil { - s.backends.CompareAndDelete(identity, b) - return false, &hst.AppError{Step: "open store segment lock file", Err: err} - } else { - b.lockfile = l - } + // set up file-based mutex + b.lockfile = lockedfile.MutexAt(path.Join(b.path, multiLockFileName)) + b.mu.Unlock() } // lock backend - if err := b.lockFile(); err != nil { + if unlock, err := b.lockfile.Lock(); err != nil { return false, &hst.AppError{Step: "lock store segment", Err: err} + } else { + // unlock backend after Do is complete + defer unlock() } // expose backend methods without exporting the pointer @@ -66,10 +68,6 @@ func (s *multiStore) Do(identity int, f func(c Cursor)) (bool, error) { // disable access to the backend on a best-effort basis c.multiBackend = nil - // unlock backend - if err := b.unlockFile(); err != nil { - return true, &hst.AppError{Step: "unlock store segment", Err: err} - } return true, nil } @@ -108,59 +106,17 @@ func (s *multiStore) List() ([]int, error) { return append([]int(nil), aidsBuf...), nil } -func (s *multiStore) Close() error { - s.mu.Lock() - defer s.mu.Unlock() - - var errs []error - s.backends.Range(func(_, value any) bool { - b := value.(*multiBackend) - errs = append(errs, b.close()) - return true - }) - - return errors.Join(errs...) -} - type multiBackend struct { path string // created/opened by prepare - lockfile *os.File + lockfile *lockedfile.Mutex mu sync.RWMutex } func (b *multiBackend) filename(id *hst.ID) string { return path.Join(b.path, id.String()) } -func (b *multiBackend) lockFileAct(lt int) (err error) { - op := "LockAct" - switch lt { - case syscall.LOCK_EX: - op = "Lock" - case syscall.LOCK_UN: - op = "Unlock" - } - - for { - err = syscall.Flock(int(b.lockfile.Fd()), lt) - if !errors.Is(err, syscall.EINTR) { - break - } - } - if err != nil { - return &fs.PathError{ - Op: op, - Path: b.lockfile.Name(), - Err: err, - } - } - return nil -} - -func (b *multiBackend) lockFile() error { return b.lockFileAct(syscall.LOCK_EX) } -func (b *multiBackend) unlockFile() error { return b.lockFileAct(syscall.LOCK_UN) } - // reads all launchers in simpleBackend // file contents are ignored if decode is false func (b *multiBackend) load(decode bool) (map[hst.ID]*hst.State, error) { @@ -184,6 +140,11 @@ func (b *multiBackend) load(decode bool) (map[hst.ID]*hst.State, error) { return nil, fmt.Errorf("unexpected directory %q in store", e.Name()) } + // skip lock file + if e.Name() == multiLockFileName { + continue + } + var id hst.ID if err := id.UnmarshalText([]byte(e.Name())); err != nil { return nil, &hst.AppError{Step: "parse state key", Err: err} @@ -268,17 +229,6 @@ func (b *multiBackend) Len() (int, error) { return len(rn), nil } -func (b *multiBackend) close() error { - b.mu.Lock() - defer b.mu.Unlock() - - err := b.lockfile.Close() - if err == nil || errors.Is(err, os.ErrInvalid) || errors.Is(err, os.ErrClosed) { - return nil - } - return &hst.AppError{Step: "close lock file", Err: err} -} - // NewMulti returns an instance of the multi-file store. func NewMulti(msg message.Msg, runDir string) Store { return &multiStore{ |
