🐳chore(库):增加 事件 库

develop
cloudfreexiao 5 years ago
parent 87e75eed73
commit ea436776c7

@ -17,6 +17,10 @@ https://github.com/hellogcc/100-gdb-tips
Modern Unix Command Modern Unix Command
https://github.com/ibraheemdev/modern-unix https://github.com/ibraheemdev/modern-unix
A tool for writing better scripts
https://github.com/google/zx
https://github.com/cloudwu/skynet/issues/288 https://github.com/cloudwu/skynet/issues/288

@ -0,0 +1,109 @@
local setmetatable = setmetatable
local DoubleQueue = {}
-- Class attributes and methods goes on this table --
local DoubleQueueObject = {}
-- Metamethods goes on this table --
local DoubleQueueObject_mt = {
__index = DoubleQueueObject,
}
-- Module exported functions --
---
-- Creates a new double queue.
function DoubleQueue.new()
local object = setmetatable({}, DoubleQueueObject_mt)
-- create all the instance state data.
object.data = {}
object.data_position = {}
object.first = 1
object.last = 0
return object
end
-- Private methods --
local function refresh_first(self)
while (self.first <= self.last) do
if (self.data[self.first]) then
return true
end
self.first = self.first + 1
end
end
-- Public methods --
---
-- Check if the queue is empty
-- @return true if it is empty, false otherwise.
function DoubleQueueObject:is_empty()
return self.first > self.last
end
---
-- Pushes data on the front of the queue.
-- @param data The data thas is being pushed.
function DoubleQueueObject:push_front(data)
if (self.data_position[data]) then
return
end
self.first = self.first - 1
self.data[self.first] = data
self.data_position[data] = self.first
end
---
-- Pushes data on the back of the queue.
-- @param data The data thas is being pushed.
function DoubleQueueObject:push_back(data)
if (self.data_position[data]) then
return
end
self.last = self.last + 1
self.data[self.last] = data
self.data_position[data] = self.last
end
---
-- Get the queue iterator.
-- Each call to the iterator function returns the next element.
-- Example:
--
-- for data in queue:get_iterator() do
-- print("mydata: "..tostring(data))
-- end
--
-- @return An iterator function.
function DoubleQueueObject:get_iterator()
local first = self.first
local function iterator()
while (first <= self.last) do
local data = self.data[first]
first = first + 1
if (data) then
return data
end
end
end
return iterator
end
---
-- Removes the first ocurrence of data from the queue.
-- If the data cant be found on the queue, nothing happens.
-- @param data The data that is going the be removed.
function DoubleQueueObject:remove(data)
if (not self.data_position[data]) then
return
end
self.data[self.data_position[data]] = nil
self.data_position[data] = nil
refresh_first(self)
end
return DoubleQueue

