mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-02-09 23:50:04 +00:00
Cancel no-longer-needed timers
Several functions accept a "timeout" argument, which is implemented by starting a timer, and then racing the desired output against the timer event. However, if the timer never wins, we weren't cancelling the timer, and so it was still queued. This is especially problematic if dozens or hundreds of rednet (or websocket) messages are received in quick succession, as we could fill the entire event queue, and stall the computer. See #1995
This commit is contained in:
parent
3293639adf
commit
b742745854
@ -62,7 +62,7 @@ public class WebsocketHandle {
|
||||
? 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 @@ public class WebsocketHandle {
|
||||
|
||||
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) {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user