Advertisement
theoriginalbit

Lua: Coroutine Management System

Jun 17th, 2013
381
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 6.05 KB | None | 0 0
  1. --[[
  2.   Coroutine-Management-System (CMS) v2.0 [Updated: 10 Feb 2014]
  3.  
  4.   Copyright © 2014 Joshua Asbury a.k.a. theoriginalbit [theoriginalbit@gmail.com]
  5.  
  6.   Permission is hereby granted, free of charge, to any person obtaining a copy
  7.   of this software and associated documentation files (the "Software"), to deal
  8.   in the Software without restriction, including without limitation the rights
  9.   to use, copy, modify, merge, publish, distribute, and/or sublicense copies
  10.   of the Software, and to permit persons to whom the Software is furnished to
  11.   do so, subject to the following conditions:
  12.  
  13.   - The above copyright notice and this permission notice shall be included in
  14.     all copies or substantial portions of the Software;
  15.   - Visible credit is given to the original author;
  16.   - The software is distributed in a non-profit way;
  17.  
  18.   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  19.   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  20.   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  21.   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  22.   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  23.   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  24.   THE SOFTWARE.
  25. --]]
  26.  
  27. local function safePairs(t)
  28.   local keys = {}
  29.   for k,v in pairs(t) do
  30.     keys[#keys+1] = k
  31.   end
  32.   local i = 0
  33.   return function()
  34.     i = i + 1
  35.     return keys[i], t[keys[i]]
  36.   end
  37. end
  38.  
  39. local nextId
  40. do
  41.   local _NEXTID = 1
  42.   nextId = function()
  43.     local temp = _NEXTID
  44.     _NEXTID = _NEXTID + 1
  45.     return temp
  46.   end
  47. end
  48.  
  49. local _ROUTINES = {}
  50. local _NAMETOID = {}
  51. local _CURRENTROUTINEID = nil
  52.  
  53. local function resolveIdentifier(ident)
  54.   if type(ident) == "number" then
  55.     if _ROUTINES[ident] then
  56.       return ident
  57.     end
  58.   elseif type(ident) == "string" then
  59.     local id = _NAMETOID[ident]
  60.     if id and _ROUTINES[id] then
  61.       return id
  62.     end
  63.   elseif type(ident) == "nil" then
  64.     return _CURRENTROUTINEID
  65.   end
  66.   error("Invalid identifier", 3)
  67. end
  68.  
  69. local function resume(co, ...)
  70.   local ok, param
  71.   if coroutine.status(co.thread) ~= "dead" then
  72.     _CURRENTROUTINEID = co.id
  73.     ok, param = coroutine.resume(co.thread, ...)
  74.     if not ok then
  75.       error(param, 0)
  76.     end
  77.   end
  78.   if coroutine.status(co.thread) == "dead" then
  79.     _ROUTINES[co.id] = nil
  80.     _NAMETOID[co.name] = nil
  81.   end
  82.   _CURRENTROUTINEID = nil
  83.   return param
  84. end
  85.  
  86. function routineStatus(id)
  87.   local co
  88.   if type(id) == "number" then
  89.     co = _ROUTINES[id]
  90.   elseif type(id) == "string" then
  91.     co = _ROUTINES[_NAMETOID[id]]
  92.   elseif type(id) == "thread" then
  93.     co = id
  94.   elseif type(ident) == "nil" then
  95.     co = _ROUTINES[_CURRENTROUTINEID]
  96.   end
  97.   co = type(co) == "table" and co.thread or co
  98.   return type(co) == "thread" and coroutine.status(co) or "dead"
  99. end
  100.  
  101. function createCoroutine(name,func)
  102.   if type(name) ~= "string" then
  103.     error("Expected coroutine name", 2)
  104.   end
  105.   if type(func) == "function" then
  106.     func = coroutine.create(func)
  107.   end
  108.   if type(func) == "thread" then
  109.     local id = nextId()
  110.     _NAMETOID[name] = id
  111.     _ROUTINES[id] = {id=id; name=name; isPaused=false; thread=func}
  112.     return id
  113.   end
  114.   return nil
  115. end
  116.  
  117. function stopCoroutine(id)
  118.   local co = _ROUTINES[resolveIdentifier(id)]
  119.   if coroutine.status(co.thread) ~= "dead" then
  120.     resume(co, "SIG_STOP") -- warn of impending stop
  121.     if coroutine.status(co.thread) == "dead" then
  122.       return true
  123.     end
  124.     return killCoroutine(id)
  125.   end
  126.   return false
  127. end
  128.  
  129. function killCoroutine(id)
  130.   local co = _ROUTINES[resolveIdentifier(id)]
  131.   if coroutine.status(co.thread) ~= "dead" then
  132.     resume(co, "SIG_KILL") -- warn of impending kill
  133.     _ROUTINES[co.id] = nil -- dereference to have the gc clean it up
  134.     _NAMETOID[co.name] = nil
  135.     return true
  136.   end
  137.   return false
  138. end
  139.  
  140. function pauseCoroutine(id)
  141.   local co = _ROUTINES[resolveIdentifier(id)]
  142.   if coroutine.status(co.thread) ~= "dead" then
  143.     co.isPaused = true
  144.     resume(co, "SIG_PAUSE")
  145.     return true
  146.   end
  147.   error("Cannot pause a dead coroutine", 2)
  148. end
  149.  
  150. function resumeCoroutine(id)
  151.   local co = _ROUTINES[resolveIdentifier(id)]
  152.   if coroutine.status(co.thread) ~= "dead" then
  153.     co.isPaused = false
  154.     resume(co, "SIG_RESUME")
  155.     return true
  156.   end
  157.   error("Cannot resume a dead coroutine", 2)
  158. end
  159.  
  160. function isCoroutinePaused(id)
  161.   local co = _ROUTINES[resolveIdentifier(id)]
  162.   return co.isPaused
  163. end
  164.  
  165. function getCoroutine(id)
  166.   return _ROUTINES[resolveIdentifier(id)].thread
  167. end
  168.  
  169. function queueTargetedEvent(id, ...)
  170.   local co = resolveIdentifier(id)
  171.   os.queueEvent("targeted_event", co.id, ...)
  172. end
  173.  
  174. local _PRIORITYQUEUE = {}
  175. function queuePriorityEvent(id, ...)
  176.   local co = resolveIdentifier(id)
  177.   table.insert(_PRIORITYQUEUE, {id=co.id; data={...}})
  178. end
  179.  
  180. local function run(min)
  181.   local eventData = {}
  182.   local filters = {}
  183.  
  184.   while true do
  185.     if #_ROUTINES <= min then
  186.       break
  187.     end
  188.     for id, routine in safePairs(_ROUTINES) do
  189.       if routine and not routine.isPaused then
  190.         if eventData[1] == "targeted_event" and eventData[2] == id then
  191.           table.remove(eventData, 1) -- remove targeted_event
  192.           table.remove(eventData, 1) -- remove targeted id
  193.           filters[id] = resume(routine, unpack(eventData))
  194.           break
  195.         elseif not filters[id] or filters[id] == eventData[1] or eventData[1] == "terminate" then
  196.           filters[id] = resume(routine, unpack(eventData))
  197.         end
  198.       end
  199.     end
  200.     while _PRIORITYQUEUE[1] do
  201.       local event = _PRIORITYQUEUE[1]
  202.       local co = _ROUTINES[event.id]
  203.       if not co.isPaused then
  204.         resume(co, unpack(event.data))
  205.       end
  206.       table.remove(_PRIORITYQUEUE, 1)
  207.     end
  208.     eventData = {coroutine.yield()}
  209.   end
  210. end
  211.  
  212. local function count(t)
  213.   local c = 0
  214.   for _ in pairs(t) do
  215.     c = c + 1
  216.   end
  217.   return c
  218. end
  219.  
  220. function waitForAny()
  221.   run(count(_ROUTINES) - 1)
  222. end
  223.  
  224. function waitForAll()
  225.   run(0)
  226. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement