mirror of
https://github.com/MightyPirates/OpenComputers.git
synced 2025-09-13 01:10:19 -04:00
fix: dispatch interrupts to all handlers, pipe coroutine resume status, term.read(), and thread waitForAll() fixes
This commit is contained in:
parent
067f217813
commit
74d77b1fdf
@ -40,20 +40,28 @@ end
|
||||
local _pullSignal = computer.pullSignal
|
||||
setmetatable(handlers, {__call=function(_,...)return _pullSignal(...)end})
|
||||
computer.pullSignal = function(...) -- dispatch
|
||||
local current_time = computer.uptime()
|
||||
local interrupting = current_time - lastInterrupt > 1 and keyboard.isControlDown() and keyboard.isKeyDown(keyboard.keys.c)
|
||||
if interrupting then
|
||||
lastInterrupt = current_time
|
||||
if keyboard.isAltDown() then
|
||||
error("interrupted", 0)
|
||||
end
|
||||
event.push("interrupted", current_time)
|
||||
end
|
||||
local event_data = table.pack(handlers(...))
|
||||
local signal = event_data[1]
|
||||
local ids = {}
|
||||
for id in pairs(handlers) do
|
||||
ids[#ids+1] = id
|
||||
end
|
||||
local time = computer.uptime()
|
||||
for _,id in ipairs(ids) do
|
||||
local handler = handlers[id]
|
||||
-- timers have false keys
|
||||
-- nil keys match anything
|
||||
if (handler.key == nil or handler.key == signal) or time >= handler.timeout then
|
||||
if (handler.key == nil or handler.key == signal) or current_time >= handler.timeout then
|
||||
handler.times = handler.times - 1
|
||||
handler.timeout = computer.uptime() + handler.interval
|
||||
handler.timeout = current_time + handler.interval
|
||||
if handler.times <= 0 then
|
||||
handlers[id] = nil
|
||||
end
|
||||
@ -196,15 +204,6 @@ function event.pullFiltered(...)
|
||||
repeat
|
||||
local closest = math.min(deadline, time_to_nearest())
|
||||
local signal = table.pack(computer.pullSignal(closest - computer.uptime()))
|
||||
if event.shouldInterrupt() then
|
||||
lastInterrupt = computer.uptime()
|
||||
error("interrupted", 0)
|
||||
end
|
||||
if event.shouldSoftInterrupt() and (filter == nil or filter("interrupted", computer.uptime() - lastInterrupt)) then
|
||||
local awaited = computer.uptime() - lastInterrupt
|
||||
lastInterrupt = computer.uptime()
|
||||
return "interrupted", awaited
|
||||
end
|
||||
if signal.n > 0 then
|
||||
if not (seconds or filter) or filter == nil or filter(table.unpack(signal, 1, signal.n)) then
|
||||
return table.unpack(signal, 1, signal.n)
|
||||
@ -213,19 +212,6 @@ function event.pullFiltered(...)
|
||||
until computer.uptime() >= deadline
|
||||
end
|
||||
|
||||
function event.shouldInterrupt()
|
||||
return computer.uptime() - lastInterrupt > 1 and
|
||||
keyboard.isControlDown() and
|
||||
keyboard.isAltDown() and
|
||||
keyboard.isKeyDown(keyboard.keys.c)
|
||||
end
|
||||
|
||||
function event.shouldSoftInterrupt()
|
||||
return computer.uptime() - lastInterrupt > 1 and
|
||||
keyboard.isControlDown() and
|
||||
keyboard.isKeyDown(keyboard.keys.c)
|
||||
end
|
||||
|
||||
function event.timer(interval, callback, times)
|
||||
checkArg(1, interval, "number")
|
||||
checkArg(2, callback, "function")
|
||||
|
@ -184,7 +184,8 @@ function pipes.createCoroutineStack(fp, init, name)
|
||||
if current_index then
|
||||
-- current should be waiting for yield
|
||||
pco.next = thread
|
||||
return true, _co.yield(...) -- pass args to resume next
|
||||
local t = table.pack(_co.yield(...)) -- pass args to resume next
|
||||
return pco.last == nil and true or pco.last, table.unpack(t,1,t.n)
|
||||
else
|
||||
-- the stack is not running
|
||||
pco.next = false
|
||||
@ -202,6 +203,7 @@ function pipes.createCoroutineStack(fp, init, name)
|
||||
if pco.next and pco.next ~= thread then
|
||||
local next = pco.next
|
||||
pco.next = false
|
||||
pco.last = yield_args[1]
|
||||
return pco.resume(next, table.unpack(yield_args,2,yield_args.n))
|
||||
end
|
||||
end
|
||||
|
@ -183,7 +183,8 @@ function term.read(history, dobreak, hint, pwchar, filter)
|
||||
if not io.stdin.tty then
|
||||
return io.read()
|
||||
end
|
||||
local handler = history or {}
|
||||
history = history or {}
|
||||
local handler = history
|
||||
handler.hint = handler.hint or hint
|
||||
|
||||
local cursor = tty.internal.build_vertical_reader()
|
||||
|
@ -5,39 +5,56 @@ local computer = require("computer")
|
||||
|
||||
local thread = {}
|
||||
|
||||
local function waitForNumber(threads, n, timeout)
|
||||
local function waitForDeath(threads, timeout, all)
|
||||
checkArg(1, threads, "table")
|
||||
checkArg(2, n, "number")
|
||||
checkArg(3, timeout, "number", "nil")
|
||||
checkArg(2, timeout, "number", "nil")
|
||||
checkArg(3, all, "boolean")
|
||||
timeout = timeout or math.huge
|
||||
local mortician = {}
|
||||
local timed_out = true
|
||||
local deadline = computer.uptime() + timeout
|
||||
while deadline > computer.uptime() do
|
||||
local num_dead = 0
|
||||
local dieing = {}
|
||||
local living = {}
|
||||
for _,t in ipairs(threads) do
|
||||
local result = t.process and t.process.data.result
|
||||
local proc_ok = type(result) ~= "table" or result[1]
|
||||
if t:status() ~= "running" -- suspended is considered dead to exit
|
||||
or not proc_ok then -- the thread is killed if its attached process has a non zero exit
|
||||
t:kill()
|
||||
num_dead = num_dead + 1
|
||||
local ready_to_die = t:status() ~= "running" -- suspended is considered dead to exit
|
||||
or not proc_ok -- the thread is killed if its attached process has a non zero exit
|
||||
if ready_to_die then
|
||||
dieing[#dieing + 1] = t
|
||||
mortician[t] = true
|
||||
else
|
||||
living[#living + 1] = t
|
||||
end
|
||||
end
|
||||
if num_dead >= n then
|
||||
return true
|
||||
|
||||
if all and #living == 0 or not all and #dieing > 0 then
|
||||
timed_out = false
|
||||
break
|
||||
end
|
||||
|
||||
-- resume each non dead thread
|
||||
-- we KNOW all threads are event.pull blocked
|
||||
event.pull()
|
||||
end
|
||||
return nil, "thread join timed out"
|
||||
|
||||
for t in pairs(mortician) do
|
||||
t:kill()
|
||||
end
|
||||
|
||||
if timed_out then
|
||||
return nil, "thread join timed out"
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function thread.waitForAny(threads, timeout)
|
||||
return waitForNumber(threads, 1, timeout)
|
||||
return waitForDeath(threads, timeout, false)
|
||||
end
|
||||
|
||||
function thread.waitForAll(threads, timeout)
|
||||
return waitForNumber(threads, #threads, timeout)
|
||||
return waitForDeath(threads, timeout, true)
|
||||
end
|
||||
|
||||
local box_thread = {}
|
||||
@ -137,11 +154,16 @@ function thread.create(fp, ...)
|
||||
while true do
|
||||
local result = table.pack(t.pco.resume(fp_co, table.unpack(args, 1, args.n)))
|
||||
if t.pco.status(fp_co) == "dead" then
|
||||
if not result[1] then
|
||||
event.onError(string.format("thread crashed: %s", tostring(result[2])))
|
||||
end
|
||||
break
|
||||
end
|
||||
args = table.pack(event.pull(table.unpack(result, 2, result.n)))
|
||||
end
|
||||
mt.__status = "dead"
|
||||
event.push("thread_exit")
|
||||
t:detach()
|
||||
end)
|
||||
local handlers = event.handlers
|
||||
local handlers_mt = getmetatable(handlers)
|
||||
|
Loading…
x
Reference in New Issue
Block a user