typestates/with_bound¶
The with_bound macro provides a scoped binding for an endpoint
view: within the macro body, the endpoint is in the Bound state
and the user code may push / pop through it; on scope exit, the
endpoint transitions back to Unbound (or Closed, depending on the
shape's destructor contract).
This is the recommended form for the common case of a worker thread
that acquires an endpoint, drives it for a bounded period of work,
and releases it on exit. The explicit getProducer() /
bindToThread() / detach() triple is retained for cases where the
endpoint must outlive a single scope (for example, when stored on a
long-lived worker object).
See also¶
- Typestates facade — overview and submodule index.
with_bound
¶
RAII wrapper template (withBoundEndpoint / withBoundProducer /
withBoundConsumer) + Queueable[T] concept per design §5.5.
Purpose¶
v0.1.0 surfaces two API styles for endpoint binding:
-
Typestate-guarded surface (existing) — users call
q.getProducer(), thenbindToThread(), thenpush(item), thenclose(). TheUnbound → Bound → Closedtypestate FSM is visible and provides compile-time guards (nopushonUnbound, no double bind, no use-after-close). -
RAII surface (this module) —
withBoundProducer(q, p): bodyexpands to a block that runsgetProducer + bindToThread, executesbodywithpbound to theBound[...]endpoint, and runscloseon scope exit viadefer. The typestate FSM still drives the compile-time guards insidebody; the user just doesn't type the transitions.
Both surfaces dispatch to the same per-cardinality push/pop bodies — there is no duplicate implementation. Bug fixes apply uniformly.
The Queueable[T] concept (§5.5.5) is a non-typestate-aware
ergonomic surface for library code that wants a single generic
Queueable[T]-shaped parameter rather than Queue[T, ccProd, ...] |
BQueue[T, ccProd, ...] | Bound[T, Tag, Queue[T, ...]] enumerated
arms. Concept resolution is verified across the full Path-C-encoded
payload set (ref X / string / seq[U] / POD T).
Path-C transparency (operator directive 2026-06-06)¶
Both surfaces work uniformly across ref X (via ManagedRef), string
/ seq[U] (via ManagedSlice), and POD T. The Path-C encoding routing
happens INSIDE the existing per-cardinality push/pop bodies; this
module never touches encoded slots. Users see Queue[ref Foo] /
Queue[string] / Queue[seq[int]] / Queue[int] with the same
surface regardless of which API style they choose.
withBoundProducer ¶
template withBoundProducer(queue: var BQueue[T, ccMulti, ccCons, N, P, C]; endpoint: untyped; body: untyped): untyped
BQueue multi-producer RAII wrapper. Acquires a per-thread producer
slot, transitions to Bound, runs body, then transitions to
EndpointClosed on scope exit.
Parameters
-
queue(var BQueue[T, ccMulti, ccCons, N, P, C]) -
endpoint(untyped) -
body(untyped)
Returns
untyped
withBoundConsumer ¶
template withBoundConsumer(queue: var BQueue[T, ccProd, ccMulti, N, P, C]; endpoint: untyped; body: untyped): untyped
BQueue multi-consumer RAII wrapper. Symmetric to
withBoundProducer.
Parameters
-
queue(var BQueue[T, ccProd, ccMulti, N, P, C]) -
endpoint(untyped) -
body(untyped)
Returns
untyped
withBoundProducer ¶
template withBoundProducer(queue: var Queue[T, ccProd, ccCons, ST, S, MaxThreads]; endpoint: untyped; body: untyped): untyped
Queue producer RAII wrapper. Covers all four cardinality arms
(SPSC / MPSC / SPMC / MPMC) — getProducer on Queue is defined
uniformly in endpoint.nim. SPSC absorbed arm (ccProd ==
ccSingle and ccCons == ccSingle) is debra-free, so
bindToThread is a no-op there; the template's shape is
unchanged.
Parameters
-
queue(var Queue[T, ccProd, ccCons, ST, S, MaxThreads]) -
endpoint(untyped) -
body(untyped)
Returns
untyped
withBoundConsumer ¶
template withBoundConsumer(queue: var Queue[T, ccProd, ccCons, ST, S, MaxThreads]; endpoint: untyped; body: untyped): untyped
Queue consumer RAII wrapper. Symmetric to withBoundProducer.
Parameters
-
queue(var Queue[T, ccProd, ccCons, ST, S, MaxThreads]) -
endpoint(untyped) -
body(untyped)
Returns
untyped
withBoundEndpoint ¶
template withBoundEndpoint(queue, endpoint, body: untyped): untyped
WARNING: this umbrella alias ALWAYS binds a PRODUCER endpoint (it
forwards to withBoundProducer). A consumer-intent call here will SILENTLY acquire a producer slot — there is no role auto-detection. For a consumer endpoint you MUST call withBoundConsumer explicitly. The producer/consumer dispatch (getProducer vs getConsumer) cannot be inferred from the queue type alone, so this alias hard-codes the producer role.
Parameters
-
queue(untyped) -
endpoint(untyped) -
body(untyped)
Returns
untyped
Queueable
¶
type Queueable[T] = concept x
## Type-class for "anything queue-like accepting / yielding `T`".
##
## Resolution: Nim's concept machinery instantiates the body with
## `x` bound to a candidate type and checks each expression compiles
## and has the stated type. The `var x` form admits both `var Queue`
## and `var BQueue` direct-push variants and `Bound` endpoint
## variants alike — push/pop on `Bound` take `self: Bound[...]`
## (not var), but Nim's overload resolution falls through to those
## from the `var x` candidate.
var qref: typeof(x)
push(qref, default(T)) is bool
pop(qref) is Option[T]