degesch: refactor Lua weak objects

This commit is contained in:
Přemysl Eric Janouch 2016-04-21 10:02:33 +02:00
parent a9b77b3206
commit fa5e005728
Signed by: p
GPG Key ID: B715679E3A361BE6

337
degesch.c
View File

@ -8322,113 +8322,126 @@ lua_plugin_parse (lua_State *L)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#define XLUA_USER_METATABLE "user" ///< Identifier for the Lua metatable
// Lua code can use weakly referenced wrappers for internal objects.
struct lua_user
typedef struct weak_ref_link *
(*lua_weak_ref_fn) (void *object, destroy_cb_fn cb, void *user_data);
typedef void (*lua_weak_unref_fn) (void *object, struct weak_ref_link **link);
struct lua_weak_info
{
const char *name; ///< Metatable name
lua_weak_ref_fn ref; ///< Weak link invalidator
lua_weak_unref_fn unref; ///< Weak link generator
};
struct lua_weak
{
struct lua_plugin *plugin; ///< The plugin we belong to
struct user *user; ///< The user
void *object; ///< The object
struct weak_ref_link *weak_ref; ///< A weak reference link
};
static void
lua_user_invalidate (void *object, void *user_data)
lua_weak_invalidate (void *object, void *user_data)
{
struct lua_user *wrapper = user_data;
wrapper->user = NULL;
struct lua_weak *wrapper = user_data;
wrapper->object = NULL;
wrapper->weak_ref = NULL;
// This can in theory call the GC, order isn't arbitrary here
lua_cache_invalidate (wrapper->plugin->L, object);
}
static void
lua_plugin_push_user (struct lua_plugin *plugin, struct user *user)
lua_weak_push (struct lua_plugin *plugin, void *object,
struct lua_weak_info *info)
{
lua_State *L = plugin->L;
if (lua_cache_get (L, user))
if (!object)
{
lua_pushnil (L);
return;
}
if (lua_cache_get (L, object))
return;
struct lua_user *wrapper = lua_newuserdata (L, sizeof *wrapper);
luaL_setmetatable (L, XLUA_USER_METATABLE);
struct lua_weak *wrapper = lua_newuserdata (L, sizeof *wrapper);
luaL_setmetatable (L, info->name);
wrapper->plugin = plugin;
wrapper->user = user;
wrapper->weak_ref = user_weak_ref (user, lua_user_invalidate, wrapper);
lua_cache_store (L, user, -1);
wrapper->object = object;
wrapper->weak_ref = info->ref (object, lua_weak_invalidate, wrapper);
lua_cache_store (L, object, -1);
}
static int
lua_weak_gc (lua_State *L, const struct lua_weak_info *info)
{
struct lua_weak *wrapper = luaL_checkudata (L, 1, info->name);
if (wrapper->object)
{
lua_cache_invalidate (L, wrapper->object);
info->unref (wrapper->object, &wrapper->weak_ref);
wrapper->object = NULL;
}
return 0;
}
static struct lua_weak *
lua_weak_deref (lua_State *L, const struct lua_weak_info *info)
{
struct lua_weak *weak = luaL_checkudata (L, 1, info->name);
luaL_argcheck (L, weak->object, 1, "dead reference used");
return weak;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/// Identifier for the Lua metatable
#define XLUA_CHANNEL_METATABLE "channel"
#define LUA_WEAK_DECLARE(id, metatable_id) \
static struct lua_weak_info lua_ ## id ## _info = \
{ \
.name = metatable_id, \
.ref = (lua_weak_ref_fn) id ## _weak_ref, \
.unref = (lua_weak_unref_fn) id ## _weak_unref, \
};
struct lua_channel
{
struct lua_plugin *plugin; ///< The plugin we belong to
struct channel *channel; ///< The channel
struct weak_ref_link *weak_ref; ///< A weak reference link
};
#define XLUA_USER_METATABLE "user" ///< Identifier for Lua metatable
#define XLUA_CHANNEL_METATABLE "channel" ///< Identifier for Lua metatable
#define XLUA_BUFFER_METATABLE "buffer" ///< Identifier for Lua metatable
#define XLUA_SERVER_METATABLE "server" ///< Identifier for Lua metatable
static void
lua_channel_invalidate (void *object, void *user_data)
{
struct lua_channel *wrapper = user_data;
wrapper->channel = NULL;
wrapper->weak_ref = NULL;
// This can in theory call the GC, order isn't arbitrary here
lua_cache_invalidate (wrapper->plugin->L, object);
}
static void
lua_plugin_push_channel (struct lua_plugin *plugin, struct channel *channel)
{
lua_State *L = plugin->L;
if (lua_cache_get (L, channel))
return;
struct lua_channel *wrapper = lua_newuserdata (L, sizeof *wrapper);
luaL_setmetatable (L, XLUA_CHANNEL_METATABLE);
wrapper->plugin = plugin;
wrapper->channel = channel;
wrapper->weak_ref = channel_weak_ref
(channel, lua_channel_invalidate, wrapper);
lua_cache_store (L, channel, -1);
}
LUA_WEAK_DECLARE (user, XLUA_USER_METATABLE)
LUA_WEAK_DECLARE (channel, XLUA_CHANNEL_METATABLE)
LUA_WEAK_DECLARE (buffer, XLUA_BUFFER_METATABLE)
LUA_WEAK_DECLARE (server, XLUA_SERVER_METATABLE)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static int
lua_user_gc (lua_State *L)
{
struct lua_user *wrapper = luaL_checkudata (L, 1, XLUA_USER_METATABLE);
if (wrapper->user)
{
lua_cache_invalidate (L, wrapper->user);
user_weak_unref (wrapper->user, &wrapper->weak_ref);
wrapper->user = NULL;
}
return 0;
return lua_weak_gc (L, &lua_user_info);
}
static int
lua_user_get_nickname (lua_State *L)
{
struct lua_user *wrapper = luaL_checkudata (L, 1, XLUA_USER_METATABLE);
luaL_argcheck (L, wrapper->user, 1, "dead reference used");
lua_pushstring (L, wrapper->user->nickname);
struct lua_weak *wrapper = lua_weak_deref (L, &lua_user_info);
struct user *user = wrapper->object;
lua_pushstring (L, user->nickname);
return 1;
}
static int
lua_user_get_channels (lua_State *L)
{
struct lua_user *wrapper = luaL_checkudata (L, 1, XLUA_USER_METATABLE);
luaL_argcheck (L, wrapper->user, 1, "dead reference used");
struct lua_weak *wrapper = lua_weak_deref (L, &lua_user_info);
struct user *user = wrapper->object;
int i = 1;
lua_newtable (L);
LIST_FOR_EACH (struct user_channel, iter, wrapper->user->channels)
LIST_FOR_EACH (struct user_channel, iter, user->channels)
{
lua_plugin_push_channel (wrapper->plugin, iter->channel);
lua_weak_push (wrapper->plugin, iter->channel, &lua_channel_info);
lua_rawseti (L, -2, i++);
}
return 1;
@ -8447,40 +8460,30 @@ static luaL_Reg lua_user_table[] =
static int
lua_channel_gc (lua_State *L)
{
struct lua_channel *wrapper =
luaL_checkudata (L, 1, XLUA_CHANNEL_METATABLE);
if (wrapper->channel)
{
lua_cache_invalidate (L, wrapper->channel);
channel_weak_unref (wrapper->channel, &wrapper->weak_ref);
wrapper->channel = NULL;
}
return 0;
return lua_weak_gc (L, &lua_channel_info);
}
static int
lua_channel_get_name (lua_State *L)
{
struct lua_channel *wrapper =
luaL_checkudata (L, 1, XLUA_CHANNEL_METATABLE);
luaL_argcheck (L, wrapper->channel, 1, "dead reference used");
lua_pushstring (L, wrapper->channel->name);
struct lua_weak *wrapper = lua_weak_deref (L, &lua_channel_info);
struct channel *channel = wrapper->object;
lua_pushstring (L, channel->name);
return 1;
}
static int
lua_channel_get_users (lua_State *L)
{
struct lua_channel *wrapper =
luaL_checkudata (L, 1, XLUA_CHANNEL_METATABLE);
luaL_argcheck (L, wrapper->channel, 1, "dead reference used");
struct lua_weak *wrapper = lua_weak_deref (L, &lua_channel_info);
struct channel *channel = wrapper->object;
int i = 1;
lua_newtable (L);
LIST_FOR_EACH (struct channel_user, iter, wrapper->channel->users)
LIST_FOR_EACH (struct channel_user, iter, channel->users)
{
lua_createtable (L, 0, 2);
lua_plugin_push_user (wrapper->plugin, iter->user);
lua_weak_push (wrapper->plugin, iter->user, &lua_user_info);
lua_setfield (L, -2, "user");
lua_plugin_kv (L, "prefixes", iter->prefixes.str);
@ -8499,140 +8502,45 @@ static luaL_Reg lua_channel_table[] =
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#define XLUA_BUFFER_METATABLE "buffer" ///< Identifier for the Lua metatable
struct lua_buffer
{
struct lua_plugin *plugin; ///< The plugin we belong to
struct buffer *buffer; ///< The buffer
struct weak_ref_link *weak_ref; ///< A weak reference link
};
static void
lua_buffer_invalidate (void *object, void *user_data)
{
struct lua_buffer *wrapper = user_data;
wrapper->buffer = NULL;
wrapper->weak_ref = NULL;
// This can in theory call the GC, order isn't arbitrary here
lua_cache_invalidate (wrapper->plugin->L, object);
}
static void
lua_plugin_push_buffer (struct lua_plugin *plugin, struct buffer *buffer)
{
lua_State *L = plugin->L;
if (lua_cache_get (L, buffer))
return;
struct lua_buffer *wrapper = lua_newuserdata (L, sizeof *wrapper);
luaL_setmetatable (L, XLUA_BUFFER_METATABLE);
wrapper->plugin = plugin;
wrapper->buffer = buffer;
wrapper->weak_ref = buffer_weak_ref
(buffer, lua_buffer_invalidate, wrapper);
lua_cache_store (L, buffer, -1);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#define XLUA_SERVER_METATABLE "server" ///< Identifier for the Lua metatable
struct lua_server
{
struct lua_plugin *plugin; ///< The plugin we belong to
struct server *server; ///< The server
struct weak_ref_link *weak_ref; ///< A weak reference link
};
static void
lua_server_invalidate (void *object, void *user_data)
{
struct lua_server *wrapper = user_data;
wrapper->server = NULL;
wrapper->weak_ref = NULL;
// This can in theory call the GC, order isn't arbitrary here
lua_cache_invalidate (wrapper->plugin->L, object);
}
static void
lua_plugin_push_server (struct lua_plugin *plugin, struct server *server)
{
lua_State *L = plugin->L;
if (lua_cache_get (L, server))
return;
struct lua_server *wrapper = lua_newuserdata (L, sizeof *wrapper);
luaL_setmetatable (L, XLUA_SERVER_METATABLE);
wrapper->plugin = plugin;
wrapper->server = server;
wrapper->weak_ref = server_weak_ref
(server, lua_server_invalidate, wrapper);
lua_cache_store (L, server, -1);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static int
lua_buffer_gc (lua_State *L)
{
struct lua_buffer *wrapper = luaL_checkudata (L, 1, XLUA_BUFFER_METATABLE);
if (wrapper->buffer)
{
lua_cache_invalidate (L, wrapper->buffer);
buffer_weak_unref (wrapper->buffer, &wrapper->weak_ref);
wrapper->buffer = NULL;
}
return 0;
return lua_weak_gc (L, &lua_buffer_info);
}
static int
lua_buffer_get_user (lua_State *L)
{
struct lua_buffer *wrapper = luaL_checkudata (L, 1, XLUA_BUFFER_METATABLE);
luaL_argcheck (L, wrapper->buffer, 1, "dead reference used");
if (wrapper->buffer->user)
lua_plugin_push_user (wrapper->plugin, wrapper->buffer->user);
else
lua_pushnil (L);
struct lua_weak *wrapper = lua_weak_deref (L, &lua_buffer_info);
struct buffer *buffer = wrapper->object;
lua_weak_push (wrapper->plugin, buffer->user, &lua_user_info);
return 1;
}
static int
lua_buffer_get_channel (lua_State *L)
{
struct lua_buffer *wrapper = luaL_checkudata (L, 1, XLUA_BUFFER_METATABLE);
luaL_argcheck (L, wrapper->buffer, 1, "dead reference used");
if (wrapper->buffer->channel)
lua_plugin_push_channel (wrapper->plugin, wrapper->buffer->channel);
else
lua_pushnil (L);
struct lua_weak *wrapper = lua_weak_deref (L, &lua_buffer_info);
struct buffer *buffer = wrapper->object;
lua_weak_push (wrapper->plugin, buffer->channel, &lua_channel_info);
return 1;
}
static int
lua_buffer_get_server (lua_State *L)
{
struct lua_buffer *wrapper = luaL_checkudata (L, 1, XLUA_BUFFER_METATABLE);
luaL_argcheck (L, wrapper->buffer, 1, "dead reference used");
if (wrapper->buffer->server)
lua_plugin_push_server (wrapper->plugin, wrapper->buffer->server);
else
lua_pushnil (L);
struct lua_weak *wrapper = lua_weak_deref (L, &lua_buffer_info);
struct buffer *buffer = wrapper->object;
lua_weak_push (wrapper->plugin, buffer->server, &lua_server_info);
return 1;
}
static int
lua_buffer_log (lua_State *L)
{
struct lua_buffer *wrapper = luaL_checkudata (L, 1, XLUA_BUFFER_METATABLE);
luaL_argcheck (L, wrapper->buffer, 1, "dead reference used");
struct lua_weak *wrapper = lua_weak_deref (L, &lua_buffer_info);
struct buffer *buffer = wrapper->object;
const char *message = lua_plugin_check_utf8 (L, 2);
struct buffer *buffer = wrapper->buffer;
log_full (wrapper->plugin->ctx, buffer->server, buffer,
BUFFER_LINE_STATUS, "#s", message);
return 0;
@ -8641,11 +8549,9 @@ lua_buffer_log (lua_State *L)
static int
lua_buffer_execute (lua_State *L)
{
struct lua_buffer *wrapper = luaL_checkudata (L, 1, XLUA_BUFFER_METATABLE);
luaL_argcheck (L, wrapper->buffer, 1, "dead reference used");
struct lua_weak *wrapper = lua_weak_deref (L, &lua_buffer_info);
struct buffer *buffer = wrapper->object;
const char *line = lua_plugin_check_utf8 (L, 2);
struct buffer *buffer = wrapper->buffer;
process_input_utf8 (wrapper->plugin->ctx, buffer, line, 0);
return 0;
}
@ -8667,50 +8573,33 @@ static luaL_Reg lua_buffer_table[] =
static int
lua_server_gc (lua_State *L)
{
struct lua_server *wrapper = luaL_checkudata (L, 1, XLUA_SERVER_METATABLE);
if (wrapper->server)
{
lua_cache_invalidate (L, wrapper->server);
server_weak_unref (wrapper->server, &wrapper->weak_ref);
wrapper->server = NULL;
}
return 0;
return lua_weak_gc (L, &lua_server_info);
}
static int
lua_server_get_user (lua_State *L)
{
struct lua_server *wrapper = luaL_checkudata (L, 1, XLUA_SERVER_METATABLE);
luaL_argcheck (L, wrapper->server, 1, "dead reference used");
if (wrapper->server->irc_user)
lua_plugin_push_user (wrapper->plugin, wrapper->server->irc_user);
else
lua_pushnil (L);
struct lua_weak *wrapper = lua_weak_deref (L, &lua_server_info);
struct server *server = wrapper->object;
lua_weak_push (wrapper->plugin, server->irc_user, &lua_user_info);
return 1;
}
static int
lua_server_get_buffer (lua_State *L)
{
struct lua_server *wrapper = luaL_checkudata (L, 1, XLUA_SERVER_METATABLE);
luaL_argcheck (L, wrapper->server, 1, "dead reference used");
if (wrapper->server->buffer)
lua_plugin_push_buffer (wrapper->plugin, wrapper->server->buffer);
else
lua_pushnil (L);
struct lua_weak *wrapper = lua_weak_deref (L, &lua_server_info);
struct server *server = wrapper->object;
lua_weak_push (wrapper->plugin, server->buffer, &lua_buffer_info);
return 1;
}
static int
lua_server_send (lua_State *L)
{
struct lua_server *wrapper = luaL_checkudata (L, 1, XLUA_SERVER_METATABLE);
luaL_argcheck (L, wrapper->server, 1, "dead reference used");
const char *line = luaL_checkstring (L, 2);
irc_send (wrapper->server, "%s", line);
struct lua_weak *wrapper = lua_weak_deref (L, &lua_server_info);
struct server *server = wrapper->object;
irc_send (server, "%s", luaL_checkstring (L, 2));
return 0;
}
@ -8807,9 +8696,9 @@ lua_input_hook_filter (struct input_hook *self, struct buffer *buffer,
lua_State *L = plugin->L;
lua_rawgeti (L, LUA_REGISTRYINDEX, hook->ref_callback);
lua_rawgetp (L, LUA_REGISTRYINDEX, hook); // 1: hook
lua_plugin_push_buffer (plugin, buffer); // 2: buffer
lua_pushstring (L, input); // 3: input
lua_rawgetp (L, LUA_REGISTRYINDEX, hook); // 1: hook
lua_weak_push (plugin, buffer, &lua_buffer_info); // 2: buffer
lua_pushstring (L, input); // 3: input
struct error *e = NULL;
if (lua_plugin_call (plugin, 3, 1, &e))
@ -8838,9 +8727,9 @@ lua_irc_hook_filter (struct irc_hook *self, struct server *s, char *message)
lua_State *L = plugin->L;
lua_rawgeti (L, LUA_REGISTRYINDEX, hook->ref_callback);
lua_rawgetp (L, LUA_REGISTRYINDEX, hook); // 1: hook
lua_plugin_push_server (plugin, s); // 2: server
lua_pushstring (L, message); // 3: message
lua_rawgetp (L, LUA_REGISTRYINDEX, hook); // 1: hook
lua_weak_push (plugin, s, &lua_server_info); // 2: server
lua_pushstring (L, message); // 3: message
struct error *e = NULL;
if (lua_plugin_call (plugin, 3, 1, &e))
@ -8927,7 +8816,7 @@ lua_completion_hook_complete (struct completion_hook *self,
lua_rawgetp (L, LUA_REGISTRYINDEX, hook); // 1: hook
lua_plugin_push_completion (L, data); // 2: data
lua_plugin_push_buffer (plugin, plugin->ctx->current_buffer);
lua_weak_push (plugin, plugin->ctx->current_buffer, &lua_buffer_info);
lua_setfield (L, -2, "buffer");
lua_pushstring (L, word); // 3: word