Commit 2784c12
Send the shell reply through the ZMQStream instead of raw on its socket
On Windows, ipykernel 7 intermittently drops an execute_request on the
shell channel: the kernel goes idle and never replies, and the client
times out waiting for execute_reply (historically ~30% of headless
notebook runs in our measurements; which cell hangs wanders run to run).
Root cause: the shell ROUTER socket is dual-use on the shell-channel
thread. A ZMQStream reads execute_requests off it, while replies are sent
back over the SAME socket out-of-band via a raw send_multipart in
SubshellManager._send_on_shell_channel. That out-of-band send drains the
socket's edge-triggered ZMQ_FD read edge (a documented libzmq corollary:
after zmq_send the socket may become readable without a new edge). Because
the send is not ZMQStream-mediated, the stream is never re-armed and a
request that arrived concurrently strands unread on a registered-but-
non-readable fd. The strand is terminal: no later arrival re-edges it.
Fix: send the reply through shell_stream.send_multipart rather than raw on
shell_socket. This keeps the ZMQStream the sole user of the socket: the
send is serviced by the stream's own _handle_events, which recvs any
concurrently-queued request first and then re-arms POLLIN via
_rebuild_io_state, so the request cannot strand. It removes the root cause
rather than re-arming after it, and needs no reach into the private
_handle_events. The shell_stream (built in kernelapp.init_kernel) is
threaded through ShellChannelThread into SubshellManager so the reply path
can reach it, and falls back to the raw socket if no stream was threaded in.
Verified by the regression test added next, which reproduces the strand
precondition deterministically and asserts the queued request is still
delivered once the reply is routed through the stream.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>1 parent 821f6c0 commit 2784c12
3 files changed
Lines changed: 27 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
608 | 608 | | |
609 | 609 | | |
610 | 610 | | |
| 611 | + | |
| 612 | + | |
| 613 | + | |
611 | 614 | | |
612 | 615 | | |
613 | 616 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
29 | 29 | | |
30 | 30 | | |
31 | 31 | | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
32 | 36 | | |
33 | 37 | | |
34 | 38 | | |
| |||
43 | 47 | | |
44 | 48 | | |
45 | 49 | | |
| 50 | + | |
46 | 51 | | |
47 | 52 | | |
48 | 53 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
40 | 40 | | |
41 | 41 | | |
42 | 42 | | |
| 43 | + | |
43 | 44 | | |
44 | 45 | | |
45 | 46 | | |
46 | 47 | | |
47 | 48 | | |
48 | 49 | | |
49 | 50 | | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
50 | 56 | | |
51 | 57 | | |
52 | 58 | | |
| |||
225 | 231 | | |
226 | 232 | | |
227 | 233 | | |
228 | | - | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
229 | 247 | | |
230 | 248 | | |
231 | 249 | | |
| |||
0 commit comments