-- -- asyncio synchronisation primitives. -- -- -- Locks -- -- Store lock metadata locally local lock_states = {} local lock_callbacks = {} setmetatable(lock_states, {__mode = 'kv'}) setmetatable(lock_callbacks, {__mode = 'k'}) -- 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