diff options
| author | Ophestra <cat@gensokyo.uk> | 2025-10-27 00:43:33 +0900 |
|---|---|---|
| committer | Ophestra <cat@gensokyo.uk> | 2025-10-28 22:00:54 +0900 |
| commit | 5e5826459ed744fc0bd717e0136614365c7a96f3 (patch) | |
| tree | 44c11b329eb750039b6e48cb0afba368874e42d5 /internal/app/state/state.go | |
| parent | 4a463b7f032961323baa0c5cc4bac0594a90ec42 (diff) | |
internal/app/state: improve handles internals
This replaces the Store interface with something better reflecting the underlying data format for #19. An implementation of Store is provided on top of the new code to ease transition.
Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'internal/app/state/state.go')
| -rw-r--r-- | internal/app/state/state.go | 73 |
1 files changed, 73 insertions, 0 deletions
diff --git a/internal/app/state/state.go b/internal/app/state/state.go index a64568f6..169af507 100644 --- a/internal/app/state/state.go +++ b/internal/app/state/state.go @@ -2,9 +2,16 @@ package state import ( + "strconv" + + "hakurei.app/container/check" "hakurei.app/hst" + "hakurei.app/internal/lockedfile" + "hakurei.app/message" ) +/* this provides an implementation of Store on top of the improved state tracking to ease in the changes */ + type Store interface { // Do calls f exactly once and ensures store exclusivity until f returns. // Returns whether f is called and any errors during the locking process. @@ -16,6 +23,13 @@ type Store interface { List() (identities []int, err error) } +// NewMulti returns an instance of the multi-file store. +func NewMulti(msg message.Msg, prefix *check.Absolute) Store { + store := &stateStore{msg: msg, base: prefix.Append("state")} + store.fileMu = lockedfile.MutexAt(store.base.Append(storeMutexName).String()) + return store +} + // Cursor provides access to the store of an identity. type Cursor interface { Save(state *hst.State) error @@ -23,3 +37,62 @@ type Cursor interface { Load() (map[hst.ID]*hst.State, error) Len() (int, error) } + +// do implements stateStore.Do on storeHandle. +func (h *storeHandle) do(f func(c Cursor)) (bool, error) { + if unlock, err := h.fileMu.Lock(); err != nil { + return false, &hst.AppError{Step: "acquire lock on store segment " + strconv.Itoa(h.identity), Err: err} + } else { + defer unlock() + } + + f(h) + return true, nil +} + +/* these compatibility methods must only be called while fileMu is held */ + +func (h *storeHandle) Save(state *hst.State) error { + return (&stateEntryHandle{nil, h.path.Append(state.ID.String()), state.ID}).save(state) +} + +func (h *storeHandle) Destroy(id hst.ID) error { + return (&stateEntryHandle{nil, h.path.Append(id.String()), id}).destroy() +} + +func (h *storeHandle) Load() (map[hst.ID]*hst.State, error) { + entries, n, err := h.entries() + if err != nil { + return nil, err + } + + r := make(map[hst.ID]*hst.State, n) + for eh := range entries { + if eh.decodeErr != nil { + err = eh.decodeErr + break + } + var s hst.State + if _, err = eh.load(&s); err != nil { + break + } + r[eh.ID] = &s + } + return r, err +} + +func (h *storeHandle) Len() (int, error) { + entries, _, err := h.entries() + if err != nil { + return -1, err + } + + var n int + for eh := range entries { + if eh.decodeErr != nil { + err = eh.decodeErr + } + n++ + } + return n, err +} |
