Motivation
Building a poll-based UI event loop (xsofy #56) that multiplexes a keyboard source and a cosmetic clock source and must never block while idle. This is mandatory under single-threaded Go-wasm, where a parked <! (backed by Atomics.wait) freezes the entire runtime, not just the goroutine. The loop needs to drain a producer-fed channel once per frame if and only if a value is immediately ready.
What's missing
async exposes only blocking takes (<!/<!!). There is no non-blocking variant and no alts!. Registered channel fns today are just chan, >!, <!, >!!, <!! (pkg/rt/lang.go:5969-5973).
Everything else the use case needs already exists, which is why only this one primitive is requested:
- Cancellable channel ops —
<!/>! already select on vm.CurrentContext().Done() (pkg/rt/lang.go:3446, :3472), so a producer goroutine parked on a put/take is released by scope-close!'s cancel+drain.
- Scoped lifecycle —
with-scope (pkg/rt/core/core.lg:2091), scope-open/scope-close!/scope-live (pkg/rt/lang.go:5974-5976), *scope-drain-timeout-ms* (core.lg:2089).
So a ctx-aware, drainable producer is writable today; the only gap is the consumer's non-blocking peek.
Requested
(poll! ch) — non-blocking take. Returns a value if one is immediately available, else a distinct empty signal (so a nil value can be distinguished from "nothing ready" — e.g. return a sentinel, or [val ok]). Go semantics:
select {
case v, ok := <-ch:
// ok==false => closed
default:
// nothing ready
}
- (nice-to-have)
(alts! [ch…] :default v) — select over several channels with a non-blocking default; generalizes poll! to multiple upstreams.
- (symmetry, optional)
(offer! ch v) — non-blocking put.
Notes
poll! alone unblocks the use case; alts! is the general form.
- Keep ctx-awareness for consistency, though a
:default branch means the non-blocking path never parks anyway.
Motivation
Building a poll-based UI event loop (xsofy #56) that multiplexes a keyboard source and a cosmetic clock source and must never block while idle. This is mandatory under single-threaded Go-wasm, where a parked
<!(backed byAtomics.wait) freezes the entire runtime, not just the goroutine. The loop needs to drain a producer-fed channel once per frame if and only if a value is immediately ready.What's missing
asyncexposes only blocking takes (<!/<!!). There is no non-blocking variant and noalts!. Registered channel fns today are justchan,>!,<!,>!!,<!!(pkg/rt/lang.go:5969-5973).Everything else the use case needs already exists, which is why only this one primitive is requested:
<!/>!alreadyselectonvm.CurrentContext().Done()(pkg/rt/lang.go:3446,:3472), so a producer goroutine parked on a put/take is released byscope-close!'s cancel+drain.with-scope(pkg/rt/core/core.lg:2091),scope-open/scope-close!/scope-live(pkg/rt/lang.go:5974-5976),*scope-drain-timeout-ms*(core.lg:2089).So a ctx-aware, drainable producer is writable today; the only gap is the consumer's non-blocking peek.
Requested
(poll! ch)— non-blocking take. Returns a value if one is immediately available, else a distinct empty signal (so anilvalue can be distinguished from "nothing ready" — e.g. return a sentinel, or[val ok]). Go semantics:(alts! [ch…] :default v)— select over several channels with a non-blocking default; generalizespoll!to multiple upstreams.(offer! ch v)— non-blocking put.Notes
poll!alone unblocks the use case;alts!is the general form.:defaultbranch means the non-blocking path never parks anyway.