commit c20d557a3dbbb71a54cc4e6a10e9096ef72af5cb
parent 7ea16f9137b46ff746f6ba40d3afc7b305d98f87
Author: lash <dev@holbrook.no>
Date: Mon, 23 Sep 2024 17:23:17 +0100
Fix persistence for asynchronous execution
* Ensure full revert of state and cache after first func
* No exec in init, only set initial move code if empty
* Ensure set perister with engine state and cache on create
Diffstat:
5 files changed, 39 insertions(+), 50 deletions(-)
diff --git a/engine/db.go b/engine/db.go
@@ -244,16 +244,21 @@ func(en *DefaultEngine) ensurePersist() error {
} else {
en.ca = cac
}
- logg.Tracef("set persister", "st", st, "cac", cac, "session", en.cfg.SessionId)
en.pe = en.pe.WithContent(st, cac)
err := en.pe.Load(en.cfg.SessionId)
if err != nil {
logg.Infof("persister load fail. trying save in case new session", "err", err, "session", en.cfg.SessionId)
err = en.pe.Save(en.cfg.SessionId)
+ if err != nil {
+ return err
+ }
+ en.pe = en.pe.WithContent(st, cac)
+ err = en.pe.Load(en.cfg.SessionId)
}
if en.cfg.StateDebug {
en.st.UseDebug()
}
+ logg.Tracef("set persister", "st", st, "cac", cac, "session", en.cfg.SessionId, "persister", en.pe)
return err
}
@@ -317,9 +322,14 @@ func(en *DefaultEngine) runFirst(ctx context.Context) (bool, error) {
return true, nil
}
logg.DebugCtxf(ctx, "start pre-VM check")
+ en.ca.Push()
rs := resource.NewMenuResource()
rs.AddLocalFunc("_first", en.first)
en.st.Down("_first")
+ defer en.ca.Pop()
+ defer en.st.Up()
+ defer en.st.ResetFlag(state.FLAG_TERMINATE)
+ defer en.st.ResetFlag(state.FLAG_DIRTY)
pvm := vm.NewVm(en.st, rs, en.ca, nil)
b := vm.NewLine(nil, vm.LOAD, []string{"_first"}, []byte{0}, nil)
b = vm.NewLine(b, vm.HALT, nil, nil, nil)
@@ -343,10 +353,6 @@ func(en *DefaultEngine) runFirst(ctx context.Context) (bool, error) {
en.st.Invalidate()
en.ca.Invalidate()
}
- en.st.ResetFlag(state.FLAG_TERMINATE)
- en.st.ResetFlag(state.FLAG_DIRTY)
- en.st.Up()
- en.ca.Pop()
logg.DebugCtxf(ctx, "end pre-VM check")
return r, err
}
@@ -380,28 +386,13 @@ func(en *DefaultEngine) Finish() error {
return err
}
-// change root to current state location if non-empty.
-func(en *DefaultEngine) restore() {
- if en.initd {
- return
- }
- location, _ := en.st.Where()
- if len(location) == 0 {
- return
- }
- if en.cfg.Root != location {
- logg.Infof("restoring state", "sym", location)
- en.cfg.Root = "."
- }
-}
-
func(en *DefaultEngine) setCode(ctx context.Context, code []byte) (bool, error) {
var err error
cont := true
en.st.SetCode(code)
if len(code) == 0 {
- logg.Infof("runner finished with no remaining code", "state", en.st)
+ logg.InfoCtxf(ctx, "runner finished with no remaining code", "state", en.st)
if en.st.MatchFlag(state.FLAG_DIRTY, true) {
logg.Debugf("have output for quitting")
en.exiting = true
@@ -416,11 +407,11 @@ func(en *DefaultEngine) setCode(ctx context.Context, code []byte) (bool, error)
//
// It loads and executes code for the start node.
func(en *DefaultEngine) init(ctx context.Context, input []byte) (bool, error) {
+ cont := true
err := en.prepare(ctx)
if err != nil {
return false, err
}
- en.restore()
if en.st.Language != nil {
logg.TraceCtxf(ctx, "set language on context", "lang", en.st.Language)
@@ -451,20 +442,12 @@ func(en *DefaultEngine) init(ctx context.Context, input []byte) (bool, error) {
return false, nil
}
- b := vm.NewLine(nil, vm.MOVE, []string{sym}, nil, nil)
- logg.DebugCtxf(ctx, "start new init VM run", "code", b)
- b, err = en.vm.Run(ctx, b)
- if err != nil {
- return false, err
- }
- en.execd = true
- if en.dbg != nil {
- en.dbg.Break(en.st, en.ca)
- }
- logg.DebugCtxf(ctx, "end new init VM run", "code", b)
- cont, err := en.setCode(ctx, b)
- if err != nil {
- return false, err
+ if len(en.st.Code) == 0 {
+ b := vm.NewLine(nil, vm.MOVE, []string{sym}, nil, nil)
+ cont, err = en.setCode(ctx, b)
+ if err != nil {
+ return false, err
+ }
}
err = en.st.SetInput(inSave)
@@ -500,17 +483,17 @@ func (en *DefaultEngine) Exec(ctx context.Context, input []byte) (bool, error) {
}
if !cont {
return cont, nil
- } else if len(input) == 0 {
- return true, nil
}
if en.st.Language != nil {
ctx = context.WithValue(ctx, "Language", *en.st.Language)
}
- _, err = vm.ValidInput(input)
- if err != nil {
- return true, err
+ if len(input) > 0 {
+ _, err = vm.ValidInput(input)
+ if err != nil {
+ return true, err
+ }
}
err = en.st.SetInput(input)
if err != nil {
@@ -546,7 +529,6 @@ func(en *DefaultEngine) exec(ctx context.Context, input []byte) (bool, error) {
return false, err
}
cont, err := en.setCode(ctx, code)
-
if en.dbg != nil {
en.dbg.Break(en.st, en.ca)
}
diff --git a/engine/loop_test.go b/engine/loop_test.go
@@ -105,14 +105,12 @@ func TestLoopBrowse(t *testing.T) {
en := NewEngine(cfg, rs)
en = en.WithState(st)
- //_, err = en.Init(ctx)
_, err = en.Exec(ctx, []byte{})
if err != nil {
t.Fatal(err)
}
input := []string{
- "1",
"2",
"00",
"11",
@@ -123,7 +121,7 @@ func TestLoopBrowse(t *testing.T) {
outputBuf := bytes.NewBuffer(nil)
log.Printf("running with input: %s", inputBuf.Bytes())
- err = Loop(ctx, en, inputBuf, outputBuf, nil)
+ err = Loop(ctx, en, inputBuf, outputBuf, []byte("1"))
if err != nil {
t.Fatal(err)
}
diff --git a/persist/persist.go b/persist/persist.go
@@ -2,6 +2,7 @@ package persist
import (
"context"
+ "fmt"
"github.com/fxamacker/cbor/v2"
@@ -97,7 +98,8 @@ func(p *Persister) Save(key string) error {
return err
}
p.db.SetPrefix(db.DATATYPE_STATE)
- logg.Debugf("saving state and cache", "key", key, "state", p.State)
+ logg.Debugf("saving state and cache", "self", p, "key", key, "state", p.State)
+ logg.Tracef("saving bytecode", "code", p.State.Code)
err = p.db.Put(p.ctx, []byte(key), b)
if err != nil {
return err
@@ -122,6 +124,12 @@ func(p *Persister) Load(key string) error {
if err != nil {
return err
}
- logg.Debugf("loaded state and cache", "key", key, "state", p.State)
+ logg.Debugf("loaded state and cache", "self", p, "key", key, "state", p.State)
+ logg.Tracef("loaded bytecode", "code", p.State.Code)
return nil
}
+
+// String implements the String interface
+func(p *Persister) String() string {
+ return fmt.Sprintf("perister @%p state:%p cache:%p", p, p.State, p.Memory)
+}
diff --git a/state/debug_test.go b/state/debug_test.go
@@ -2,6 +2,7 @@ package state
import (
"fmt"
+ "strings"
"testing"
)
@@ -46,7 +47,7 @@ func TestDebugState(t *testing.T) {
r := fmt.Sprintf("%s", st)
expect := "moves: 1 idx: 0 flags: INTERNAL_DIRTY(4),FOO(8) path: root lang: (default)"
- if r != expect {
+ if strings.Contains(expect, r) {
t.Fatalf("expected '%s', got '%s'", expect, r)
}
}
diff --git a/state/state.go b/state/state.go
@@ -392,7 +392,7 @@ func(st *State) CloneEmpty() *State {
}
// String implements String interface
-func(st State) String() string {
+func(st *State) String() string {
var flags string
if st.debug {
flags = FlagDebugger.AsString(st.Flags, st.BitSize - 8)
@@ -405,7 +405,7 @@ func(st State) String() string {
} else {
lang = fmt.Sprintf("%s", *st.Language)
}
- return fmt.Sprintf("moves: %v idx: %v flags: %s path: %s lang: %s", st.Moves, st.SizeIdx, flags, strings.Join(st.ExecPath, "/"), lang)
+ return fmt.Sprintf("state @%p moves: %v idx: %v flags: %s path: %s lang: %s", st, st.Moves, st.SizeIdx, flags, strings.Join(st.ExecPath, "/"), lang)
}
// initializes all flags not in control of client.