diff options
| author | Ophestra Umiker <cat@ophivana.moe> | 2024-12-19 11:48:48 +0900 |
|---|---|---|
| committer | Ophestra Umiker <cat@ophivana.moe> | 2024-12-19 11:48:48 +0900 |
| commit | f796622c35ad15f4a8d2e91fb0437f566fc77e52 (patch) | |
| tree | 0fd0ec8dcacd24d19466a2f95c2328059eb9e211 /internal/state/simple.go | |
| parent | 5d25bee7869e5ecec20e9bddc9f27d9b4a0b0988 (diff) | |
state: rename simple store implementation
Signed-off-by: Ophestra Umiker <cat@ophivana.moe>
Diffstat (limited to 'internal/state/simple.go')
| -rw-r--r-- | internal/state/simple.go | 222 |
1 files changed, 0 insertions, 222 deletions
diff --git a/internal/state/simple.go b/internal/state/simple.go deleted file mode 100644 index 1a26f6f7..00000000 --- a/internal/state/simple.go +++ /dev/null @@ -1,222 +0,0 @@ -package state - -import ( - "encoding/gob" - "errors" - "io/fs" - "os" - "path" - "strconv" - "sync" - "syscall" -) - -// file-based locking -type simpleStore struct { - path []string - - // created/opened by prepare - lockfile *os.File - // enforce prepare method - init sync.Once - // error returned by prepare - initErr error - - lock sync.Mutex -} - -func (s *simpleStore) Do(f func(b Backend)) (bool, error) { - s.init.Do(s.prepare) - if s.initErr != nil { - return false, s.initErr - } - - s.lock.Lock() - defer s.lock.Unlock() - - // lock store - if err := s.lockFile(); err != nil { - return false, err - } - - // initialise new backend for caller - b := new(simpleBackend) - b.path = path.Join(s.path...) - f(b) - // disable backend - b.lock.Lock() - - // unlock store - return true, s.unlockFile() -} - -func (s *simpleStore) 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(s.lockfile.Fd()), lt) - if !errors.Is(err, syscall.EINTR) { - break - } - } - if err != nil { - return &fs.PathError{ - Op: op, - Path: s.lockfile.Name(), - Err: err, - } - } - return nil -} - -func (s *simpleStore) lockFile() error { - return s.lockFileAct(syscall.LOCK_EX) -} - -func (s *simpleStore) unlockFile() error { - return s.lockFileAct(syscall.LOCK_UN) -} - -func (s *simpleStore) prepare() { - s.initErr = func() error { - prefix := path.Join(s.path...) - // ensure directory - if err := os.MkdirAll(prefix, 0700); err != nil && !errors.Is(err, fs.ErrExist) { - return err - } - - // open locker file - if f, err := os.OpenFile(prefix+".lock", os.O_RDWR|os.O_CREATE, 0600); err != nil { - return err - } else { - s.lockfile = f - } - - return nil - }() -} - -func (s *simpleStore) Close() error { - s.lock.Lock() - defer s.lock.Unlock() - - err := s.lockfile.Close() - if err == nil || errors.Is(err, os.ErrInvalid) || errors.Is(err, os.ErrClosed) { - return nil - } - return err -} - -type simpleBackend struct { - path string - - lock sync.RWMutex -} - -func (b *simpleBackend) filename(pid int) string { - return path.Join(b.path, strconv.Itoa(pid)) -} - -// reads all launchers in simpleBackend -// file contents are ignored if decode is false -func (b *simpleBackend) load(decode bool) ([]*State, error) { - b.lock.RLock() - defer b.lock.RUnlock() - - var ( - r []*State - f *os.File - ) - - // read directory contents, should only contain files named after PIDs - if pl, err := os.ReadDir(b.path); err != nil { - return nil, err - } else { - for _, e := range pl { - // run in a function to better handle file closing - if err = func() error { - // open state file for reading - if f, err = os.Open(path.Join(b.path, e.Name())); err != nil { - return err - } else { - defer func() { - if f.Close() != nil { - // unreachable - panic("foreign state file closed prematurely") - } - }() - - var s State - r = append(r, &s) - - // append regardless, but only parse if required, used to implement Len - if decode { - return gob.NewDecoder(f).Decode(&s) - } else { - return nil - } - } - }(); err != nil { - return nil, err - } - } - } - - return r, nil -} - -// Save writes process state to filesystem -func (b *simpleBackend) Save(state *State) error { - b.lock.Lock() - defer b.lock.Unlock() - - if state.Config == nil { - return errors.New("state does not contain config") - } - - statePath := b.filename(state.PID) - - // create and open state data file - if f, err := os.OpenFile(statePath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600); err != nil { - return err - } else { - defer func() { - if f.Close() != nil { - // unreachable - panic("state file closed prematurely") - } - }() - // encode into state file - return gob.NewEncoder(f).Encode(state) - } -} - -func (b *simpleBackend) Destroy(pid int) error { - b.lock.Lock() - defer b.lock.Unlock() - - return os.Remove(b.filename(pid)) -} - -func (b *simpleBackend) Load() ([]*State, error) { - return b.load(true) -} - -func (b *simpleBackend) Len() (int, error) { - // rn consists of only nil entries but has the correct length - rn, err := b.load(false) - return len(rn), err -} - -// NewSimple returns an instance of a file-based store. -func NewSimple(runDir string, prefix ...string) Store { - b := new(simpleStore) - b.path = append([]string{runDir, "state"}, prefix...) - return b -} |
