OpenComputers Von Neumann Machine Programs
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

166 lines
4.0 KiB

local coroutine = coroutine or require("coroutine")
local computer = computer or require("computer")
local event = event or require("event")
local ll = ll or require("liblua")
local os = os or require("os")
local math = math or require("math")
local queue = queue or require("queue")
local table = table or require("table")
local async = {}
-- TODO fix this so async can be required in more than 1 file
-- Set global interrupted value if it doesn't exist
if type(async.interrupted) ~= "boolean" then
async.interrupted = false
end
-- Register an interrupt handler if it doesn't exist
do
local marker = "async_interrupt"
for i, v in ipairs(event.handlers) do
-- Ignore any previous interrupt handlers
if v.marker == marker then
return
end
end
local interrupted_id = event.listen("interrupted",
function(...)
async.interrupted = true
end
)
-- Add our marker back
event.handlers[interrupted_id].marker = marker
end
-- Allows async listenting to events
async.listen = event.listen
-- Async pull messages. Must be nonblocking only, otherwise use event.pull
function async.pull(...)
local args = {...}
-- Set timeout if it exists
local timeout = math.huge
if type(args[1]) == "number" then
timeout = args[1]
args = ll.slice(args, 2)
end
-- Set name if it exists
local name = ""
if type(args[1]) == "string" then
name = args[1]
end
-- Initialize event queue
local q = queue.new()
local function callback(name, ...)
-- Add event to the queue when called
q:push({name, ...})
end
-- Register a listener to add to our queue
local id = event.listen(name, callback)
local start = computer.uptime()
-- Infinite loop waiting for an event
local waited
repeat
os.sleep(0.01)
coroutine.yield()
waited = computer.uptime() - start
until (#q > 0 or interrupted or waited > timeout)
-- Cancel the event when done
event.cancel(id)
if interrupted then return nil, "interrupted" end
if waited > timeout then return nil, "timeout exceeded" end
if #q > 0 then
return table.unpack(q:get())
end
end
-- Sleep in a coroutine friendly way
function async.sleep(s, blocking)
-- If not a coroutine just regular sleep
if blocking then
os.sleep(s)
return
end
local start = computer.uptime()
repeat
os.sleep(.01)
coroutine.yield()
until (computer.uptime() - start > s)
end
-- Simple loop
local loop = {
bundles = {},
live = {},
dead = {},
add = function(self, ...)
local bs = {...}
-- Insert both into bundles and live so no deep copy needs to occur later
for _, v in ipairs(bs) do
table.insert(self.bundles, #self.bundles+1, v)
table.insert(self.live, #self.live+1, v)
end
end
}
-- Used to pack function and args into a nice container for the loop and schedulers
function async.bundle(f, ...)
return {
coro = coroutine.create(f),
args = {...} or {},
results = {}
}
end
-- Return a new loop
function async.getLoop()
return setmetatable({}, {__index=loop})
end
-- Most simple scheduling
function async.roundRobin(loop, ignoreError)
-- Ignore a function throwing an error
if ignoreError == nil then
ignoreError = false
end
local i = 1
repeat
local b = loop.live[i]
local r = {coroutine.resume(b.coro, table.unpack(b.args))}
local status, results = r[1], ll.slice(r, 2)
-- Fail and exit if a function is bad
if not status and not ignoreError then
io.stderr:write(results[1])
return
-- Don't exit but print the bad message
elseif not status then
end
-- Stop running dead coroutines and add the results
if coroutine.status(b.coro) == "dead" then
b.results = results
table.insert(loop.dead, #loop.dead+1, table.remove(loop.live, i))
-- Set i back to the beginning in case it's off now
if i > #loop.live then i = 1 end
else
i = (i % (#loop.live)) + 1
end
until #loop.live == 0
return loop
end
return async
-- wget -f http://mc.bashed.rocks:13699/lib/async.lua lib/async.lua