Advertisement
szymski

Untitled

Jun 17th, 2017
146
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 5.50 KB | None | 0 0
  1. --@name Task Library
  2. --@author Szymekk
  3.  
  4. --------------------------------------
  5. -- Task class
  6. --------------------------------------
  7.  
  8. local task = { }
  9. task.__index = task
  10.  
  11. function task:init(func)
  12. self._func = func
  13.  
  14. self._running = false -- The task has been run once and haven't finished
  15. self._finished = false -- The has returned - self.result should not be nil
  16. self.result = nil -- Values retured from the task as a table
  17.  
  18. self._parent = nil -- Waiting parent of the task, should be awoken after the current task finishes
  19.  
  20. self._async = nil -- Async object of the task, when true - parent should be awoken
  21.  
  22. self._coroutine = coroutine.create(function(...)
  23. local result = { func(...) }
  24.  
  25. if self._async then
  26. result = { coroutine.yield() } -- Wait for async:release()
  27. self._async = nil
  28.  
  29. self.result = result
  30. self._running = false
  31. self._finished = true
  32.  
  33. -- Resume the parent
  34. if self._parent then
  35. local parent = self._parent
  36.  
  37. self._async = nil
  38. self._parent = nil
  39.  
  40. local prevTask = tasks.current
  41.  
  42. tasks.current = parent
  43. coroutine.resume(parent._coroutine)
  44. tasks.current = prevTask
  45. end
  46. end
  47.  
  48. return unpack(returned)
  49. end)
  50. end
  51.  
  52. --- Runs or resumes the task
  53. function task:run(...)
  54. -- Waiting for an asynchronic task - it's resumed somewhere else
  55. if self._waiting then
  56. return
  57. end
  58.  
  59. if self._async then
  60. return
  61. end
  62.  
  63. if self._finished then
  64. error("Cannot run finished task")
  65. end
  66.  
  67. -- The task is running for the first time
  68. if not self._running then
  69. self._running = true
  70. end
  71.  
  72. local prevTask = tasks.current
  73.  
  74. tasks.current = self
  75. local result
  76. local args = { ... }
  77. xpcall(function() -- Temporary workaround
  78. result = { coroutine.resume(self._coroutine, unpack(args)) }
  79. end, function(err)
  80. -- TODO: Check if the error is the weird one
  81. error(err)
  82. end)
  83. tasks.current = prevTask
  84.  
  85. -- The task could become async
  86. if self._async and self._async ~= true then
  87. return
  88. end
  89.  
  90. -- When task finished the work
  91. if coroutine.status(self._coroutine) == "dead" then
  92. self.result = result
  93. self._running = false
  94. self._finished = true
  95. end
  96. end
  97.  
  98. task.resume = task.run
  99.  
  100. --- Makes the current task pause until this task finishes
  101. function task:wait(...)
  102. local parent = tasks.current
  103. self._parent = parent
  104.  
  105. self:run(...)
  106.  
  107. -- The task haven't finished yet - it's probably asynchronic
  108. if not self._finished then
  109. parent._waiting = true
  110. coroutine.yield()
  111. parent._waiting = false
  112. end
  113.  
  114. self._parent = nil
  115.  
  116. return unpack(self.result)
  117. end
  118.  
  119. --------------------------------------
  120. -- Creation
  121. --------------------------------------
  122.  
  123. tasks = { }
  124.  
  125. tasks.current = nil -- Currently running task
  126.  
  127. function tasks.new(func)
  128. local tbl = { }
  129. setmetatable(tbl, task)
  130. tbl:init(func)
  131. return tbl
  132. end
  133.  
  134. --------------------------------------
  135. -- Async
  136. --------------------------------------
  137.  
  138. local async = { }
  139. async.__index = async
  140.  
  141. function async:init(task)
  142. self._task = task
  143. end
  144.  
  145. --- Finishes an asynchronic task.
  146. --- Any parameters passed to this function
  147. --- will be the task's result.
  148. function async:release(...)
  149. local args = { ... }
  150.  
  151. local prevTask = tasks.current
  152.  
  153. tasks.current = self._task
  154. pcall(function()
  155. coroutine.resume(self._task._coroutine, unpack(args)) -- This function errors and won't go further, pcall is a temporary fix
  156. end)
  157. tasks.current = prevTask
  158. end
  159.  
  160. function async.new(...)
  161. local tbl = { }
  162. setmetatable(tbl, async)
  163. tbl:init(...)
  164. return tbl
  165. end
  166.  
  167. --- Makes the current task asynchronic.
  168. --- It won't finish as soon as its function returns,
  169. --- but an async object will be returned - calling
  170. --- async:release(toReturn) will finish the task.
  171. function tasks.makeAsync()
  172. local asyncObj = async.new(tasks.current)
  173. tasks.current._async = asyncObj
  174.  
  175. return asyncObj
  176. end
  177.  
  178. --------------------------------------
  179. -- Testing
  180. --------------------------------------
  181.  
  182. if CLIENT then
  183. function tasks.download(url)
  184. return tasks.new(function()
  185. local async = tasks.makeAsync()
  186.  
  187. http.get(url, function(body)
  188. async:release(body)
  189. end, function()
  190. async:release()
  191. end)
  192. end)
  193. end
  194.  
  195. function tasks.wait(duration)
  196. return tasks.new(function()
  197. local async = tasks.makeAsync()
  198.  
  199. timer.simple(duration, function()
  200. async:release()
  201. end)
  202. end)
  203. end
  204.  
  205. -- Create a new task
  206. local tsk = tasks.new(function()
  207. local data = tasks.download("https://blockchain.info/ticker"):wait() -- Downloads data and waits for it
  208. print(data)
  209.  
  210. tasks.wait(2):wait() -- Uses a timer to sleep 2 seconds
  211.  
  212. data = tasks.download("https://blockchain.info/tobtc?currency=USD&value=500"):wait()
  213. print("And another! - ", data)
  214. end)
  215.  
  216. tsk:run() -- Run it
  217. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement