From f278afd5bf507e8430e2f8a14ad1551f0ac060f4 Mon Sep 17 00:00:00 2001 From: James Wrigley Date: Mon, 20 May 2024 20:31:53 +0200 Subject: [PATCH] Make Socket hold a reference to its Context (#229) 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 ee884b2..58a9db5 100644 --- a/src/socket.jl +++ b/src/socket.jl @@ -3,6 +3,7 @@ A ZMQ socket. """ mutable struct Socket data::Ptr{Cvoid} + context::Context pollfd::FDWatcher @doc """ @@ -15,7 +16,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 63c2dac..cc79f7d 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -14,7 +14,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)