Skip to content

Commit 68e3717

Browse files
committed
Safely stop the heartbeat thread
Previously we were not ensuring that the heartbeat thread had stopped before exiting, and were closing the context in an unsafe way. This was causing hangs on Mac and segfaults on Windows.
1 parent 11a3438 commit 68e3717

File tree

3 files changed

+23
-5
lines changed

3 files changed

+23
-5
lines changed

src/IJulia.jl

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -175,11 +175,13 @@ function Base.close(kernel::Kernel)
175175
popdisplay()
176176

177177
# Close all sockets
178-
close(kernel.publish[])
179-
close(kernel.raw_input[])
180178
close(kernel.requests[])
181179
close(kernel.control[])
182-
close(kernel.heartbeat_context[])
180+
close(kernel.publish[])
181+
close(kernel.raw_input[])
182+
183+
# Wait for the heartbeat thread
184+
stop_heartbeat(kernel)
183185

184186
# The waitloop should now be ready to exit
185187
kernel.inited = false

src/handlers.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,8 +214,8 @@ request](https://jupyter-client.readthedocs.io/en/latest/messaging.html#kernel-s
214214
sending the reply this will exit the process.
215215
"""
216216
function shutdown_request(socket, kernel, msg)
217-
# stop heartbeat thread by closing the context
218-
close(kernel.heartbeat_context[])
217+
# stop heartbeat thread
218+
stop_heartbeat(kernel)
219219

220220
# In protocol 5.4 the shutdown reply moved to the control socket
221221
shutdown_socket = VersionNumber(msg) >= v"5.4" ? kernel.control[] : kernel.requests[]

src/heartbeat.jl

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,23 @@ function heartbeat_thread(heartbeat::Ptr{Cvoid})
2323
end
2424

2525
function start_heartbeat(heartbeat, kernel)
26+
heartbeat.linger = 0
2627
heartbeat_c = @cfunction(heartbeat_thread, Cint, (Ptr{Cvoid},))
2728
ccall(:uv_thread_create, Cint, (Ptr{Int}, Ptr{Cvoid}, Ptr{Cvoid}),
2829
kernel.heartbeat_threadid, heartbeat_c, heartbeat)
2930
end
31+
32+
function stop_heartbeat(kernel)
33+
# First we call zmq_ctx_shutdown() to ensure that the zmq_proxy() call
34+
# returns. We don't call ZMQ.close(::Context) directly because that
35+
# currently isn't threadsafe:
36+
# https://github.com/JuliaInterop/ZMQ.jl/issues/256
37+
ZMQ.lib.zmq_ctx_shutdown(kernel.heartbeat_context[])
38+
@ccall uv_thread_join(kernel.heartbeat_threadid::Ptr{Int})::Cint
39+
40+
# Now that the heartbeat thread has joined and its guaranteed to no longer
41+
# be working on the heartbeat socket, we can safely close it and then the
42+
# context.
43+
close(kernel.heartbeat[])
44+
close(kernel.heartbeat_context[])
45+
end

0 commit comments

Comments
 (0)