TinyGo port of actor: CSP/actor-model building blocks
git clone https://git.smesh.lol/tiny-actor
Generic building blocks for CSP/actor-model concurrency, ported to TinyGo.
Each type maps to a common inter-goroutine communication pattern, eliminating the per-actor boilerplate of defining request structs, response channels, and caller methods.
| Type | Caller | Actor | Pattern |
|---|---|---|---|
Func[Req, Resp] | Call(req) -> resp | msg.Req, msg.Reply(resp) | function call |
Query[Resp] | Call() -> resp | msg.Reply(resp) | getter |
Proc[Req] | Call(req) | msg.Req, msg.Done() | setter / command |
Signal | Call() | msg.Done() | trigger / reset |
Inbox[T] | Send(v) / TrySend(v) | <-i.Recv() | fire-and-forget |
Lifecycle | Stop() | <-Stopping(), defer MarkDone() | shutdown |
All synchronous types use unbuffered channels. The caller blocks until the actor processes the message. This eliminates races where a buffered write appears to succeed but the actor hasn't seen it yet.
import "git.smesh.lol/tiny-actor"
type Counter struct {
inc actor.Signal
get actor.Query[int]
actor.Lifecycle
}
func NewCounter() *Counter {
c := &Counter{
inc: actor.NewSignal(),
get: actor.NewQuery[int](),
Lifecycle: actor.NewLifecycle(),
}
actor.Go(c.Lifecycle, c.run)
return c
}
func (c *Counter) run() {
var n int
for {
select {
case <-c.Stopping():
return
case msg := <-c.inc.Recv():
n++
msg.Done()
case msg := <-c.get.Recv():
msg.Reply(n)
}
}
}
func (c *Counter) Inc() { c.inc.Call() }
func (c *Counter) Get() int { return c.get.Call() }
This is the TinyGo port of actor. The API is unchanged and the package is pure Go: no cgo, no reflection, no sync primitives, no OS-specific code. It was ported and tested with TinyGo 0.43.0 on Go 1.25.3.
The library needs goroutines, so the target must have a scheduler:
wasip1 and wasm use the asyncify scheduler and run the tests as-is.tasks scheduler and compile.-scheduler=none and cannot run actors. Enabling tasks there overflows the flash and RAM of the classic Arduino targets.
Run the test suite:
tinygo test -target=wasip1 ./...
tinygo test -target=wasm ./...
go test ./... also passes; TinyGo is the primary target.
coordination is via channels and select.
happens-before: when Call returns, the actor has processed the message. Use Inbox for intentionally async paths.
Callers get copies via response channels, never references.
interface to satisfy, no registration.
lookup, filtered query)
config)
(set value, record event)
flush, increment)
Use TrySend to drop on backpressure.
This library implements the intra-domain actor patterns described in Moxie Architecture Patterns - specifically the actor loop (section 3.1), typed operation channels (section 3.2), the synchronous-mutation rule (section 3.3), and context-aware calls (section 3.6). The five channel types are a direct realization of the taxonomy from section 3.2.
Same architecture, four implementations:
go get git.smesh.lol/actorgo get git.smesh.lol/tiny-actorpickle = { git = "https://git.smesh.lol/pickle" }npm install annealgo get git.smesh.lol/tiny-actor
AGPL-3.0-or-later