From 74d77b1fdf2d6afc9876ddb7e4eef455e254b27f Mon Sep 17 00:00:00 2001 From: payonel Date: Sat, 10 Jun 2017 17:53:30 -0700 Subject: [PATCH] fix: dispatch interrupts to all handlers, pipe coroutine resume status, term.read(), and thread waitForAll() fixes --- .../opencomputers/loot/openos/lib/event.lua | 36 +++++--------- .../opencomputers/loot/openos/lib/pipes.lua | 4 +- .../opencomputers/loot/openos/lib/term.lua | 3 +- .../opencomputers/loot/openos/lib/thread.lua | 48 ++++++++++++++----- 4 files changed, 51 insertions(+), 40 deletions(-) diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/event.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/event.lua index d6f12eeb7..e46040654 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/event.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/event.lua @@ -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") diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/pipes.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/pipes.lua index 6d16b0669..4606b94a9 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/pipes.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/pipes.lua @@ -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 diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/term.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/term.lua index 92ac9fbd7..7a00c291b 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/term.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/term.lua @@ -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() diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/thread.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/thread.lua index d83e1f6c3..8366243f2 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/thread.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/thread.lua @@ -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)