Created
April 17, 2015 19:27
-
-
Save nefftd/fdad6c5be9f6c932420a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- dispatcher.lua: Callback handling mechanism. | |
-- Namespace | |
local dispatcher = {} | |
-- Import | |
local util = require('irc.util') | |
local table = table | |
local next = next | |
local pairs = pairs | |
local pcall = pcall | |
local unpack = unpack | |
local select = select | |
local setmetatable = setmetatable | |
-- Dispatcher API | |
local disAPI = {} | |
disAPI.__index = disAPI | |
function disAPI:register(event,func) | |
util.argcheck(event,1,'string') | |
util.argcheck(func,2,'function') | |
if not self.events[event] then | |
-- Registering event for first time | |
self.events[event] = {} | |
self.regqueue[event] = {} | |
self.dispatchqueue[event] = {} | |
end | |
local events = self.events[event] | |
local regqueue = self.regqueue[event] | |
if events[func] or regqueue[func] then return end | |
if #self.dispatchqueue[event] > 0 then | |
-- We're not in the process of dispatching: insert directly | |
events[func] = true | |
else | |
-- We're dispatching, queue it up to be inserted later | |
regqueue[func] = true | |
end | |
end | |
function disAPI:unregister(event,func) | |
util.argcheck(event,1,'string') | |
util.argcheck(func,2,'function') | |
local events = self.events[event] | |
local regqueue = self.regqueue[event] | |
if not events then return end -- no callbacks listening | |
events[func] = nil | |
regqueue[func] = nil | |
if not next(events) and not next(regqueue) then | |
-- No more callbacks listening to this events; delete it | |
self.events[event] = nil | |
self.regqueue[event] = nil | |
self.dispatchqueue[event] = nil | |
end | |
end | |
local nop = function() end | |
local function dispatch(self,event) | |
local errorhandler = self.errorhandler or nop | |
local succ,err | |
local args = self.dispatchqueue[1] | |
for func in pairs(self.events[event]) do | |
succ,err = pcall(func,unpack(args,1,args.n)) | |
if not succ then | |
pcall(errorhandler,err) | |
end | |
end | |
-- Don't pop until the end; otherwise, dispatch queue looks empty on | |
-- subsequent calls to :invoke(). | |
table.remove(self.dispatchqueue[event],1) | |
end | |
function disAPI:invoke(event,...) | |
util.argcheck(event,1,'string') | |
if not self.events[event] then return end -- no callbacks registered | |
local queue = self.dispatchqueue[event] | |
queue[#queue+1] = { n = select('#',...), ... } | |
if #queue > 1 then | |
return -- Wait until it unrolls to process new invocation | |
end | |
repeat | |
dispatch(self,event) | |
until #queue == 0 | |
-- Process all the callbacks waiting to be registered for this event | |
local events = self.events[event] | |
local regqueue = self.regqueue[event] | |
for func in pairs(regqueue) do | |
events[func] = true | |
regqueue[func] = nil | |
end | |
end | |
function disAPI:seterrorhandler(func) | |
util.argcheck(func,1,'function') | |
self.errorhandler = func | |
end | |
function disAPI:geterrorhandler() | |
return self.errorhandler | |
end | |
-- Public API: grab new instance | |
function dispatcher.new() | |
local new = { | |
events = {}, | |
dispatchqueue = {}, | |
regqueue = {}, | |
} | |
setmetatable(new,disAPI) | |
return new | |
end | |
setmetatable(dispatcher,{ __call = dispatcher.new }) | |
return dispatcher |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment