go: sched.sx — channels + goroutines (v0 synchronous) + 12 tests; Phase 5 starts [shapes-scheduler]
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 26s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 26s
Phase 5 (goroutines + channels) opens.
lib/go/sched.sx is the **independent implementation** referenced by
plans/lib-guest-scheduler.md — the first-consumer cut whose realised
shape will inform the eventual sister kit.
Channel representation:
(list :go-chan SEND-FN RECV-FN CLOSED?-FN CLOSE!-FN)
Each closure shares a mutable `buf` (a list mutated via append! and
set!) and a `closed` flag. Channel identity is closure-instance —
two `make()` calls produce distinct values per Go spec § Channel types.
Primitive API in sched.sx:
go-make-chan / go-chan? / go-chan-send! / go-chan-recv! /
go-chan-closed? / go-chan-close!
Eval integration in eval.sx:
* `make` and `close` added as builtins. v0 `make()` takes no args
and returns an unbounded-buffer channel.
* `:send` stmt → go-chan-send! on the channel.
* Unary `<-` recv on channel values → go-chan-recv!. `:empty`
sentinel converted to nil (stand-in for blocking semantics).
* `:go expr` → synchronous eval (v0 limitation, see sched.sx
header).
**v0 concurrency model — synchronous goroutines.** SX doesn't expose
first-class continuations to guest code, so v0 runs `go f()`
immediately and depends on the spawned goroutine running to
completion before the main goroutine receives. This is the right
semantics for the simple producer/consumer patterns covered here.
True preemption with blocking send/recv is Phase 5b — requires either
a CEK-style trampolining eval rewrite or kit-level continuation
support. Logged in sched.sx header and in the sister-plan diary.
Runtime suite (12 tests):
* 6 direct API tests: identity, FIFO order, closed-flag
* 6 source-level: make + send + recv, go ping-pong, close,
multi-goroutine fan-in, worker-with-result
Sister-plan scheduler diary updated with the channel-as-closure-
bundle insight and the v0 synchronous-spawn caveat.
runtime 12/12, total 469/469.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -305,6 +305,17 @@ Progress-log line → push `origin/loops/go`.
|
||||
slice triple with capacity) refine but don't gate Phase 5.
|
||||
|
||||
### Phase 5 — Goroutines + channels + select (`lib/go/sched.sx`) ⬜
|
||||
- [x] Scaffold: `lib/go/sched.sx` with `go-make-chan` (closures-over-
|
||||
mutable-buf), `go-chan-send!` / `go-chan-recv!` / `go-chan-closed?`
|
||||
/ `go-chan-close!`. Channel identity via closure-instance.
|
||||
- [x] Eval integration: `make`/`close` builtins; `:send` stmt; unary
|
||||
`<-` recv on channels; `:go` stmt (synchronous in v0 — see
|
||||
sched.sx header).
|
||||
- [ ] Real preemption (suspending sends on full buffer / recvs on empty).
|
||||
Requires reified execution state; deferred to Phase 5b.
|
||||
- [ ] `select { case ... }` multiplexing.
|
||||
- [ ] `range` over channels.
|
||||
- [ ] `time.After`-like timer channel.
|
||||
- **Independent implementation.** Do NOT use lib/guest/scheduler/ — that
|
||||
kit doesn't exist yet and depends on this work for its design. See
|
||||
`plans/lib-guest-scheduler.md`.
|
||||
@@ -586,6 +597,22 @@ Minimal repro: see `lib/go/lex.sx#gl-oct-digit?` and `#gl-match-op`.
|
||||
|
||||
_Newest first. Append one dated entry per commit._
|
||||
|
||||
- 2026-05-27 — **Phase 5 first slice.** `lib/go/sched.sx` lands with
|
||||
the v0 channel primitive: `go-make-chan` returns a closures-over-
|
||||
mutable-buf channel. Send appends, recv pops first, close flips a
|
||||
flag. Channel identity via closure-instance (matches Go spec — two
|
||||
`make()` calls produce distinct values). `make`/`close` are now
|
||||
builtins; `:send` stmt and unary `<-` recv hook through;
|
||||
`:go expr` evaluates synchronously (no real preemption — SX doesn't
|
||||
expose continuations, so v0 runs goroutines to completion in source
|
||||
order). Patterns that rely on the spawned goroutine pushing to a
|
||||
channel before the main reads do work end-to-end. runtime suite
|
||||
12/12 incl. multi-goroutine fan-in and worker-pattern. Total
|
||||
469/469. `[shapes-scheduler]` — sister-plan diary updated with the
|
||||
realised channel-as-closures-over-state shape and the v0
|
||||
synchronous-spawn caveat.
|
||||
|
||||
Sister-plan diary update follows.
|
||||
- 2026-05-27 — Phase 4 cont.: **method dispatch + unary ops + e2e
|
||||
programs. Acceptance bar (80+) crossed.** Methods register under
|
||||
`#method/TYPE/NAME` (same scheme the type checker uses). When `p.M(...)`
|
||||
|
||||
@@ -231,6 +231,39 @@ real result.
|
||||
|
||||
_Newest first. Append one dated entry per milestone landed._
|
||||
|
||||
- 2026-05-27 — From Go-on-SX Phase 5 first slice: the channel
|
||||
primitive landed as closures-over-mutable-state in
|
||||
`lib/go/sched.sx`. Concrete shape:
|
||||
|
||||
```
|
||||
(list :go-chan SEND-FN RECV-FN CLOSED?-FN CLOSE!-FN)
|
||||
```
|
||||
|
||||
Each closure captures a shared `buf` (a mutable list) and `closed`
|
||||
flag (a let-bound boolean mutated via `set!`). Identity: two
|
||||
`make()` calls produce distinct closures, satisfying Go spec
|
||||
§ Channel types' "distinct channels with same type" rule.
|
||||
|
||||
**Design insight for the kit**: the channel-as-closure-bundle shape
|
||||
is the right scheduler-kit primitive — implementation-hide the
|
||||
buffer behind opaque accessor closures, so the underlying storage
|
||||
can be swapped (linked list → ring buffer → segmented array) without
|
||||
changing the API. Erlang's mailboxes will need the same trick.
|
||||
|
||||
**v0 limitation logged**: no real preemption. SX doesn't expose
|
||||
first-class continuations to guest code, so v0 runs `go f()`
|
||||
synchronously and relies on the spawned goroutine completing before
|
||||
the main goroutine receives. Real concurrent semantics — blocking
|
||||
send on full buffer, blocking recv on empty — needs the
|
||||
scheduler kit to ship the suspension/resumption machinery (or for
|
||||
Phase 5b to bake CEK-style trampolining into the eval layer).
|
||||
|
||||
Cross-ref: the `:select-case` uniform shape from the parser-side
|
||||
diary entry pairs with this — the kit's `sched-select` should
|
||||
accept a list of channel-op cases (built from the closures-over-
|
||||
state primitives logged here) and pick a ready one. Source:
|
||||
Go-on-SX commit landing `lib/go/sched.sx` first cut.
|
||||
|
||||
- 2026-05-27 — Follow-up from same Phase 2 work: **`select` AST shape**
|
||||
landed. Each case is `(list :select-case COMM-STMT BODY)` where
|
||||
COMM-STMT is one of `:send`, `:short-decl` (recv into new var),
|
||||
|
||||
Reference in New Issue
Block a user