Skip to content

Commit

Permalink
Modify frames by value then store when unwinding
Browse files Browse the repository at this point in the history
  • Loading branch information
chriso committed Sep 18, 2023
1 parent 2bea6f5 commit 7c5c762
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 50 deletions.
14 changes: 11 additions & 3 deletions compiler/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ func (c *compiler) compileFunction(p *packages.Package, fn *ast.FuncDecl, color

ctx := ast.NewIdent("_c")
frame := ast.NewIdent("_f")
fp := ast.NewIdent("_fp")

yieldTypeExpr := make([]ast.Expr, 2)
yieldTypeExpr[0] = typeExpr(color.Params().At(0).Type())
Expand All @@ -277,9 +278,9 @@ func (c *compiler) compileFunction(p *packages.Package, fn *ast.FuncDecl, color
},
})

// _f := _c.Push()
// _f, _fp := _c.Push()
gen.Body.List = append(gen.Body.List, &ast.AssignStmt{
Lhs: []ast.Expr{frame},
Lhs: []ast.Expr{frame, fp},
Tok: token.DEFINE,
Rhs: []ast.Expr{
&ast.CallExpr{
Expand Down Expand Up @@ -379,7 +380,14 @@ func (c *compiler) compileFunction(p *packages.Package, fn *ast.FuncDecl, color
Cond: &ast.CallExpr{
Fun: &ast.SelectorExpr{X: ctx, Sel: ast.NewIdent("Unwinding")},
},
Body: &ast.BlockStmt{List: saveStmts},
Body: &ast.BlockStmt{
List: append(saveStmts, &ast.ExprStmt{
X: &ast.CallExpr{
Fun: &ast.SelectorExpr{X: ctx, Sel: ast.NewIdent("Store")},
Args: []ast.Expr{fp, frame},
},
}),
},
Else: &ast.BlockStmt{List: []ast.Stmt{
&ast.ExprStmt{X: &ast.CallExpr{Fun: &ast.SelectorExpr{X: ctx, Sel: ast.NewIdent("Pop")}}}},
},
Expand Down
42 changes: 28 additions & 14 deletions compiler/testdata/coroutine_durable.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 15 additions & 11 deletions context_durable.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ package coroutine
import "github.com/stealthrocket/coroutine/internal/serde"

type serializedCoroutine struct {
entry func()
stack Stack
entry func()
stack Stack
resume bool
}

func init() {
Expand All @@ -24,9 +25,10 @@ type Context[R, S any] struct {
recv R
send S

// Booleans managing the completion state of the coroutine.
done bool
stop bool
// Booleans managing the state of the coroutine.
done bool
stop bool
resume bool

// Entry point of the coroutine, this is captured so the associated
// generator can call into the coroutine to start or resume it at the
Expand All @@ -39,8 +41,9 @@ type Context[R, S any] struct {
// MarshalAppend appends a serialized Context to the provided buffer.
func (c *Context[R, S]) MarshalAppend(b []byte) ([]byte, error) {
s := serde.Serialize(&serializedCoroutine{
entry: c.entry,
stack: c.Stack,
entry: c.entry,
stack: c.Stack,
resume: c.resume,
})
return append(b, s...), nil
}
Expand All @@ -54,14 +57,15 @@ func (c *Context[R, S]) Unmarshal(b []byte) (int, error) {
s := v.(*serializedCoroutine)
c.entry = s.entry
c.Stack = s.stack
c.resume = s.resume
sn := start - len(b)
return sn, nil
}

// TODO: do we have use cases for yielding more than one value?
func (c *Context[R, S]) Yield(value R) S {
if frame := c.Top(); frame.Resume {
frame.Resume = false
if c.resume {
c.resume = false
if c.stop {
panic(unwind{})
}
Expand All @@ -71,7 +75,7 @@ func (c *Context[R, S]) Yield(value R) S {
panic("cannot yield from a coroutine that has been stopped")
}
var zero S
frame.Resume = true
c.resume = true
c.send = zero
c.recv = value
panic(unwind{})
Expand All @@ -80,7 +84,7 @@ func (c *Context[R, S]) Yield(value R) S {

// Unwinding returns true if the coroutine is currently unwinding its stack.
func (c *Context[R, S]) Unwinding() bool {
return len(c.Frames) > 0 && c.Top().Resume
return c.resume
}

type unwind struct{}
13 changes: 7 additions & 6 deletions coroutine_durable.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,14 @@ func (c Coroutine[R, S]) Next() (hasNext bool) {
defer func() {
clearContext(g)

switch err := recover(); err {
case nil:
case unwind{}:
default:
panic(err)
}

if c.ctx.Unwinding() {
switch err := recover(); err {
case nil:
case unwind{}:
default:
panic(err)
}
stop := c.ctx.stop
c.ctx.done, hasNext = stop, !stop
} else {
Expand Down
Loading

0 comments on commit 7c5c762

Please sign in to comment.