Skip to content
Snippets Groups Projects
Commit 2d9c039b authored by Andrey Saranchin's avatar Andrey Saranchin Committed by Aleksandr Lyapunov
Browse files

lua: use the new trigger API in swim triggers

New swim:on_member_event triggers use a slightly changed new trigger API
because it uses closures as triggers and a context can be passed.
Firstly, swim:on_member_event returns closure over the new trigger
instead of passed handler. Secondly, ctx can be passed as the second
argument - it is considered as an old trigger if the object is callable
and as ctx otherwise. Also, ctx can be passed as the third argument - it
is considered as name if it is string and as ctx otherwise. And, ctx can
be passed as the fourth argument. When one uses key-value API, ctx can
be passed with key "ctx".

Closes #6484
Closes #8659

NO_DOC=later
parent 0e689b84
No related branches found
No related tags found
No related merge requests found
## feature/lua
* Any callable object can now be registered as a trigger (gh-6484).
## feature/lua
* All triggers are now provided with optional names (gh-8659).
......@@ -48,13 +48,63 @@ lua_swim_member_event_push(struct lua_State *L, void *event)
return 2;
}
/**
* Normalize arguments for swim_object:on_member_event.
* Is implemented in C because is_callable function is not exported to Lua.
*
* Input format:
* 1. new_trigger,
* 2. old_trigger or ctx - is considered as old_trigger if object is callable.
* 3. trigger_name or ctx - is considered as trigger_name if object is string.
* 4. ctx or nil
* Also, ctx can be passed with key-value trigger API using key "ctx".
*
* Output format: new_trigger, old_trigger, trigger_name, ctx.
*/
static int
lua_swim_on_member_event_normalize_arguments(struct lua_State *L)
{
if (!luaL_iscallable(L, 1) && lua_istable(L, 1)) {
/* Key-value API is used. */
lua_getfield(L, 1, "func");
lua_pushnil(L);
lua_getfield(L, 1, "name");
lua_getfield(L, 1, "ctx");
return 4;
}
/* Fill the stack with nils. */
lua_settop(L, 4);
const int default_ctx_idx = 4;
int ctx_idx = default_ctx_idx;
/* New trigger. */
lua_pushvalue(L, 1);
/* Old trigger or ctx. */
if (luaL_iscallable(L, 2) || lua_isnil(L, 2) || luaL_isnull(L, 2)) {
lua_pushvalue(L, 2);
} else {
lua_pushnil(L);
ctx_idx = 2;
}
/* Name or ctx, if it wasn't met earlier. */
if (lua_type(L, 3) == LUA_TSTRING || lua_isnil(L, 3) ||
luaL_isnull(L, 3) || ctx_idx != default_ctx_idx) {
lua_pushvalue(L, 3);
} else {
lua_pushnil(L);
ctx_idx = 3;
}
/* Context. */
lua_pushvalue(L, ctx_idx);
return 4;
}
/** Set or/and delete a trigger on a SWIM member event. */
static int
lua_swim_on_member_event(struct lua_State *L)
{
uint32_t ctypeid;
struct swim *s = *(struct swim **) luaL_checkcdata(L, 1, &ctypeid);
return lbox_trigger_reset(L, 3, swim_trigger_list_on_member_event(s),
return lbox_trigger_reset(L, 2, swim_trigger_list_on_member_event(s),
lua_swim_member_event_push, NULL);
}
......@@ -115,6 +165,8 @@ tarantool_lua_swim_init(struct lua_State *L)
{"swim_delete", lua_swim_delete},
{"swim_quit", lua_swim_quit},
{"swim_on_member_event", lua_swim_on_member_event},
{"swim_on_member_event_normalize_arguments",
lua_swim_on_member_event_normalize_arguments},
{NULL, NULL}
};
luaT_newmodule(L, "swim.lib", lua_swim_internal_methods);
......
......@@ -828,25 +828,31 @@ end
--
-- Add or/and delete a trigger on member event. Possible usages:
--
-- * on_member_event(new[, ctx]) - add a new trigger. It should
-- * on_member_event(new[, ctx][, name]) - add a new trigger. It should
-- accept 3 arguments: an updated member, an events object, an
-- optional @a ctx parameter passed as is.
-- optional @a ctx parameter passed as is. If name is passed as
-- the third parameter, the new trigger is set by name.
--
-- * on_member_event(new, old[, ctx]) - add a new trigger @a new
-- if not nil, in place of @a old trigger.
--
-- * on_member_event(new, old, name[, ctx]) - add a new trigger
-- @a new if not nil by passed name, @a old is ignored. Name
-- must be a string, otherwise it is considered to be a ctx.
--
-- * on_member_event{func = new, name = name, ctx = ctx} - add
-- a new trigger @a new if not nil by passed name.
--
-- * on_member_event() - get a list of triggers.
--
local function swim_on_member_event(s, new, old, ctx)
local function swim_on_member_event(s, ...)
local ptr = swim_check_instance(s, 'swim:on_member_event')
if type(old) ~= 'function' then
ctx = old
old = nil
end
local new, old, name, ctx =
internal.swim_on_member_event_normalize_arguments(...)
if new ~= nil then
new = swim_on_member_event_new(s, new, ctx)
end
return internal.swim_on_member_event(ptr, new, old)
return internal.swim_on_member_event(ptr, new, old, name)
end
--
......
......@@ -42,6 +42,8 @@ g.before_all(function()
-- one is a function that sets triggers, and the second one is the name
-- of the associated event in the trigger registry. The second element
-- is optional.
-- SWIM triggers cannot be tested because they use wrappers as triggers,
-- so they should be tested in a separate test.
rawset(_G, 'old_api_triggers', {
{box.session.on_connect, 'box.session.on_connect'},
{box.session.on_disconnect, 'box.session.on_disconnect'},
......@@ -285,3 +287,75 @@ g.test_callable_number = function()
end
end)
end
-- Triggers swim:on_member_event require a separate test since they accept ctx
-- and use wrappers as triggers.
g.test_swim_on_member_event = function()
g.server:exec(function()
local fiber = require('fiber')
local swim = require('swim')
local function uuid(i)
local min_valid_prefix = '00000000-0000-1000-8000-'
if i < 10 then
return min_valid_prefix..'00000000000'..tostring(i)
end
assert(i < 100)
return min_valid_prefix..'0000000000'..tostring(i)
end
local function uri(port)
port = port or 0
return '127.0.0.1:'..tostring(port)
end
local history = {}
local ctx_history = {}
local s = swim.new({generation = 0})
s:on_member_event(function(_, _, ctx)
table.insert(history, 1)
table.insert(ctx_history, ctx)
end, 'ctx1')
s:on_member_event(function(_, _, ctx)
table.insert(history, 2)
table.insert(ctx_history, ctx)
end, nil, {ctx = 'ctx2'})
-- Each next trigger is inserted and then replaced.
local t3 = s:on_member_event(function() end, nil, nil, 'ctx3')
s:on_member_event(function(_, _, ctx)
table.insert(history, 3)
table.insert(ctx_history, ctx)
end, t3, nil, 'ctx3')
s:on_member_event(function() end, nil, 'named1', 'ctx4')
s:on_member_event(function(_, _, ctx)
table.insert(history, 4)
table.insert(ctx_history, ctx)
end, nil, 'named1', 'ctx4')
s:on_member_event(function() end, 'ctx5', 'named2')
s:on_member_event(function(_, _, ctx)
table.insert(history, 5)
table.insert(ctx_history, ctx)
end, 'ctx5', 'named2')
s:on_member_event{func = function() end, name = 'named3'}
s:on_member_event{func = function(_, _, ctx)
table.insert(history, 6)
table.insert(ctx_history, ctx)
end, name = 'named3', ctx = 'ctx6'}
s:on_member_event(function() end, nil, 'named4')
s:on_member_event(function(_, _, ctx)
table.insert(history, 7)
table.insert(ctx_history, ctx)
end, nil, 'named4')
s:cfg{uuid = uuid(1), uri = uri(), heartbeat_rate = 0.01}
while #history < 1 do fiber.sleep(0) end
t.assert_equals(history, {7, 6, 5, 4, 3, 2, 1})
t.assert_equals(ctx_history,
{'ctx6', 'ctx5', 'ctx4', 'ctx3', {ctx = 'ctx2'}, 'ctx1'})
end)
end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment