2019-07-28 00:53:17 +02:00
|
|
|
--
|
|
|
|
-- asyncio synchronisation primitives.
|
|
|
|
--
|
|
|
|
|
|
|
|
--
|
|
|
|
-- Locks
|
|
|
|
--
|
|
|
|
|
|
|
|
-- Store lock metadata locally
|
|
|
|
local lock_states = {}
|
|
|
|
local lock_callbacks = {}
|
|
|
|
|
2019-08-24 11:10:10 +02:00
|
|
|
setmetatable(lock_states, {__mode = 'kv'})
|
|
|
|
setmetatable(lock_callbacks, {__mode = 'k'})
|
2019-07-28 00:53:17 +02:00
|
|
|
|
|
|
|
-- Create the functions
|
|
|
|
-- https://docs.python.org/3/library/asyncio-sync.html#lock
|
|
|
|
local lock = {}
|
|
|
|
function lock:acquire()
|
|
|
|
if not asyncio.running then
|
|
|
|
error('lock:acquire() called outside of a coroutine.')
|
|
|
|
elseif not lock_callbacks[self] then
|
|
|
|
lock_callbacks[self] = {}
|
|
|
|
end
|
|
|
|
|
|
|
|
if lock_states[self] then
|
|
|
|
asyncio.await(table.insert(lock_callbacks[self], asyncio.resume))
|
|
|
|
elseif #lock_callbacks[self] > 0 then
|
|
|
|
table.remove(lock_callbacks[self], 1)()
|
|
|
|
asyncio.await(table.insert(lock_callbacks[self], asyncio.resume))
|
|
|
|
end
|
|
|
|
lock_states[self] = coroutine.running()
|
|
|
|
end
|
|
|
|
|
|
|
|
function lock:release()
|
|
|
|
if not lock_states[self] then error('Lock is not acquired.', 2) end
|
|
|
|
lock_states[self] = nil
|
|
|
|
|
|
|
|
-- lock_callbacks[self] should never be nil.
|
|
|
|
assert(lock_callbacks[self])
|
|
|
|
|
|
|
|
-- Call the next function.
|
|
|
|
local func = table.remove(lock_callbacks[self], 1)
|
|
|
|
if func then func() end
|
|
|
|
end
|
|
|
|
|
|
|
|
function lock:locked()
|
|
|
|
return lock_states[self] and true or false
|
|
|
|
end
|
|
|
|
|
|
|
|
function asyncio.Lock()
|
|
|
|
return {
|
|
|
|
acquire = lock.acquire,
|
|
|
|
release = lock.release,
|
|
|
|
locked = lock.locked,
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
--
|
|
|
|
-- Events
|
|
|
|
--
|
|
|
|
|
|
|
|
local event_callbacks = {}
|
|
|
|
setmetatable(event_callbacks, {__mode = 'k'})
|
|
|
|
|
|
|
|
local event = {}
|
|
|
|
|
|
|
|
function event:wait()
|
|
|
|
if not asyncio.running then
|
|
|
|
error('event:wait() called outside of a coroutine.')
|
|
|
|
elseif event_callbacks[self] then
|
|
|
|
asyncio.await(table.insert(event_callbacks[self], asyncio.resume))
|
|
|
|
end
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Automatically defer execution when running event:set()
|
|
|
|
async function event:set()
|
|
|
|
local funcs = event_callbacks[self]
|
|
|
|
event_callbacks[self] = nil
|
|
|
|
if not funcs then return end
|
|
|
|
|
|
|
|
async for _, func in asyncio.ipairs(funcs) do
|
|
|
|
-- If event:set() is re-called, re-add any unhandled coroutines.
|
|
|
|
if event_callbacks[self] then
|
|
|
|
table.insert(event_callbacks[self], 1, func)
|
|
|
|
else
|
|
|
|
func()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function event:clear()
|
|
|
|
if not event_callbacks[self] then event_callbacks[self] = {} end
|
|
|
|
end
|
|
|
|
|
|
|
|
function event:is_set()
|
|
|
|
return not event_callbacks[self]
|
|
|
|
end
|
|
|
|
|
|
|
|
function asyncio.Event()
|
|
|
|
local res = {
|
|
|
|
wait = event.wait,
|
|
|
|
set = event.set,
|
|
|
|
clear = event.clear,
|
|
|
|
is_set = event.is_set,
|
|
|
|
}
|
|
|
|
event_callbacks[res] = {}
|
|
|
|
return res
|
|
|
|
end
|