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.

192 lines
4.9 KiB

1 year ago
1 year ago
  1. local coroutine = require("coroutine")
  2. local computer = require("computer")
  3. local event = require("event")
  4. local ll = require("liblua")
  5. local os = require("os")
  6. local table = require("table")
  7. local async = {}
  8. -- An async wrapper for event.pullFiltered
  9. function async.pullFiltered(...)
  10. local start = computer.uptime()
  11. local args = {...}
  12. local blocking = nil
  13. -- Check if we're in a coroutine or not
  14. if type(args[#args]) == "boolean" and args[#args] then
  15. blocking = true
  16. else
  17. blocking = false
  18. end
  19. --------------------
  20. -- Break on interrupts
  21. function f(name, ...)
  22. -- if args[1] happens to be "interrupted" already the second won't trigger
  23. if name == args[1] or name == "interrupted" then
  24. return true
  25. end
  26. end
  27. --------------------
  28. if type(args[1]) == "string" then
  29. -- Check if not in a coroutine
  30. if blocking then return event.pullFiltered(f, ...) end
  31. repeat
  32. coroutine.yield()
  33. tmp = {event.pullFiltered(.01, f, ...)}
  34. until #tmp > 0
  35. else
  36. local timeout = nil
  37. if type(args[1]) == "number" then
  38. timeout = args[1]
  39. args = ll.slice({...}, 2)
  40. end
  41. -- For speed purposes check up here even though it's more repeated code
  42. if type(args[1]) == "function" then
  43. --------------------
  44. -- Wrap other function so this will handle interrupts
  45. function g(name, ...)
  46. local a = ll.slice(args, 2)
  47. -- Handle interrupt
  48. if name == "interrupted" then
  49. return true
  50. end
  51. -- Otherwise run other function
  52. return args[1](name, ...)
  53. end
  54. --------------------
  55. if timeout ~= nil then
  56. -- Check if not in a coroutine
  57. if blocking then
  58. return event.pullFiltered(timeout, g, table.unpack(args))
  59. end
  60. repeat
  61. coroutine.yield()
  62. tmp = {event.pullFiltered(.01, g, table.unpack(args))}
  63. until #tmp > 0 or computer.uptime() - start >= timeout
  64. else
  65. -- Check if not in a coroutine
  66. if blocking then
  67. return event.pullFiltered(g, ...)
  68. end
  69. repeat
  70. coroutine.yield()
  71. tmp = {event.pullFiltered(.01, g, table.unpack(args))}
  72. until #tmp > 0
  73. end
  74. else
  75. if timeout ~= nil then
  76. -- Check if not in a coroutine
  77. if blocking then
  78. return event.pullFiltered(timeout, f, table.unpack(args))
  79. end
  80. repeat
  81. coroutine.yield()
  82. tmp = {event.pullFiltered(.01, f, table.unpack(args))}
  83. until #tmp > 0 or computer.uptime() - start >= timeout
  84. else
  85. -- Check if not in a coroutine
  86. if blocking then
  87. return event.pullFiltered(f, table.unpack(args))
  88. end
  89. repeat
  90. coroutine.yield()
  91. tmp = {event.pullFiltered(.01, f, table.unpack(args))}
  92. until #tmp > 0
  93. end
  94. end
  95. end
  96. if tmp == nil then
  97. return nil
  98. end
  99. return table.unpack(tmp)
  100. end
  101. -- An alias for async.pullFiltered which does all of the logic for both functions
  102. async.pull = async.pullFiltered
  103. -- Sleep in a coroutine friendly way
  104. function async.sleep(s, blocking)
  105. -- If not a coroutine just regular sleep
  106. if blocking then
  107. os.sleep(s)
  108. return
  109. end
  110. local start = computer.uptime()
  111. repeat
  112. coroutine.yield()
  113. until (computer.uptime() - start > s)
  114. end
  115. -- Simple loop
  116. local loop = {
  117. bundles = {},
  118. live = {},
  119. dead = {},
  120. add = function(self, ...)
  121. local bs = {...}
  122. -- Insert both into bundles and live so no deep copy needs to occur later
  123. for _, v in ipairs(bs) do
  124. table.insert(self.bundles, #self.bundles+1, v)
  125. table.insert(self.live, #self.live+1, v)
  126. end
  127. end
  128. }
  129. -- Used to pack function and args into a nice container for the loop and schedulers
  130. function async.bundle(f, ...)
  131. return {
  132. coro = coroutine.create(f),
  133. args = {...} or {},
  134. results = {}
  135. }
  136. end
  137. -- Return a new loop
  138. function async.getLoop()
  139. return setmetatable({}, {__index=loop})
  140. end
  141. -- Most simple scheduling
  142. function async.roundRobin(loop, ignoreError)
  143. -- Ignore a function throwing an error
  144. if ignoreError == nil then
  145. ignoreError = false
  146. end
  147. local i = 1
  148. repeat
  149. local b = loop.live[i]
  150. local r = {coroutine.resume(b.coro, table.unpack(b.args))}
  151. local status, results = r[1], ll.slice(r, 2)
  152. -- Fail and exit if a function is bad
  153. if not status and not ignoreError then
  154. io.stderr:write(results[1])
  155. return
  156. -- Don't exit but print the bad message
  157. elseif not status then
  158. end
  159. -- Stop running dead coroutines and add the results
  160. if coroutine.status(b.coro) == "dead" then
  161. b.results = results
  162. table.insert(loop.dead, #loop.dead+1, table.remove(loop.live, i))
  163. else
  164. i = (i % (#loop.live)) + 1
  165. end
  166. until #loop.live == 0
  167. return loop
  168. end
  169. return async
  170. -- wget -f http://mc.bashed.rocks:13699/lib/async.lua lib/async.lua