From 9767ab35ec1a5495f0f9a46ef2c483c76b93e32d Mon Sep 17 00:00:00 2001 From: JamesWrigley Date: Fri, 17 May 2024 14:05:14 +0200 Subject: [PATCH] Make Socket hold a reference to its Context This prevents the `Context` from being garbage collected while the `Socket` is still alive, otherwise the `Socket` would be closed by the `Context`'s finalizer while the `Socket` is potentially still in use. --- src/socket.jl | 3 ++- test/runtests.jl | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/socket.jl b/src/socket.jl index 4680b16..3501761 100644 --- a/src/socket.jl +++ b/src/socket.jl @@ -19,6 +19,7 @@ Do-block constructor. """ mutable struct Socket data::Ptr{Cvoid} + context::Context pollfd::FDWatcher """ @@ -31,7 +32,7 @@ mutable struct Socket if p == C_NULL throw(StateError(jl_zmq_error_str())) end - socket = new(p) + socket = new(p, ctx) setfield!(socket, :pollfd, FDWatcher(fd(socket), #=readable=#true, #=writable=#false)) finalizer(close, socket) push!(getfield(ctx, :sockets), WeakRef(socket)) diff --git a/test/runtests.jl b/test/runtests.jl index f07d174..d7274f3 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -13,7 +13,23 @@ using ZMQ, Test @test_throws StateError Socket(ctx, PUB) end +# This test is in its own function to keep it simple and try to trick Julia into +# thinking it can safely GC the Context. +function context_gc_test() + ctx = Context() + s = Socket(ctx, PUB) + + # Force garbage collection to attempt to delete ctx + GC.gc() + + # But it shouldn't be garbage collected since the socket should have a + # reference to it, so the socket should still be open. + @test isopen(s) +end + @testset "ZMQ sockets" begin + context_gc_test() + s=Socket(PUB) @test s isa Socket ZMQ.close(s)