@ -0,0 +1,431 @@
--- https://github.com/katcipis/luanotify
-- This module aims to build a generic hierarchic event system.
-- The hierarchic model uses string event names to define what event are you working with.
--
-- For example:
--
-- * "event" -> Just a normal event.
-- * "event:subevent" -> Using the subevent feature.
--
-- The ":" is what defines that you are using hierarchic events,
-- every ":" you put is a new hierarchic level.
--
-- When emiting "event:subevent", everyone connected at "event" and "event:subevent"
-- is going to be notified. When emitting "event", only the ones connected at "event" are
-- going to be notified.
--
-- Using this hierarchical structure it is easy to be notified only when a specific event happens
-- or when a whole bunch of events inside a category happens. All events can be expressed on a tree,
-- where a node is a event, and you can connect,add a pre emit, add a post emit,
-- disconnect, block, unblock, emit, on any node of the tree.
--
-- @class module
-- @name notify.event
local setmetatable = setmetatable
local string = string
local unpack = unpack
local Queue = require "notify.dqueue"
local separator = ":"
-- Module exported functions
local Event = {}
-- Class attributes and methods goes on this table --
local EventObject = {}
-- Metamethods goes on this table --
local EventObject_mt = {
__index = EventObject,
}
-- Private methods definition --
local function new_node()
return {
handlers = Queue.new(),
pre_emits = Queue.new(),
post_emits = Queue.new(),
blocked_handlers = {},
subevents = {},
}
end
local function get_nodes_names(event_name)
local nodes_names = {}
for n in string.gmatch(event_name, "[^" .. separator .. "]+") do
nodes_names[#nodes_names + 1] = n
end
return nodes_names
end
local function get_node(self, event_name)
local events_names = get_nodes_names(event_name)
local current_node = self.events[events_names[1]] or new_node()
self.events[events_names[1]] = current_node
for i = 2, #events_names do
local sub_node = current_node.subevents[events_names[i]] or new_node()
current_node.subevents[events_names[i]] = sub_node
current_node = sub_node
end
return current_node
end
local function unused_event(self, event_name)
local events_names = get_nodes_names(event_name)
local current_node = self.events[events_names[1]]
if not current_node then
return true
end
for i = 2, #events_names do
local sub_node = current_node.subevents[events_names[i]]
if not sub_node then
return true
end
current_node = sub_node
end
return false
end
local function event_iterator(self, event_name)
local events_names = get_nodes_names(event_name)
local i = 2
local current_node = self.events[events_names[1]]
local function iterator()
if not current_node then
return
end
local ret = current_node
if events_names[i] then
current_node = current_node.subevents[events_names[i]]
i = i + 1
else
current_node = nil
end
return ret
end
return iterator
end
local function call_pre_emits(self, event_name)
local nodes = Queue.new()
local reversed_nodes = Queue.new()
for node in event_iterator(self, event_name) do
for pre_emit in node.pre_emits:get_iterator() do
pre_emit(event_name)
end
nodes:push_back(node)
reversed_nodes:push_front(node)
end
return nodes, reversed_nodes
end
local function call_post_emits(event_name, reversed_nodes)
for node in reversed_nodes:get_iterator() do
for post_emit in node.post_emits:get_iterator() do
post_emit(event_name)
end
end
end
local function call_handlers(self, params)
for node in params.nodes:get_iterator() do
for handler in node.handlers:get_iterator() do
if (self.stopped) then
return
end
if (node.blocked_handlers[handler] == 0) then
if (params.accumulator) then
params.accumulator(handler(params.event_name, unpack(params.args)))
else
handler(params.event_name, unpack(params.args))
end
end
end
end
end
-- Module exported functions
---
-- Creates a new Event object.
-- @return The new Event object.
function Event.new()
local object = setmetatable({}, EventObject_mt)
-- create all the instance state data.
object.stopped = false
object.events = {}
return object
end
-- Class definition and methods --
---
-- Connects a handler function on this event.
-- If any subevent is emitted, this handler will be called too.
-- @param event_name - The event name.
-- @param handler_function - The function that will be called when the event_name is emitted.
function EventObject:connect(event_name, handler_function)
local node = get_node(self, event_name)
node.handlers:push_back(handler_function)
if not node.blocked_handlers[handler_function] then
node.blocked_handlers[handler_function] = 0
end
end
---
-- Disconnects a handler function on this event.
-- @param event_name - The event name.
-- @param handler_function - The function that will be disconnected.
function EventObject:disconnect(event_name, handler_function)
if unused_event(self, event_name) then
return
end
local node = get_node(self, event_name)
node.handlers:remove(handler_function)
node.blocked_handlers[handler_function] = nil
end
---
-- Does not execute the given handler function when the give event is emitted until it is unblocked.
-- It can be called several times for the same handler function.
--
-- Example:
--
-- local Event = require "notify.event"
-- local event = Event.new()
-- local function handler1(arg)
-- print(arg)
-- end
--
-- event:connect("mouse", handler1)
-- event:emit("mouse", "example") -- example gets printed.
--
-- event:block("mouse", handler1);
-- event:emit("mouse", "example") -- nothing gets printed.
--
--
-- @param event_name - The event name.
-- @param handler_function - The handler function that will be blocked.
function EventObject:block(event_name, handler_function)
if unused_event(self, event_name) then
return
end
local node = get_node(self, event_name)
local block = node.blocked_handlers[handler_function]
if block then
node.blocked_handlers[handler_function] = block + 1
end
end
---
-- Unblocks the handler function from the given event.
-- The calls to unblock must match the calls to block.
--
-- Example:
--
-- local Event = require "notify.event"
-- local event = Event.new()
--
-- local function handler1(arg)
-- print(arg)
-- end
--
-- event:connect("mouse", handler1)
-- event:emit("mouse", "example") -- example gets printed.
--
-- event:block("mouse", handler1);
-- event:emit("mouse", "example") -- nothing gets printed.
--
-- event:block("mouse", handler1);
-- event:emit("mouse", "example") -- nothing gets printed.
--
-- event:unblock("mouse", handler1);
-- event:emit("mouse", "example") -- nothing gets printed.
-- event:unblock("mouse", handler1);
-- event:emit("mouse", "example") -- example gets printed.
--
--
-- @param event_name - The event name.
-- @param handler_function - The handler function that will be unblocked.
function EventObject:unblock(event_name, handler_function)
if unused_event(self, event_name) then
return
end
local node = get_node(self, event_name)
if node.blocked_handlers[handler_function] and node.blocked_handlers[handler_function] > 0 then
node.blocked_handlers[handler_function] = node.blocked_handlers[handler_function] - 1
end
end
---
-- Emits an event and all handler functions connected to it will be called.
-- Emiting the event "event1::event2::event3" will call the handlers connected
-- on the following events, on this order:
-- * event1
-- * event1:event2
-- * event1:event2:event3
--
-- @param event_name - The event name.
-- @param ... - A optional list of parameters, they will be repassed to the handler functions connected to this event.
function EventObject:emit(event_name, ...)
self.stopped = false
local nodes, reversed_nodes = call_pre_emits(self, event_name)
call_handlers(self, {
event_name = event_name,
nodes = nodes,
args = {...},
})
call_post_emits(event_name, reversed_nodes)
end
---
-- Typical emission discards handlers return values completely.
-- This is most often what you need: just inform the world about something.
-- However, sometimes you need a way to get feedback.
-- For instance, you may want to ask: “is this value acceptable ?”
-- This is what accumulators are for. Accumulators are specified to events at emission time.
-- They can combine, alter or discard handlers return values, post-process them or even stop emission.
-- Since a handler can return multiple values, accumulators can receive multiple args too.
-- Following Lua flexible style we give the user the freedom to do whatever he wants with accumulators.
-- If you are using the hierarchic event system the behaviour of handlers calling is similar to the emit function.
-- @param event_name - The event name.
-- @param accumulator - Function that will receive handlers results or a table to accumulate
-- all the handlers returned values.
-- @param ... - A optional list of parameters, they will be repassed to the handler
-- functions connected to this signal.
function EventObject:emit_with_accumulator(event_name, accumulator, ...)
self.stopped = false
local nodes, reversed_nodes = call_pre_emits(self, event_name)
call_handlers(self, {
event_name = event_name,
nodes = nodes,
accumulator = accumulator,
args = {...},
})
call_post_emits(event_name, reversed_nodes)
end
---
-- Adds a pre_emit func, pre_emit functions can't be blocked, only added or removed.
-- They can't have their return collected by accumulators, they will not receive any data
-- passed on the emission and they are always called before ANY handler is called.
-- This is useful when you want to perform some global task before handling an event,
-- like opening a socket that the handlers might need to use or a opening a database.
-- pre_emit functions can make sure everything is ok before handling an event, reducing
-- the need to do this check_ups inside the handler functions itself (sometimes multiple times).
-- They are called on a queue (FIFO) policy based on the order they added.
-- When using hierarchy, pre_emission happen top-bottom. For example, with a mouse::button1 event,
-- first the pre_emit functions on mouse will be called, then mouse::button1 post_emit functions will be called.
-- @param event_name - The event name.
-- @param pre_emit_func - The pre_emit function.
function EventObject:add_pre_emit(event_name, pre_emit_func)
get_node(self, event_name).pre_emits:push_back(pre_emit_func)
end
---
-- Removes a pre-emit func from the given event.
-- @param event_name - The event name.
-- @param pre_emit_func - The pre_emit function.
function EventObject:remove_pre_emit(event_name, pre_emit_func)
if unused_event(self, event_name) then
return
end
get_node(self, event_name).pre_emits:remove(pre_emit_func)
end
---
-- Adds a post_emit function, post_emit functions can't be blocked, only added or removed,
-- they can't have their return collected by accumulators, they will not receive any data passed
-- on the emission and they are always called after ALL handlers where called.
-- This is useful when you want to perform some global task after handling an event,
-- like closing a socket or a database that the handlers might need to use or do some cleanup.
-- post_emit functions can make sure everything is released after handling an event, reducing the need
-- to do this check_ups inside some handler function, since some resources can be shared by multiple handlers.
-- They are called on a stack (LIFO) policy based on the order they added. When using hierarchy,
-- post_emission happen bottom-top. For example, with a mouse::button1 event, first the post_emit
-- functions on mouse::button1 will be called, then mouse post_emit functions will be called.
-- @param event_name - The event name.
-- @param post_emit_func - The post_emit function.
function EventObject:add_post_emit(event_name, post_emit_func)
get_node(self, event_name).post_emits:push_front(post_emit_func)
end
---
-- Removes a post-emit func from the given event.
-- @param event_name - The event name.
-- @param post_emit_func - The post_emit function.
function EventObject:remove_post_emit(event_name, post_emit_func)
if unused_event(self, event_name) then
return
end
get_node(self, event_name).post_emits:remove(post_emit_func)
end
---
-- Has effect only during a emission and will stop only this particular emission of the event.
-- Usually called inside a pre-emit (when a condition fail) or on any handler.
--
-- Example:
--
-- local Event = require "notify.event"
-- local event = Event.new()
--
-- function handler1()
-- print("handler1")
-- event.stop();
-- end
--
-- function handler2()
-- print("2")
-- end
--
-- event:connect("mouse", handler1)
-- event:connect("mouse::click", handler2)
--
-- event:emit("mouse::click") --handler2 never gets printed because handler1 always stops the emission
--
function EventObject:stop()
self.stopped = true
end
---
-- Removes all pre/post-emits and handlers from the given event_name.
-- If no name is given all pre/post-emits and handlers will be removed.
-- @param event_name - The name of the event that will be cleared, or nil to clear all events.
function EventObject:clear(event_name)
if (not event_name) then
self.events = {}
return
end
end
-- Public functions --
local global_event = Event.new()
---
-- Always returns the same Event instance, this way is easy to share the same Event object across different modules.
-- @return An EventObject instance.
function Event.get_global_event()
return global_event
end
return Event

@ -0,0 +1,352 @@
---
-- Signal object implementation.
-- @class module
-- @name notify.signal
local setmetatable = setmetatable
local Queue = require "notify.dqueue"
local Signal = {}
-- Class attributes and methods goes on this table --
local SignalObject = {}
-- Metamethods goes on this table --
local SignalObject_mt = {
__index = SignalObject,
}
-- Class definition and methods --
---
-- Disconnects a handler function from this signal, the function will no longer be called.
-- Example:
--
-- local signal = require "notify.signal"
-- local s = signal.new()
--
-- function handler(arg)
-- print(arg)
-- end
--
-- s:connect(handler)
-- s:emit("example") -- example gets printed
-- s:disconnect(handler)
-- s:emit("example") -- nothing gets printed
--
-- @param handler_function The function that will be disconnected.
function SignalObject:disconnect(handler_function)
self.handlers:remove(handler_function)
self.handlers_block[handler_function] = nil
end
---
-- Connects a handler function on this signal, all handlers connected will be called
-- when the signal is emitted with a FIFO behaviour (The first connected will be the first called).
-- Example:
-- local signal = require "notify.signal"
--
-- function handler1(arg)
-- print(arg.."1")
-- end
-- function handler2(arg)
-- print(arg.."2")
-- end
--
-- local s = signal.new()
-- s:connect(handler1)
-- s:connect(handler2)
-- s:emit("example") -- example1 gets printed before example2.
--
-- @param handler_function The function that will be called when this signal is emitted.
function SignalObject:connect(handler_function)
if (not self.handlers_block[handler_function]) then
self.handlers_block[handler_function] = 0
self.handlers:push_back(handler_function)
end
end
---
-- Does not execute the given handler function when the signal is emitted until it is unblocked.
-- It can be called several times for the same handler function.
-- Example:
--
-- local signal = require "notify.signal"
-- local s = signal.new()
--
-- function handler(arg)
-- print(arg)
-- end
--
-- s:connect(handler)
-- s:emit("example") -- example gets printed
--
-- s:block(handler)
-- s:emit("example") -- nothing gets printed
--
-- @param handler_function The handler function that will be blocked.
function SignalObject:block(handler_function)
if (self.handlers_block[handler_function]) then
self.handlers_block[handler_function] = self.handlers_block[handler_function] + 1
end
end
---
-- Unblocks the given handler function, this handler function will be executed on
-- the order it was previously connected, and it will only be unblocked when
-- the calls to unblock are equal to the calls to block.
-- Example:
--
-- local signal = require "notify.signal"
-- local s = signal.new()
-- function handler(arg)
-- print(arg)
-- end
-- s:connect(handler)
-- s:emit("example") -- example gets printed
-- s:block(handler)
-- s:emit("example") -- nothing gets printed
-- s:block(handler)
-- s:emit("example") -- nothing gets printed
-- s:unblock(handler)
-- s:emit("example") -- nothing gets printed
-- s:unblock(handler)
-- s:emit("example") -- example gets printed
--
-- @param handler_function The handler function that will be unblocked.
function SignalObject:unblock(handler_function)
if (self.handlers_block[handler_function]) then
if (self.handlers_block[handler_function] > 0) then
self.handlers_block[handler_function] = self.handlers_block[handler_function] - 1
end
end
end
---
-- Emits a signal calling the handler functions connected to this signal passing the given args.
--
-- local signal = require "notify.signal"
-- local s = signal.new()
-- function handler1(arg1, arg2)
-- print(arg1)
-- print(arg2)
-- end
-- function handler2(arg)
-- print(arg)
-- end
-- s:connect(handler1)
-- s:connect(handler2)
-- s:emit("example") -- a nil will get printed because only one argument was passed
-- s:emit("example1", "example2") -- No nil will get printed.
-- s:emit() -- Only nils will get printed because no argument was passed.
--
-- @param … A optional list of parameters, they will be repassed to the handler functions connected to this signal.
function SignalObject:emit(...)
self.signal_stopped = false
for set_up in self.pre_emit_funcs:get_iterator() do
set_up()
end
for handler in self.handlers:get_iterator() do
if (self.signal_stopped) then
break
end
if (self.handlers_block[handler] == 0) then
handler(...)
end
end
for tear_down in self.post_emit_funcs:get_iterator() do
tear_down()
end
end
---
-- Typical signal emission discards handler return values completely.
-- This is most often what you need: just inform the world about something.
-- However, sometimes you need a way to get feedback. For instance,
-- you may want to ask: “is this value acceptable ?”
-- This is what accumulators are for. Accumulators are specified to signals at emission time.
-- They can combine, alter or discard handler return values, post-process them or even stop emission.
-- Since a handler can return multiple values, accumulators can receive multiple args too, following
-- Lua flexible style user has the freedom to do whatever he wants with accumulators.
--
-- local signal = require "notify.signal"
-- local s = signal.new()
--
-- function handler1(arg)
-- return arg * 2
-- end
--
-- function handler2(arg)
-- return arg * 3
-- end
--
-- local result = {}
-- function accum(arg)
-- result[#result+1] = arg
-- end
--
-- s:connect(handler1)
-- s:connect(handler2)
--
-- s:emit_with_accumulator(accum, 2)
--
-- for k,v in ipairs(result) do -- print 4, 6
-- print(v)
-- end
--
-- @param accumulator Function that will accumulate handlers results.
-- @param … A optional list of parameters, they will be repassed to the handler functions connected to this signal.
function SignalObject:emit_with_accumulator(accumulator, ...)
self.signal_stopped = false;
for set_up in self.pre_emit_funcs:get_iterator() do
set_up()
end
for handler in self.handlers:get_iterator() do
if (self.signal_stopped) then
break
end
if (self.handlers_block[handler] == 0) then
accumulator(handler(...))
end
end
for tear_down in self.post_emit_funcs:get_iterator() do
tear_down()
end
end
---
-- Adds a pre_emit func, pre_emit functions cant be blocked, only added or removed,
-- they cannot have their return collected by accumulators, will not receive any data passed
-- on the emission and they are always called before ANY handler is called.
-- This is useful when you want to perform some global task before handling an event,
-- like opening a socket that the handlers might need to use or a database, pre_emit functions
-- can make sure everything is ok before handling an event, reducing the need to do this check_ups
-- inside the handler function. They are called on a queue (FIFO) policy based on the order they added.
--
-- local signal = require "notify.signal"
-- local s = signal.new()
--
-- function handler1()
-- print(1)
-- end
--
-- function handler2()
-- print(2)
-- end
--
-- function pre_emit()
-- print("0")
-- end
--
-- s:connect(handler1)
-- s:connect(handler2)
-- s:emit() -- 1 and 2 printed.
-- s:add_pre_emit(pre_emit)
-- s:emit() -- 0,1 and 2 are printed.
--
-- @param pre_emit_func The pre_emit function.
function SignalObject:add_pre_emit(pre_emit_func)
self.pre_emit_funcs:push_back(pre_emit_func)
end
---
-- Removes the pre_emit function
-- @param pre_emit_func The pre_emit function.
function SignalObject:remove_pre_emit(pre_emit_func)
self.pre_emit_funcs:remove(pre_emit_func)
end
---
-- Adds a post_emit function, post_emit functions cant be blocked, only added or removed,
-- they cannot have their return collected by accumulators, they will not receive any data
-- passed on the emission and they are always called after ALL handlers where called.
-- This is useful when you want to perform some global task after handling an event,
-- like closing a socket that the handlers might need to use or a database or do some cleanup.
-- post_emit functions can make sure everything is released after handling an event,
-- reducing the need to do this check_ups inside some handler function, since some resources
-- can be shared by multiple handlers. They are called on a stack (LIFO) policy based on the order they added.
-- Example:
--
-- local signal = require "notify.signal"
-- local s = signal.new()
--
-- function handler1()
-- print(1)
-- end
--
-- function handler2()
-- print(2)
-- end
--
-- function post_emit()
-- print("3")
-- end
--
-- s:connect(handler1)
-- s:connect(handler2)
-- s:emit() -- 1 and 2 printed.
-- s:add_post_emit(post_emit)
-- s:emit() -- 1, 2 and 3 are printed.
--
-- @param post_emit_func The post_emit function.
function SignalObject:add_post_emit(post_emit_func)
self.post_emit_funcs:push_front(post_emit_func)
end
---
-- Removes the post_emit function
-- @param post_emit_func The post_emit function.
function SignalObject:remove_post_emit(post_emit_func)
self.post_emit_funcs:remove(post_emit_func)
end
---
-- Stops the current emission, if there is any handler left to be called by the signal it wont be called.
-- Example:
--
-- local signal = require "notify.signal"
-- local s = signal.new()
--
-- local function handler1()
-- print("hanlder1")
-- signal:stop()
-- end
--
-- local function handler2()
-- print("hanlder2")
-- end
--
-- s:connect(handler1)
-- s:connect(handler2)
-- s:emit() -- handler2 never gets printed because handler1 always stops the emission
--
function SignalObject:stop()
self.signal_stopped = true
end
-- Signal module exported functions --
---
-- Creates a new SignalObject.
function Signal.new()
local object = {}
-- set the metatable of the new object as the SignalObject_mt table (inherits SignalObject).
setmetatable(object, SignalObject_mt)
-- create all the instance state data.
object.handlers_block = {}
object.handlers = Queue.new()
object.pre_emit_funcs = Queue.new()
object.post_emit_funcs = Queue.new()
object.signal_stopped = false
return object
end
return Signal
Loading…
Cancel
Save