microsoft/bocpy v0.9.0
microsoft/bocpy
Captured source
source ↗v0.9.0 - Main Pinned Cowns
Repository: microsoft/bocpy
Tag: v0.9.0
Published: 2026-06-06T11:21:36Z
Prerelease: no
Release notes: Main-pinned cowns — a new PinnedCown subclass holds its value as a plain PyObject * on the main interpreter, never round-tripped through XIData. Behaviors whose request set contains any pinned cown are routed by the scheduler to a single-consumer main-thread queue and drained by the new pump entry point (or implicitly by wait, which auto-pumps when pinned cowns exist). Designed for objects that cannot survive cross-interpreter shipping — pyglet shapes, Tk widgets, GPU contexts, open file handles, ctypes pointers. The companion examples/boids.py rewrite demonstrates the coarse-grained pinned-dispatch pattern: per-cell physics stays on workers, and one @when(PinnedCown) per frame batches the write-back into main-thread matrices. Also in this release: quiesce, a non-tearing-down checkpoint primitive.
New Features
quiesce(timeout=None, *, stats=False, noticeboard=False)** —
blocks until every in-flight behavior completes, without tearing down workers or the noticeboard thread. Implemented via a new terminator_seed_inc peer of terminator_seed_dec (Pyrona-style seed-up / seed-down pairing) so quiescence becomes a *checkpoint* rather than a shutdown. Useful for parallel-search patterns that need to inspect a best-so-far cown between rounds and for tests that must read a worker-produced send queue before its producer interpreter is destroyed. The stats and noticeboard flags mirror wait: returns None by default, a per-worker stats list[dict] when stats=True, a noticeboard dict[str, Any] when noticeboard=True, or a WaitResult when both are set. Raises TimeoutError if quiescence is not reached within timeout. Exported from bocpy.__all__.
- `PinnedCown(Cown[T])` — a cown whose value lives
permanently on the main interpreter. Constructible only from the main interpreter (raises RuntimeError from workers); the value is never picklable, never reified twice, and never reconstructed in a worker. The capsule *handle* remains a first-class cross-interpreter shareable — workers may hold it, embed it in a regular Cown value graph, and place it in noticeboard entries, but only the main thread may acquire the value. See the new pinned_cowns page for the full contract and the coarse-grained-dispatch pattern.
- `pump(deadline_ms=None, max_behaviors=None, raise_on_error=False)`
— drains the main-thread queue of behaviors whose request sets contain a PinnedCown. Call from your event loop's idle / on-tick hook (pyglet schedule_interval, Tk after, asyncio task, …); script-mode programs need not call it explicitly because wait pumps internally. Non-preemptive: deadline_ms gates *starting* the next behavior, not interrupting one already running. Body exceptions default to landing on the result cown's .exception; raise_on_error=True re-raises the first body exception after drain. Returns a new PumpResult NamedTuple (executed, deadline_reached, raised).
- `set_pump_watchdog(warn_ms=1000, raise_ms=None, on_starve=None)`
— configure the pinned-queue starvation watchdog. Both thresholds gate on queue-non-empty time, not raw last-pump time, so programs running only unpinned work never trip them. Default is warn-only; users opt into fail-fast via an explicit raise_ms so interactive debugger sessions are not wedged by a breakpoint.
- `set_wait_pump_poll(ms=50)` — set the poll cadence for
wait's auto-pump loop. Re-read every iteration so a concurrent call updates the active wait immediately.
- `bocpy.PumpResult` — three-field
NamedTuplereturned by
pump. executed counts pinned behaviors whose lifecycle completed (including acquire-failure paths whose MCS chain still drained). deadline_reached is True only when the deadline_ms budget tripped before the queue drained. raised counts only body exceptions captured to a result cown (cleanup-path failures use PyErr_WriteUnraisable and do not count). Exported from bocpy.__all__.
- Coarse-grained pinned-dispatch `examples/boids.py` — the
per-cell send("update") / main-thread receive("update") barrier is replaced by per-cell physics on workers plus one pinned @when per frame that captures every per-cell result cown together with the two main-thread PinnedCown matrices and performs the batched write-back. Same visual output, fully worker-parallel per-cell work, single main-thread touchpoint.
Public C ABI
- `bocpy_main_interpid()` — new
static inlinehelper in
returning PyInterpreterState_GetID( PyInterpreterState_Main()) pre-typed as int_least64_t to match bocpy_interpid for owner-field equality checks. Safe to call from a worker sub-interpreter for diagnostic / assert use. Additive — existing consumers recompile unchanged; BOCPY_ABI is unchanged at 1. The templates/c_abi_consumer bocpy~= pin moves to ~=0.9` to signal the new ABI surface it was authored against.
Improvements
- `@when` loop-variable snapshot via default arg — the
transpiler now accepts def b(c, i=i) as an explicit loop-snapshot idiom in addition to the existing implicit form (just reference the loop variable in the body). Trailing positional parameters beyond the cown count are also auto-captured by name (def b(c, factor) captures factor).
- `@when` alias decorators — the transpiler now recognises
from bocpy import when as boc_when and import bocpy [as alias] followed by @bocpy.when(...) or @alias.when(...), provided the aliasing import is at module level. Previously only the bare @when form was detected.
- `Behaviors.start()` compiles the export module on main —
the transpiler's rewritten module is now also instantiated as an in-memory types.ModuleType on the main thread (plus a linecache entry for traceback fidelity) so pump can resolve __behavior__N the same way workers do via their bootstrap.
- Scheduler-owned behavior pre-header —
bq_nodeand the
new pinned OR-fold byte moved out of the opaque BOCBehavior into a scheduler-owned boc_behavior_prehdr_t allocated immediately before each behavior (CPython _PyGC_Head style). boc_sched.c no longer needs any knowledge of BOCBehavior's internal layout; layout drift between the scheduler and its users is impossible by construction.
- `terminator_wait_pumpable` — new entry in
boc_terminator.{c,h}…
Excerpt shown — open the source for the full document.
Notability
notability 3.0/10Routine library release by Microsoft.