Add engine and state restart on empty termination node

This commit is contained in:
lash
2023-04-16 10:40:41 +01:00
parent 957d59bb1a
commit bf1d634474
6 changed files with 121 additions and 21 deletions

View File

@@ -28,6 +28,7 @@ type Engine struct {
rs resource.Resource
ca cache.Memory
vm *vm.Vm
root string
initd bool
}
@@ -44,11 +45,8 @@ func NewEngine(cfg Config, st *state.State, rs resource.Resource, ca cache.Memor
ca: ca,
vm: vm.NewVm(st, rs, ca, szr),
}
var err error
if st.Moves == 0 {
err = engine.Init(cfg.Root, ctx)
}
return engine, err
engine.root = cfg.Root
return engine, nil
}
// Init must be explicitly called before using the Engine instance.
@@ -62,6 +60,7 @@ func(en *Engine) Init(sym string, ctx context.Context) error {
if sym == "" {
return fmt.Errorf("start sym empty")
}
inSave, _ := en.st.GetInput()
err := en.st.SetInput([]byte{})
if err != nil {
return err
@@ -77,6 +76,10 @@ func(en *Engine) Init(sym string, ctx context.Context) error {
}
log.Printf("ended init VM run with code %x", b)
en.st.SetCode(b)
err = en.st.SetInput(inSave)
if err != nil {
return err
}
en.initd = true
return nil
}
@@ -92,7 +95,17 @@ func(en *Engine) Init(sym string, ctx context.Context) error {
// - no current bytecode is available
// - input processing against bytcode failed
func (en *Engine) Exec(input []byte, ctx context.Context) (bool, error) {
err := vm.ValidInput(input)
var err error
if en.st.Moves == 0 {
err = en.Init(en.root, ctx)
if err != nil {
return false, err
}
if len(input) == 0 {
return true, nil
}
}
err = vm.ValidInput(input)
if err != nil {
return true, err
}
@@ -109,6 +122,7 @@ func (en *Engine) Exec(input []byte, ctx context.Context) (bool, error) {
if len(code) == 0 {
return false, fmt.Errorf("no code to execute")
}
log.Printf("start new VM run with code %x", code)
code, err = en.vm.Run(code, ctx)
if err != nil {
@@ -124,13 +138,14 @@ func (en *Engine) Exec(input []byte, ctx context.Context) (bool, error) {
if len(code) > 0 {
log.Printf("terminated with code remaining: %x", code)
}
return false, nil
return false, err
}
en.st.SetCode(code)
if len(code) == 0 {
log.Printf("runner finished with no remaining code")
return false, nil
err = en.reset(ctx)
return false, err
}
return true, nil
@@ -149,3 +164,22 @@ func(en *Engine) WriteResult(w io.Writer, ctx context.Context) (int, error) {
}
return io.WriteString(w, r)
}
func(en *Engine) reset(ctx context.Context) error {
var err error
var isTop bool
for !isTop {
isTop, err = en.st.Top()
if err != nil {
return err
}
_, err = en.st.Up()
if err != nil {
return err
}
en.ca.Pop()
}
en.st.Restart()
en.initd = false
return en.Init(en.root, ctx)
}

View File

@@ -87,7 +87,11 @@ func TestEngineInit(t *testing.T) {
if err != nil {
t.Fatal(err)
}
//
err = en.Init("root", ctx)
if err != nil {
t.Fatal(err)
}
w := bytes.NewBuffer(nil)
_, err = en.WriteResult(w, ctx)
if err != nil {
@@ -152,3 +156,40 @@ func TestEngineExecInvalidInput(t *testing.T) {
t.Fatalf("expected fail on invalid input")
}
}
func TestEngineResumeTerminated(t *testing.T) {
generateTestData(t)
ctx := context.TODO()
st := state.NewState(17)
rs := NewFsWrapper(dataDir, &st)
ca := cache.NewCache().WithCacheSize(1024)
en, err := NewEngine(Config{
Root: "root",
}, &st, &rs, ca, ctx)
if err != nil {
t.Fatal(err)
}
err = en.Init("root", ctx)
if err != nil {
t.Fatal(err)
}
_, err = en.Exec([]byte("1"), ctx)
if err != nil {
t.Fatal(err)
}
_, err = en.Exec([]byte("1"), ctx)
if err != nil {
t.Fatal(err)
}
location, idx := st.Where()
if location != "root" {
t.Fatalf("expected 'root', got %s", location)
}
if idx != 0 {
t.Fatalf("expected idx '0', got %v", idx)
}
}