Skip to content

Commit

Permalink
merge context and coroutine files
Browse files Browse the repository at this point in the history
Signed-off-by: Achille Roussel <[email protected]>
  • Loading branch information
achille-roussel committed Sep 19, 2023
1 parent 879e5af commit 056e003
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 120 deletions.
90 changes: 0 additions & 90 deletions context_durable.go

This file was deleted.

30 changes: 0 additions & 30 deletions context_volatile.go

This file was deleted.

87 changes: 87 additions & 0 deletions coroutine_durable.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,93 @@

package coroutine

import "github.com/stealthrocket/coroutine/internal/serde"

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

func init() {
serde.RegisterType[serializedCoroutine]()
}

// Context is passed to a coroutine and flows through all
// functions that Yield (or could yield).
type Context[R, S any] struct {
// Value passed to Yield when a coroutine yields control back to its caller,
// and value returned to the coroutine when the caller resumes it.
//
// Keep as first fields so they don't use any space if they are the empty
// struct.
recv R
send S

// 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
// last yield point.
entry func()

Stack
}

// 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,
resume: c.resume,
})
return append(b, s...), nil
}

// Unmarshal deserializes a Context from the provided buffer, returning
// the number of bytes that were read in order to reconstruct the
// context.
func (c *Context[R, S]) Unmarshal(b []byte) (int, error) {
start := len(b)
v, b := serde.Deserialize(b)
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 c.resume {
c.resume = false
if c.stop {
panic(unwind{})
}
return c.send
} else {
if c.stop {
panic("cannot yield from a coroutine that has been stopped")
}
var zero S
c.resume = true
c.send = zero
c.recv = value
panic(unwind{})
}
}

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

type unwind struct{}

type Coroutine[R, S any] struct{ ctx *Context[R, S] }

func (c Coroutine[R, S]) Context() *Context[R, S] { return c.ctx }
Expand Down
25 changes: 25 additions & 0 deletions coroutine_volatile.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,31 @@

package coroutine

import "runtime"

type Context[R, S any] struct {
recv R
send S
next chan struct{}
stop bool
done bool
}

func (c *Context[R, S]) Yield(v R) S {
if c.stop {
panic("cannot yield from a coroutine that has been stopped")
}
var zero S
c.send = zero
c.recv = v
c.next <- struct{}{}
<-c.next
if c.stop {
runtime.Goexit()
}
return c.send
}

// Coroutine instances expose APIs allowing the program to drive the execution
// of coroutines.
//
Expand Down

0 comments on commit 056e003

Please sign in to comment.