Skip to content

Instantly share code, notes, and snippets.

@nefftd
Created April 17, 2015 19:27
Show Gist options
  • Save nefftd/fdad6c5be9f6c932420a to your computer and use it in GitHub Desktop.
Save nefftd/fdad6c5be9f6c932420a to your computer and use it in GitHub Desktop.
-- 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