aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal/app/state/state.go
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2025-10-27 00:43:33 +0900
committerOphestra <cat@gensokyo.uk>2025-10-28 22:00:54 +0900
commit5e5826459ed744fc0bd717e0136614365c7a96f3 (patch)
tree44c11b329eb750039b6e48cb0afba368874e42d5 /internal/app/state/state.go
parent4a463b7f032961323baa0c5cc4bac0594a90ec42 (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.go73
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
+}