diff --git a/bc.lua b/bc.lua index 98716d6119a0..109a1798015f 100644 --- a/bc.lua +++ b/bc.lua @@ -116,9 +116,20 @@ function BaseControl:new(network) } setmetatable(self, {__index=BaseControl, __call=BaseControl.new}) - self.network:start(function(remote, msg) - self:_network_handler(remote, msg) - end) + -- Create a weak reference to self. This allows us to uninstall the + -- network handler automatically once this instance gets garbage-collected. + do + local weak = setmetatable({self=self}, {__mode="v"}) + local network = self.network + + self.network:start(function(remote, msg) + if weak.self ~= nil then + weak.self:_network_handler(remote, msg) + else + network:stop() + end + end) + end self.network:broadcast{ty=Message.Hello} return self diff --git a/network.lua b/network.lua index 7f927a9775a2..5535df1c1cf7 100644 --- a/network.lua +++ b/network.lua @@ -21,6 +21,10 @@ function network.deregister(addr) nodes[addr] = nil end +function network.get_callback(addr) + return nodes[addr] +end + function network.send(addr, port, msg) local callback = nodes[addr] if callback == nil then diff --git a/tests/cleanup.lua b/tests/cleanup.lua index bb5ac9a18577..f3f2f56308d7 100644 --- a/tests/cleanup.lua +++ b/tests/cleanup.lua @@ -11,11 +11,14 @@ function test_clean_simple() bc:close() local bc = BaseControl:finalize() + assert_true(network.get_callback(addr) ~= nil, "callback not registered") bc:close() assert_error(function() bc:nouns() end) + + assert_nil(network.get_callback(addr), "callback still in network") end function test_clean_network() @@ -66,3 +69,25 @@ function test_clean_network2() bc2:get("cln3") end) end + +function test_unclean_deregister() + local bc = BaseControl:new() + local addr = network.get_scene() + + network.set_scene("UNCL-DEREG") + network.send( + addr, + BaseControl.Network.default_port, + serialization.serialize{ty=BaseControl.Message.Hello} + ) + + assert_true(network.get_callback(addr) ~= nil, "callback not registered") + bc = nil + collectgarbage() + network.send( + addr, + BaseControl.Network.default_port, + serialization.serialize{ty=BaseControl.Message.Hello} + ) + assert_nil(network.get_callback(addr), "callback still in network") +end