dispatchEvent enqueues an event for delivery to user handlers and fires broadcast handlers concurrently. Broadcast work (tool calls, permission requests) is fired in a separate goroutine so it does not block the JSON-RPC read loop. User event handlers are delivered by a single consumer goroutine (p
(event SessionEvent)
| 1348 | // are delivered by a single consumer goroutine (processEvents), guaranteeing |
| 1349 | // serial, FIFO dispatch without blocking the read loop. |
| 1350 | func (s *Session) dispatchEvent(event SessionEvent) { |
| 1351 | s.updateOpenCanvasesFromEvent(event) |
| 1352 | go s.handleBroadcastEvent(event) |
| 1353 | |
| 1354 | // Send to the event channel in a closure with a recover guard. |
| 1355 | // Disconnect closes eventCh, and in Go sending on a closed channel |
| 1356 | // panics — there is no non-panicking send primitive. We only want |
| 1357 | // to suppress that specific panic; other panics are not expected here. |
| 1358 | func() { |
| 1359 | defer func() { recover() }() |
| 1360 | s.eventCh <- event |
| 1361 | }() |
| 1362 | } |
| 1363 | |
| 1364 | // processEvents is the single consumer goroutine for the event channel. |
| 1365 | // It invokes user handlers serially, in arrival order. Panics in individual |