docs(code-runtime-python): correct the ProtocolChannel serialization docstring
The class docstring still credited the GIL plus per-frame PIPE_BUF atomicity for serializing writes, which _write_lock's full-write loop already superseded. State the current contract (writers serialized by _write_lock around a full-write loop) and drop the double blank line under the binding-replies note heading.
This commit is contained in:
parent
a8e47dae37
commit
9f449a79a6
3 changed files with 7 additions and 5 deletions
|
|
@ -2,5 +2,5 @@
|
|||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/implemented/bug-fix/2026-07-31-code-runtime-python-settlement-fixes.md
|
||||
2026-07-31-code-runtime-python-settlement-fixes.md: d276c04e0182dc589f48a77d8dd73b233ae11d9d
|
||||
2026-07-31-code-runtime-python-settlement-fixes.md: b51fb2e9c28d07efa1691b1036b7b6e899b21062
|
||||
2026-07-31-code-runtime-python-settlement-fixes.zh.md: a59e5457f2469e86ed4120ce47ed8a5d8ae08f4f
|
||||
|
|
|
|||
|
|
@ -40,7 +40,6 @@ In [`py/bootstrap.py`](../../../../packages/code-runtime/code-runtime-python/py/
|
|||
|
||||
### Binding replies complete on the calling loop's thread
|
||||
|
||||
|
||||
Also in `py/bootstrap.py`, a binding reply Future is created on the loop that ran `dispatch`. When the model calls a binding from a worker THREAD via `asyncio.run(tools.x(...))`, that Future belongs to the thread's loop, not the main loop where `_pump_replies` reads the reply. `asyncio.Future` is not thread-safe: completing it from another thread does not wake its own loop, so the direct `set_result`/`set_exception` left the awaiting thread stranded and the run degraded to a wall-clock timeout. Each pending entry now records its Future's loop alongside the Future, and `_pump_replies` completes it via that loop's `call_soon_threadsafe`. The shared `pending`/`next_id` state is guarded by a `threading.Lock` held across the id claim, the fd-3 write, and the counter advance, so concurrent callers cannot interleave frames out of the id order the host requires. `call_soon_threadsafe` onto a loop that has already CLOSED (the worker thread finished and abandoned its call before the reply arrived) raises `RuntimeError`; that schedule is wrapped so the moot reply is dropped rather than letting the exception end the pump task and strand every later reply.
|
||||
|
||||
## Testing
|
||||
|
|
|
|||
|
|
@ -345,9 +345,12 @@ class ProtocolChannel:
|
|||
|
||||
Writes are unbuffered and go straight to the fd, so ``send_sync`` is safe
|
||||
from inside model code (which may run outside an asyncio task) and from
|
||||
background tasks alike. The single writer is serialized by CPython's GIL
|
||||
plus one os.write per frame (POSIX guarantees atomicity for writes below
|
||||
``PIPE_BUF``, and our frames are short JSON lines).
|
||||
background tasks alike. Concurrent writers are serialized by ``_write_lock``
|
||||
around a full-write loop (see ``send_sync``): ``os.write`` releases the GIL,
|
||||
a frame may exceed ``PIPE_BUF`` (logs up to ``maxLogBytes``, completions up
|
||||
to ``maxValueBytes``, uncapped ``call`` args), and one ``os.write`` may
|
||||
consume only part of a frame — so neither the GIL nor per-frame atomicity is
|
||||
relied on for framing.
|
||||
"""
|
||||
|
||||
def __init__(self, fd: int) -> None:
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue