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>
65 lines
2.2 KiB
Plaintext
65 lines
2.2 KiB
Plaintext
;; lib/go/sched.sx — Go scheduler primitives: channels + goroutines.
|
|
;;
|
|
;; This is **the independent implementation** referenced by
|
|
;; plans/lib-guest-scheduler.md. The shape that emerges here informs
|
|
;; the eventual sister kit; this file's structures are the Phase 5
|
|
;; "first-consumer" cut.
|
|
;;
|
|
;; v0 concurrency model — IMPORTANT
|
|
;;
|
|
;; SX has no first-class continuations exposed to guest code, so we
|
|
;; can't suspend a goroutine mid-statement. v0 runs `go f()` SYNCHRO-
|
|
;; NOUSLY (it's an immediate call whose return value is dropped). This
|
|
;; preserves the right semantics for patterns where the spawned
|
|
;; goroutine simply pushes to a channel that the main goroutine then
|
|
;; receives — because the spawned goroutine runs to completion first
|
|
;; and leaves the value in the channel buffer.
|
|
;;
|
|
;; True preemption with blocking sends/recvs is a Phase 5b refinement.
|
|
;; The sister-plan diary tracks the design insight (single
|
|
;; sched-spawn primitive, channel-op direction tag) so the eventual
|
|
;; kit doesn't bake in v0's synchronous limitation.
|
|
;;
|
|
;; Channel representation
|
|
;;
|
|
;; (list :go-chan ACCESSORS-FN-LIST)
|
|
;;
|
|
;; ACCESSORS-FN-LIST is a list of closures sharing a mutable buffer
|
|
;; and a closed flag. The closures expose:
|
|
;; index 1: send-fn — (lambda (val) ...)
|
|
;; index 2: recv-fn — (lambda () val-or-:empty)
|
|
;; index 3: closed?-fn — (lambda () bool)
|
|
;; index 4: close!-fn — (lambda () ...)
|
|
;;
|
|
;; Channel identity: distinct calls to go-make-chan produce closures
|
|
;; with distinct identity — `(= ch1 ch2)` is false for distinct
|
|
;; channels, matching Go spec § Channel types.
|
|
|
|
(define
|
|
go-make-chan
|
|
(fn
|
|
()
|
|
(let
|
|
((buf (list)) (closed false))
|
|
(list
|
|
:go-chan (fn (v) (append! buf v) nil)
|
|
(fn
|
|
()
|
|
(cond
|
|
(= (len buf) 0)
|
|
:empty :else
|
|
(let ((v (first buf))) (set! buf (rest buf)) v)))
|
|
(fn () closed)
|
|
(fn () (set! closed true) nil)))))
|
|
|
|
(define
|
|
go-chan?
|
|
(fn
|
|
(v)
|
|
(and (list? v) (not (= (len v) 0)) (= (first v) :go-chan))))
|
|
|
|
(define go-chan-send! (fn (ch val) ((nth ch 1) val)))
|
|
(define go-chan-recv! (fn (ch) ((nth ch 2))))
|
|
(define go-chan-closed? (fn (ch) ((nth ch 3))))
|
|
(define go-chan-close! (fn (ch) ((nth ch 4))))
|