local TablePool = require('Infrast/TablePool') local xpcall = xpcall local setmetatable = setmetatable local pairs = pairs local ipairs = ipairs local table_insert = table.insert local table_remove = table.remove local _voidSelf = {} local _eventChannels = {} local OpenDispatchFuncStatistic = false if OpenDispatchFuncStatistic then local GetTimeFunc = AuxUtil.GetLocalTimeStampMS local LuaHookUtil = require("Tools/LuaHookUtil") local RecordTimeFunc = LuaHookUtil.AddOneFuncExcuteTime local _xpcall = xpcall xpcall = function(func, ...) local curTime = GetTimeFunc() _xpcall(func, ...) RecordTimeFunc(curTime, func) end end local report_fail = logging and function(...) logging.error(LTAG.ERROR, ...) end or print --[[ eventId 事件ID eventObservers 事件的监听列表 func 事件的响应函数 itSelf 表示事件的self参数,没有self对象的回调方法使用VoidSelf占位 对于相同的回调只能注册一次,func和itSelf都相等才视为相同的回调 事件支持递归,回调方法中可以任意新增或删除事件,包括自身,立刻生效 事件回调方法的执行顺序稳定,与注册的顺序相同 eventObservers = { [eventId] = { _list_iter_stack = { [1] = iter, [2] = iter, [3] = iter ... } 迭代器栈,位置记录节点 _list_next = { [1]=func, [2]=itSelf, ... _id = ?, _list_prev = ?, _list_next = { [1]=func, [2]=itSelf, ... _id = ?, _list_prev = ?, _list_next = { [1]=func, [2]=itSelf, ... _id = ?, _list_prev = ?, _list_next = ? } }, } }, } --]] local node_registry = {} local node_identity = 0 local list_node_pool = TablePool( function(item, id, prev, next, ...) if item then item._id = id item._list_prev = prev item._list_next = next LuaExt.ResetTable(item, ...) return item else return { _id = id, _list_prev = prev, _list_next = next, ...} end end, function(item) for k, v in pairs(item) do item[k] = nil end end, 512, 512 ) --[[ 链表节点的结构 侵入式链表,目前仅事件系统内部使用 关键信息保存在字典部分: _id(迭代器使用的一个句柄,考虑以后移除) _list_prev _list_next 数据保存在数组部分: [1] ... [2] ... [3] ... 链表自身额外保存一个用于递归遍历使用的_list_iter_stack --]] local function list_create() local list = list_node_pool.TryGet() list._list_iter_stack = list_node_pool.TryGet() return list end local function list_recycle(list) list_node_pool.Recycle(list._list_iter_stack) list._list_iter_stack = nil list_node_pool.Recycle(list) end local function list_clear(list) local node = list._list_next while node do local next = node._list_next node_registry[node._id] = nil list_node_pool.Recycle(node) node = next end end local function list_is_empty(list) return list._list_next == nil end local function list_push_front(list, ...) local head = list._list_next --头节点指向链表本身,这样不需要为prev为空作特殊判断处理 node_identity = node_identity + 1 local newId = node_identity local newHead = list_node_pool.TryGet(newId, list, head, ...) node_registry[newId] = newHead list._list_next = newHead if head then head._list_prev = newHead end end local function list_push_back(list, ...) local head = list._list_next local preNode if head then preNode = head while preNode._list_next do preNode = preNode._list_next end else preNode = list end node_identity = node_identity + 1 local newId = node_identity local node = list_node_pool.TryGet(newId, preNode, nil, ...) preNode._list_next = node node_registry[newId] = node end local function list_remove(list, node) --头节点的 _list_prev 字段指向链表自身所以可保证 _list_prev 字段永不为空,恰好链表自身也通过和节点一样的 _list_next 字段指向节点 local id = node._id local prev = node._list_prev local next = node._list_next prev._list_next = next if next then next._list_prev = prev end --所以当删除的是 _list_iter_stack 中的节点时将其回退到上一个位置,以保证下次调用迭代函数时指向正确的位置, _list_prev 同理可能指向节点或是链表自身 for i, iter in ipairs(list._list_iter_stack) do if iter == node then list._list_iter_stack[i] = node._list_prev end end --remove node list_node_pool.Recycle(node) node_registry[id] = nil end local function list_remove_id(list, id) --头节点的 _list_prev 字段指向链表自身所以可保证 _list_prev 字段永不为空,恰好链表自身也通过和节点一样的 _list_next 字段指向节点 local node = node_registry[id] if node then local prev = node._list_prev local next = node._list_next prev._list_next = next if next then next._list_prev = prev end --所以当删除的是 _list_iter_stack 中的节点时将其回退到上一个位置,以保证下次调用迭代函数时指向正确的位置, _list_prev 同理可能指向节点或是链表自身 for i, iter in ipairs(list._list_iter_stack) do if iter == node then list._list_iter_stack[i] = node._list_prev end end --remove node list_node_pool.Recycle(node) node_registry[id] = nil end end local function list_remove_value2(list, v1, v2) local node = list._list_next while node do if node[1] == v1 and node[2] == v2 then --remove node from list local id = node._id local prev = node._list_prev local next = node._list_next prev._list_next = next if next then next._list_prev = prev end --update iterator for i, iter in ipairs(list._list_iter_stack) do if iter == node then list._list_iter_stack[i] = node._list_prev end end --remove node list_node_pool.Recycle(node) node_registry[id] = nil return true else node = node._list_next end end return false end local function list_find_value2(list, v1, v2) local node = list._list_next while node do if node[1] == v1 and node[2] == v2 then return node else node = node._list_next end end end local function list_create_iterator(list) table_insert(list._list_iter_stack,list) end local function list_node_iterator(list) --循环过程中对链表的修改会造成当前节点失效,例如链表ABC,当循环到A时删除AB,会导致从A的 _list_next 找到的B不是正确的位置 --这里引入 _list_iter_stack 作为一个有状态迭代器的栈,在链表级别的更新时一并修改迭代器状态,保证其永远处于链表的有效节点上且位置正确 local count = #list._list_iter_stack if count > 0 then local iter = list._list_iter_stack[count] if iter._list_next then iter = iter._list_next list._list_iter_stack[count] = iter return iter else table_remove(list._list_iter_stack) end end end local function list_value2_iterator(list) local count = #list._list_iter_stack if count > 0 then local iter = list._list_iter_stack[count] if iter._list_next then iter = iter._list_next list._list_iter_stack[count] = iter return iter[1], iter[2] else table_remove(list._list_iter_stack) end end end local function CreateEventChannel() local channel = { eventObservers = {}, --统计正在Dispatch的数量 --如果Dispatch时候Unregister导致observer被回收,在同时Register相同的事件会创建新的observer,导致后续Register的事件不会在这次执行 --因此添加eventDispatchCount数量统计防止在Dispatch的时候Unregister eventDispatchCount = 0 } _eventChannels[channel] = true return channel end local function MakeClearFunc(channel) if channel.eventDispatchCount > 0 then report_fail('event is dispathing, can not be cleared') else --TODO clear pool or not ? for k, v in pairs(channel.eventObservers) do list_clear(v) list_recycle(v) channel.eventObservers[k] = nil end channel.eventDispatchCount = 0 end end local function MakeRegistFunc(channel) return function(eventId, func, itSelf) if eventId == nil or func == nil then return end itSelf = itSelf or _voidSelf local evtObs = channel.eventObservers[eventId] if evtObs then local node = list_find_value2(evtObs, func, itSelf) if node == nil then --regist new callback func list_push_back(evtObs, func, itSelf) end else evtObs = list_create() list_push_back(evtObs, func, itSelf) channel.eventObservers[eventId] = evtObs end end end local function MakeUnRegistFunc(channel) return function(eventId, func, itSelf) if eventId == nil or func == nil then return end itSelf = itSelf or _voidSelf local evtObs = channel.eventObservers[eventId] if evtObs then --unregist callback func list_remove_value2(evtObs, func, itSelf) --clear empty evtObs when no event is dispatching if channel.eventDispatchCount <= 0 and list_is_empty(evtObs) then list_recycle(evtObs) channel.eventObservers[eventId] = nil end end end end local function MakeDispatchFunc(channel, dispatchWithEventID) if dispatchWithEventID then --额外添加事件ID作为第一个参数 return function(eventId, ...) local evtObs = channel.eventObservers[eventId] if evtObs then channel.eventDispatchCount = channel.eventDispatchCount + 1 list_create_iterator(evtObs) for func, itSelf in list_value2_iterator,evtObs do if itSelf == _voidSelf then xpcall(func, report_fail, eventId, ...) else xpcall(func, report_fail, itSelf, eventId, ...) end end channel.eventDispatchCount = channel.eventDispatchCount - 1 --clear empty evtObs after all dispatch is done if channel.eventDispatchCount <= 0 and list_is_empty(evtObs) then list_recycle(evtObs) channel.eventObservers[eventId] = nil end end end else return function(eventId, ...) local evtObs = channel.eventObservers[eventId] if evtObs then channel.eventDispatchCount = channel.eventDispatchCount + 1 list_create_iterator(evtObs) for func, itSelf in list_value2_iterator,evtObs do if itSelf == _voidSelf then xpcall(func, report_fail, ...) else xpcall(func, report_fail, itSelf, ...) end end channel.eventDispatchCount = channel.eventDispatchCount - 1 --clear empty evtObs after all dispatch is done if channel.eventDispatchCount <= 0 and list_is_empty(evtObs) then list_recycle(evtObs) channel.eventObservers[eventId] = nil end end end end end --------------------------EventSystem-------------------------------- local function CreateEventSystem(dispathWithEventID) local channel = CreateEventChannel() local system = { Regist = MakeRegistFunc(channel), UnRegist = MakeUnRegistFunc(channel), Dispatch = MakeDispatchFunc(channel, dispathWithEventID), Clear = MakeClearFunc(channel), } return system end local EventSystem = CreateEventSystem() EventSystem.CreateEventSystem = CreateEventSystem return EventSystem --[[ 单元测试 pcall(function() local EventSystem = require("Event.EventSystem") local EvtID = "EVT" local call = {} local Func = {} function Func.A() table.insert(call, "A") end function Func.B() table.insert(call, "B") end function Func.C() table.insert(call, "C") end EventSystem.Regist(EvtID, Func.A) EventSystem.Regist(EvtID, Func.B) EventSystem.Regist(EvtID, Func.C) EventSystem.Dispatch(EvtID, 1) EventSystem.Clear() print("ABC", table.concat(call) == "ABC") end) pcall(function() local EventSystem = require("Event.EventSystem") local EvtID = "EVT" local call = {} local Func = {} function Func.A() table.insert(call, "A") EventSystem.Regist(EvtID, Func.B) end function Func.B() table.insert(call, "B") EventSystem.Regist(EvtID, Func.C) end function Func.C() table.insert(call, "C") end EventSystem.Regist(EvtID, Func.A) EventSystem.Dispatch(EvtID, 1) EventSystem.Clear() print("ABC", table.concat(call) == "ABC") end) pcall(function() local EventSystem = require("Event.EventSystem") local EvtID = "EVT" local call = {} local Func = { count = 1 } function Func.A() table.insert(call, "A") if Func.count < 3 then Func.count = Func.count + 1 EventSystem.UnRegist(EvtID, Func.A) EventSystem.Regist(EvtID, Func.A) end end EventSystem.Regist(EvtID, Func.A) EventSystem.Dispatch(EvtID, 1) EventSystem.Clear() print("AAA", table.concat(call) == "AAA") end) pcall(function() local EventSystem = require("Event.EventSystem") local EvtID = "EVT" local call = {} local Func = {} function Func.A() table.insert(call, "A") EventSystem.UnRegist(EvtID, Func.B) end function Func.B() table.insert(call, "B") end function Func.C() table.insert(call, "C") EventSystem.Regist(EvtID, Func.B) end EventSystem.Regist(EvtID, Func.A) EventSystem.Regist(EvtID, Func.B) EventSystem.Regist(EvtID, Func.C) EventSystem.Dispatch(EvtID, 1) EventSystem.Clear() print("ACB", table.concat(call) == "ACB") end) pcall(function() local EventSystem = require("Event.EventSystem") local EvtID = "EVT" local call = {} local Func = {} function Func.A() table.insert(call, "A") EventSystem.UnRegist(EvtID, Func.C) end function Func.B() table.insert(call, "B") EventSystem.Regist(EvtID, Func.C) end function Func.C() table.insert(call, "C") end EventSystem.Regist(EvtID, Func.A) EventSystem.Regist(EvtID, Func.B) EventSystem.Regist(EvtID, Func.C) EventSystem.Dispatch(EvtID, 1) EventSystem.Clear() print("ABC", table.concat(call) == "ABC") end) pcall(function() local EventSystem = require("Event.EventSystem") local EvtID = "EVT" local call = {} local Func = {} function Func.A() table.insert(call, "A") end function Func.B() table.insert(call, "B") EventSystem.UnRegist(EvtID, Func.B) EventSystem.Dispatch(EvtID, 2) end function Func.C(id) table.insert(call, "C"..id) end EventSystem.Regist(EvtID, Func.A) EventSystem.Regist(EvtID, Func.B) EventSystem.Regist(EvtID, Func.C) EventSystem.Dispatch(EvtID, 1) EventSystem.Clear() print("ABAC2C1", table.concat(call) == "ABAC2C1") end) pcall(function() local EventSystem = require("Event.EventSystem") local EvtID = "EVT" local call = {} local Func = {} function Func.A() table.insert(call, "A") end function Func.B() table.insert(call, "B") EventSystem.UnRegist(EvtID, Func.B) end function Func.C() table.insert(call, "C") end EventSystem.Regist(EvtID, Func.A) EventSystem.Regist(EvtID, Func.B,nil) EventSystem.Regist(EvtID, Func.C) EventSystem.Dispatch(EvtID, 1) EventSystem.Clear() print("ABC", table.concat(call) == "ABC") end) --]]