You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Serialize event dispatch in .NET and Go SDKs (#791)
* Serialize event dispatch in .NET and Go SDKs
In .NET, StreamJsonRpc dispatches notifications concurrently on the
thread pool. The old code invoked user event handlers inline from
DispatchEvent, which meant handlers could run concurrently and out of
order.
In Go, the JSON-RPC read loop is single-threaded, so user handlers were
already serialized. However, broadcast handlers (tool calls, permission
requests) ran inline on the read loop, which deadlocked when a handler
issued an RPC request back through the same connection.
This PR decouples user handler dispatch from the transport by routing
events through a channel (Go) / Channel<T> (.NET). A single consumer
goroutine/task drains the channel and invokes user handlers serially, in
FIFO order. This matches the guarantees provided by the Node.js and
Python SDKs (which get natural serialization from their single-threaded
event loops) while fitting Go's and .NET's multi-threaded runtimes.
Broadcast handlers (tool calls, permission requests) are fired as
fire-and-forget directly from the dispatch entry point, outside the
channel, so a stalled handler cannot block event delivery. This matches
the existing Node.js (void this._executeToolAndRespond()) and Python
(asyncio.ensure_future()) behavior.
Go changes:
- Add eventCh channel to Session; start processEvents consumer goroutine
- dispatchEvent enqueues to channel and fires broadcast handler goroutine
- Close channel on Disconnect to stop the consumer
- Update unit tests and E2E tests for async delivery
.NET changes:
- Add unbounded Channel<SessionEvent> to CopilotSession; start
ProcessEventsAsync consumer task in constructor
- DispatchEvent enqueues to channel and fires broadcast handler task
- Complete channel on DisposeAsync
- Per-handler error catching via ImmutableArray iteration
- Cache handler array snapshot to avoid repeated allocation
- Inline broadcast error handling into HandleBroadcastEventAsync
- Update Should_Receive_Session_Events test to await async delivery
- Add Handler_Exception_Does_Not_Halt_Event_Delivery test
- Add DisposeAsync_From_Handler_Does_Not_Deadlock test
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix panic on send to closed event channel in Go SDK
Protect dispatchEvent with a recover guard so that a notification
arriving after Disconnect does not crash the process. Also wrap the
channel close in sync.Once so Disconnect is safe to call more than once.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Steve Sanderson <SteveSandersonMS@users.noreply.github.com>
0 commit comments