diff --git a/projects/core/src/main/java/dan200/computercraft/core/apis/http/websocket/WebsocketHandle.java b/projects/core/src/main/java/dan200/computercraft/core/apis/http/websocket/WebsocketHandle.java index eafd04df02..11c4da7fe8 100644 --- a/projects/core/src/main/java/dan200/computercraft/core/apis/http/websocket/WebsocketHandle.java +++ b/projects/core/src/main/java/dan200/computercraft/core/apis/http/websocket/WebsocketHandle.java @@ -62,7 +62,7 @@ public final MethodResult receive(Optional timeout) throws LuaException ? environment.startTimer(Math.round(checkFinite(0, timeout.get()) / 0.05)) : -1; - return new ReceiveCallback(timeoutId).pull; + return new ReceiveCallback(environment, timeoutId).pull; } /** @@ -110,18 +110,22 @@ private void checkOpen() throws LuaException { private final class ReceiveCallback implements ILuaCallback { final MethodResult pull = MethodResult.pullEvent(null, this); + private final IAPIEnvironment environment; private final int timeoutId; - ReceiveCallback(int timeoutId) { + ReceiveCallback(IAPIEnvironment environment, int timeoutId) { this.timeoutId = timeoutId; + this.environment = environment; } @Override public MethodResult resume(Object[] event) { if (event.length >= 3 && Objects.equals(event[0], MESSAGE_EVENT) && Objects.equals(event[1], address)) { + environment.cancelTimer(timeoutId); return MethodResult.of(Arrays.copyOfRange(event, 2, event.length)); } else if (event.length >= 2 && Objects.equals(event[0], CLOSE_EVENT) && Objects.equals(event[1], address) && websocket.isClosed()) { // If the socket is closed abort. + environment.cancelTimer(timeoutId); return MethodResult.of(); } else if (event.length >= 2 && timeoutId != -1 && Objects.equals(event[0], TIMER_EVENT) && event[1] instanceof Number id && id.intValue() == timeoutId) { diff --git a/projects/core/src/main/resources/data/computercraft/lua/rom/apis/gps.lua b/projects/core/src/main/resources/data/computercraft/lua/rom/apis/gps.lua index a884af2536..e9c33b833c 100644 --- a/projects/core/src/main/resources/data/computercraft/lua/rom/apis/gps.lua +++ b/projects/core/src/main/resources/data/computercraft/lua/rom/apis/gps.lua @@ -196,6 +196,8 @@ function locate(_nTimeout, _bDebug) modem.close(CHANNEL_GPS) end + os.cancelTimer(timeout) + -- Return the response if pos1 and pos2 then if _bDebug then diff --git a/projects/core/src/main/resources/data/computercraft/lua/rom/apis/rednet.lua b/projects/core/src/main/resources/data/computercraft/lua/rom/apis/rednet.lua index 097086a516..969e6082cb 100644 --- a/projects/core/src/main/resources/data/computercraft/lua/rom/apis/rednet.lua +++ b/projects/core/src/main/resources/data/computercraft/lua/rom/apis/rednet.lua @@ -298,6 +298,7 @@ function receive(protocol_filter, timeout) -- Return the first matching rednet_message local sender_id, message, protocol = p1, p2, p3 if protocol_filter == nil or protocol == protocol_filter then + if timer then os.cancelTimer(timer) end return sender_id, message, protocol end elseif event == "timer" then @@ -431,6 +432,7 @@ function lookup(protocol, hostname) if hostname == nil then table.insert(results, sender_id) elseif message.sHostname == hostname then + os.cancelTimer(timer) return sender_id end end @@ -440,6 +442,9 @@ function lookup(protocol, hostname) break end end + + os.cancelTimer(timer) + if results then return table.unpack(results) end diff --git a/projects/core/src/test/resources/test-rom/spec/apis/rednet_spec.lua b/projects/core/src/test/resources/test-rom/spec/apis/rednet_spec.lua index d808f13955..1554680031 100644 --- a/projects/core/src/test/resources/test-rom/spec/apis/rednet_spec.lua +++ b/projects/core/src/test/resources/test-rom/spec/apis/rednet_spec.lua @@ -171,6 +171,36 @@ describe("The rednet library", function() fake_computer.run_all { computer_1, computer_2 } end) + describe("timeouts", function() + it("can time out messages", function() + local computer = computer_with_rednet(1, function(rednet) + local id = rednet.receive(1) + expect(id):eq(nil) + end, { open = true }) + + local computers = { computer } + fake_computer.run_all(computers, false) + fake_computer.advance_all(computers, 1) + fake_computer.run_all(computers) + end) + + it("cancels the pending timer", function() + local computer = computer_with_rednet(1, function(rednet) + -- Send a message to ourselves with a timer + rednet.send(1, "hello") + local id = rednet.receive(1) + expect(id):eq(1) + end, { open = true }) + + fake_computer.run_all({ computer }) + + -- Our pending timer list only contains the rednet.run timer. + expect(computer.pending_timers):same { + [debugx.getupvalue(computer.env.rednet.run, "prune_received_timer")] = 10, + } + end) + end) + it("repeats messages between computers", function() local computer_1, modem_1 = computer_with_rednet(1, function(rednet) rednet.send(3, "Hello") diff --git a/projects/core/src/test/resources/test-rom/spec/support/fake_computer.lua b/projects/core/src/test/resources/test-rom/spec/support/fake_computer.lua index a07cc73420..e6d3a7d3e7 100644 --- a/projects/core/src/test/resources/test-rom/spec/support/fake_computer.lua +++ b/projects/core/src/test/resources/test-rom/spec/support/fake_computer.lua @@ -50,6 +50,7 @@ local function make_computer(id, fn) pending_timers[t], next_timer = clock + delay, next_timer + 1 return t end, + cancelTimer = function(id) pending_timers[id] = nil end, clock = function() return clock end, sleep = function(time) local timer = env.os.startTimer(time or 0) @@ -98,7 +99,16 @@ local function make_computer(id, fn) local position = vector.new(0, 0, 0) - return { env = env, peripherals = peripherals, queue_event = queue_event, step = step, co = co, advance = advance, position = position } + return { + env = env, + peripherals = peripherals, + queue_event = queue_event, + step = step, + co = co, + advance = advance, + position = position, + pending_timers = pending_timers, + } end local function parse_channel(c)