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.

locks.lua 2.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. --
  2. -- asyncio synchronisation primitives.
  3. --
  4. --
  5. -- Locks
  6. --
  7. -- Store lock metadata locally
  8. local lock_states = {}
  9. local lock_callbacks = {}
  10. setmetatable(lock_states, {__mode = 'kv'})
  11. setmetatable(lock_callbacks, {__mode = 'k'})
  12. -- Create the functions
  13. -- https://docs.python.org/3/library/asyncio-sync.html#lock
  14. local lock = {}
  15. function lock:acquire()
  16. if not asyncio.running then
  17. error('lock:acquire() called outside of a coroutine.')
  18. elseif not lock_callbacks[self] then
  19. lock_callbacks[self] = {}
  20. end
  21. if lock_states[self] then
  22. asyncio.await(table.insert(lock_callbacks[self], asyncio.resume))
  23. elseif #lock_callbacks[self] > 0 then
  24. table.remove(lock_callbacks[self], 1)()
  25. asyncio.await(table.insert(lock_callbacks[self], asyncio.resume))
  26. end
  27. lock_states[self] = coroutine.running()
  28. end
  29. function lock:release()
  30. if not lock_states[self] then error('Lock is not acquired.', 2) end
  31. lock_states[self] = nil
  32. -- lock_callbacks[self] should never be nil.
  33. assert(lock_callbacks[self])
  34. -- Call the next function.
  35. local func = table.remove(lock_callbacks[self], 1)
  36. if func then func() end
  37. end
  38. function lock:locked()
  39. return lock_states[self] and true or false
  40. end
  41. function asyncio.Lock()
  42. return {
  43. acquire = lock.acquire,
  44. release = lock.release,
  45. locked = lock.locked,
  46. }
  47. end
  48. --
  49. -- Events
  50. --
  51. local event_callbacks = {}
  52. setmetatable(event_callbacks, {__mode = 'k'})
  53. local event = {}
  54. function event:wait()
  55. if not asyncio.running then
  56. error('event:wait() called outside of a coroutine.')
  57. elseif event_callbacks[self] then
  58. asyncio.await(table.insert(event_callbacks[self], asyncio.resume))
  59. end
  60. return true
  61. end
  62. -- Automatically defer execution when running event:set()
  63. async function event:set()
  64. local funcs = event_callbacks[self]
  65. event_callbacks[self] = nil
  66. if not funcs then return end
  67. async for _, func in asyncio.ipairs(funcs) do
  68. -- If event:set() is re-called, re-add any unhandled coroutines.
  69. if event_callbacks[self] then
  70. table.insert(event_callbacks[self], 1, func)
  71. else
  72. func()
  73. end
  74. end
  75. end
  76. function event:clear()
  77. if not event_callbacks[self] then event_callbacks[self] = {} end
  78. end
  79. function event:is_set()
  80. return not event_callbacks[self]
  81. end
  82. function asyncio.Event()
  83. local res = {
  84. wait = event.wait,
  85. set = event.set,
  86. clear = event.clear,
  87. is_set = event.is_set,
  88. }
  89. event_callbacks[res] = {}
  90. return res
  91. end