From 52d1ded7dfde6adf77047c4b9a50e323d3fc86dd Mon Sep 17 00:00:00 2001
From: Přemysl Janouch
Date: Fri, 4 Nov 2016 20:38:09 +0100
Subject: degesch: move the Lua async code within the file
---
degesch.c | 800 +++++++++++++++++++++++++++++++-------------------------------
1 file changed, 400 insertions(+), 400 deletions(-)
diff --git a/degesch.c b/degesch.c
index 511fdae..a761dee 100644
--- a/degesch.c
+++ b/degesch.c
@@ -8507,454 +8507,151 @@ lua_plugin_parse (lua_State *L)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-// The script can create as many wait channels as wanted. They only actually
-// do anything once they get yielded to the main lua_resume() call.
-
-/// Identifier for the Lua metatable
-#define XLUA_WCHANNEL_METATABLE "wchannel"
-
-struct lua_wait_channel
-{
- LIST_HEADER (struct lua_wait_channel)
-
- struct lua_task *task; ///< The task we're active in
-
- /// Check if the event is ready and eventually push values to the thread;
- /// the channel then may release any resources
- bool (*check) (struct lua_wait_channel *self);
+// Lua code can use weakly referenced wrappers for internal objects.
- /// Release all resources held by the subclass
- void (*cleanup) (struct lua_wait_channel *self);
-};
+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);
-static int
-lua_wchannel_gc (lua_State *L)
+struct lua_weak_info
{
- struct lua_wait_channel *self =
- luaL_checkudata (L, 1, XLUA_WCHANNEL_METATABLE);
- if (self->cleanup)
- self->cleanup (self);
- return 0;
-}
+ const char *name; ///< Metatable name
+ struct ispect_field *ispect; ///< Introspection data
-static luaL_Reg lua_wchannel_table[] =
-{
- { "__gc", lua_wchannel_gc },
- { NULL, NULL }
+ lua_weak_ref_fn ref; ///< Weak link invalidator
+ lua_weak_unref_fn unref; ///< Weak link generator
};
-// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-// A task encapsulates a thread so that wait channels yielded from its main
-// function get waited upon by the event loop
-
-#define XLUA_TASK_METATABLE "task" ///< Identifier for the Lua metatable
-
-struct lua_task
+struct lua_weak
{
- LIST_HEADER (struct lua_task)
-
struct lua_plugin *plugin; ///< The plugin we belong to
- lua_State *thread; ///< Lua thread
- struct lua_wait_channel *active; ///< Channels we're waiting on
- struct poller_idle idle; ///< Idle job
+ struct lua_weak_info *info; ///< Introspection data
+ void *object; ///< The object
+ struct weak_ref_link *weak_ref; ///< A weak reference link
};
static void
-lua_task_unregister_channels (struct lua_task *self)
+lua_weak_invalidate (void *object, void *user_data)
{
- LIST_FOR_EACH (struct lua_wait_channel, iter, self->active)
- {
- iter->task = NULL;
- LIST_UNLINK (self->active, iter);
- lua_cache_invalidate (self->plugin->L, iter);
- }
+ 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_task_cancel_internal (struct lua_task *self)
+lua_weak_push (lua_State *L, struct lua_plugin *plugin, void *object,
+ struct lua_weak_info *info)
{
- if (self->thread)
+ if (!object)
{
- lua_cache_invalidate (self->plugin->L, self->thread);
- self->thread = NULL;
+ lua_pushnil (L);
+ return;
}
- lua_task_unregister_channels (self);
- poller_idle_reset (&self->idle);
+ if (lua_cache_get (L, object))
+ return;
- // The task no longer has to stay alive
- lua_cache_invalidate (self->plugin->L, self);
+ struct lua_weak *wrapper = lua_newuserdata (L, sizeof *wrapper);
+ luaL_setmetatable (L, info->name);
+ wrapper->plugin = plugin;
+ wrapper->info = info;
+ wrapper->object = object;
+ wrapper->weak_ref = NULL;
+ if (info->ref)
+ wrapper->weak_ref = info->ref (object, lua_weak_invalidate, wrapper);
+ lua_cache_store (L, object, -1);
}
static int
-lua_task_cancel (lua_State *L)
+lua_weak_gc (lua_State *L, const struct lua_weak_info *info)
{
- struct lua_task *self = luaL_checkudata (L, 1, XLUA_TASK_METATABLE);
- // We could also yield and make lua_task_resume() check "self->thread",
- // however the main issue here is that the script should just return
- luaL_argcheck (L, L != self->thread, 1,
- "cannot cancel task from within itself");
- lua_task_cancel_internal (self);
+ struct lua_weak *wrapper = luaL_checkudata (L, 1, info->name);
+ if (wrapper->object)
+ {
+ lua_cache_invalidate (L, wrapper->object);
+ if (info->unref)
+ info->unref (wrapper->object, &wrapper->weak_ref);
+ wrapper->object = NULL;
+ }
return 0;
}
-#define lua_task_wakeup(self) poller_idle_set (&(self)->idle)
-
-static bool
-lua_task_schedule (struct lua_task *self, int n, struct error **e)
+static struct lua_weak *
+lua_weak_deref (lua_State *L, const struct lua_weak_info *info)
{
- lua_State *L = self->thread;
- for (int i = -1; -i <= n; i--)
- {
- struct lua_wait_channel *channel =
- luaL_testudata (L, i, XLUA_WCHANNEL_METATABLE);
- if (!channel)
- return error_set (e, "bad argument #%d to yield: %s", -i + n + 1,
- "tasks can only yield wait channels");
- if (channel->task)
- return error_set (e, "bad argument #%d to yield: %s", -i + n + 1,
- "wait channels can only be active in one task at most");
- }
- for (int i = -1; -i <= n; i--)
- {
- // Quietly ignore duplicate channels
- struct lua_wait_channel *channel = lua_touserdata (L, i);
- if (channel->task)
- continue;
+ struct lua_weak *weak = luaL_checkudata (L, 1, info->name);
+ luaL_argcheck (L, weak->object, 1, "dead reference used");
+ return weak;
+}
- // By going in reverse the list ends up in the right order
- channel->task = self;
- LIST_PREPEND (self->active, channel);
- lua_cache_store (self->plugin->L, channel, i);
- }
- lua_pop (L, n);
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- // There doesn't have to be a single channel
- // We can also be waiting on a channel that is already ready
- lua_task_wakeup (self);
- return true;
-}
+#define LUA_WEAK_DECLARE(id, metatable_id) \
+ static struct lua_weak_info lua_ ## id ## _info = \
+ { \
+ .name = metatable_id, \
+ .ispect = g_ ## id ## _ispect, \
+ .ref = (lua_weak_ref_fn) id ## _weak_ref, \
+ .unref = (lua_weak_unref_fn) id ## _weak_unref, \
+ };
-static void
-lua_task_resume (struct lua_task *self, int index)
-{
- lua_State *L = self->thread;
- bool waiting_on_multiple = self->active && self->active->next;
+#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
- // Since we've ended the wait, we don't need to hold on to them anymore
- lua_task_unregister_channels (self);
+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)
- // On the first run we also have the main function on the stack,
- // before any initial arguments
- int n = lua_gettop (L) - (lua_status (L) == LUA_OK);
+// The global context is kind of fake and don't have any ref-counting,
+// however it's still very much an object
+static struct lua_weak_info lua_ctx_info =
+{
+ .name = PROGRAM_NAME,
+ .ispect = g_ctx_ispect,
+};
- // Pack the values in a table and prepend the index of the channel, so that
- // the caller doesn't need to care about the number of return values
- if (waiting_on_multiple)
- {
- lua_plugin_pack (L, n);
- lua_pushinteger (L, index);
- lua_insert (L, -2);
- n = 2;
- }
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- int res = lua_resume (L, NULL, n);
- struct error *error = NULL;
- if (res == LUA_YIELD)
- {
- // AFAIK we don't get any good error context information from here
- if (lua_task_schedule (self, lua_gettop (L), &error))
- return;
- }
- // For simplicity ignore any results from successful returns
- else if (res != LUA_OK)
- {
- luaL_traceback (L, L, lua_tostring (L, -1), 0 /* or 1? */);
- lua_plugin_process_error (self->plugin, lua_tostring (L, -1), &error);
- lua_pop (L, 2);
- }
- if (error)
- lua_plugin_log_error (self->plugin, "task", error);
- lua_task_cancel_internal (self);
+static int
+lua_user_gc (lua_State *L)
+{
+ return lua_weak_gc (L, &lua_user_info);
}
-static void
-lua_task_check (struct lua_task *self)
+static int
+lua_user_get_channels (lua_State *L)
{
- poller_idle_reset (&self->idle);
+ struct lua_weak *wrapper = lua_weak_deref (L, &lua_user_info);
+ struct user *user = wrapper->object;
- lua_Integer i = 0;
- LIST_FOR_EACH (struct lua_wait_channel, iter, self->active)
+ int i = 1;
+ lua_newtable (L);
+ LIST_FOR_EACH (struct user_channel, iter, user->channels)
{
- i++;
- if (iter->check (iter))
- {
- lua_task_resume (self, i);
- return;
- }
+ lua_weak_push (L, wrapper->plugin, iter->channel, &lua_channel_info);
+ lua_rawseti (L, -2, i++);
}
- if (!self->active)
- lua_task_resume (self, i);
+ return 1;
}
-// The task dies either when it finishes, it is cancelled, or at plugin unload
-static luaL_Reg lua_task_table[] =
+static luaL_Reg lua_user_table[] =
{
- { "cancel", lua_task_cancel },
- { "__gc", lua_task_cancel },
- { NULL, NULL }
+ { "__gc", lua_user_gc },
+ { "get_channels", lua_user_get_channels },
+ { NULL, NULL }
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-struct lua_wait_timer
+static int
+lua_channel_gc (lua_State *L)
{
- struct lua_wait_channel super; ///< The structure we're deriving
- struct poller_timer timer; ///< Timer event
- bool expired; ///< Whether the timer has expired
-};
-
-static bool
-lua_wait_timer_check (struct lua_wait_channel *wchannel)
-{
- struct lua_wait_timer *self =
- CONTAINER_OF (wchannel, struct lua_wait_timer, super);
- return self->super.task && self->expired;
-}
-
-static void
-lua_wait_timer_cleanup (struct lua_wait_channel *wchannel)
-{
- struct lua_wait_timer *self =
- CONTAINER_OF (wchannel, struct lua_wait_timer, super);
- poller_timer_reset (&self->timer);
-}
-
-static void
-lua_wait_timer_dispatch (struct lua_wait_timer *self)
-{
- self->expired = true;
- if (self->super.task)
- lua_task_wakeup (self->super.task);
-}
-
-static int
-lua_plugin_push_wait_timer (struct lua_plugin *plugin, lua_State *L,
- lua_Integer timeout)
-{
- struct lua_wait_timer *self = lua_newuserdata (L, sizeof *self);
- luaL_setmetatable (L, XLUA_WCHANNEL_METATABLE);
- memset (self, 0, sizeof *self);
-
- self->super.check = lua_wait_timer_check;
- self->super.cleanup = lua_wait_timer_cleanup;
-
- poller_timer_init (&self->timer, &plugin->ctx->poller);
- self->timer.dispatcher = (poller_timer_fn) lua_wait_timer_dispatch;
- self->timer.user_data = self;
-
- if (timeout)
- poller_timer_set (&self->timer, timeout);
- else
- self->expired = true;
- return 1;
-}
-
-// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-static int
-lua_async_go (lua_State *L)
-{
- struct lua_plugin *plugin = lua_touserdata (L, lua_upvalueindex (1));
- luaL_checktype (L, 1, LUA_TFUNCTION);
-
- lua_State *thread = lua_newthread (L);
- lua_cache_store (L, thread, -1);
- lua_pop (L, 1);
-
- // Move the main function w/ arguments to the thread
- lua_xmove (L, thread, lua_gettop (L));
-
- struct lua_task *task = lua_newuserdata (L, sizeof *task);
- luaL_setmetatable (L, XLUA_TASK_METATABLE);
- memset (task, 0, sizeof *task);
- task->plugin = plugin;
- task->thread = thread;
-
- poller_idle_init (&task->idle, &plugin->ctx->poller);
- task->idle.dispatcher = (poller_idle_fn) lua_task_check;
- task->idle.user_data = task;
- poller_idle_set (&task->idle);
-
- // Make sure the task doesn't get garbage collected and return it
- lua_cache_store (L, task, -1);
- return 1;
-}
-
-static int
-lua_async_timer_ms (lua_State *L)
-{
- struct lua_plugin *plugin = lua_touserdata (L, lua_upvalueindex (1));
- lua_Integer timeout = luaL_checkinteger (L, 1);
- if (timeout < 0)
- luaL_argerror (L, 1, "timeout mustn't be negative");
- return lua_plugin_push_wait_timer (plugin, L, timeout);
-}
-
-static luaL_Reg lua_async_library[] =
-{
- { "go", lua_async_go },
- { "timer_ms", lua_async_timer_ms },
- { NULL, NULL },
-};
-
-// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-// Lua code can use weakly referenced wrappers for internal objects.
-
-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
- struct ispect_field *ispect; ///< Introspection data
-
- 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 lua_weak_info *info; ///< Introspection data
- void *object; ///< The object
- struct weak_ref_link *weak_ref; ///< A weak reference link
-};
-
-static void
-lua_weak_invalidate (void *object, void *user_data)
-{
- 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_weak_push (lua_State *L, struct lua_plugin *plugin, void *object,
- struct lua_weak_info *info)
-{
- if (!object)
- {
- lua_pushnil (L);
- return;
- }
- if (lua_cache_get (L, object))
- return;
-
- struct lua_weak *wrapper = lua_newuserdata (L, sizeof *wrapper);
- luaL_setmetatable (L, info->name);
- wrapper->plugin = plugin;
- wrapper->info = info;
- wrapper->object = object;
- wrapper->weak_ref = NULL;
- if (info->ref)
- 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);
- if (info->unref)
- 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;
-}
-
-// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-#define LUA_WEAK_DECLARE(id, metatable_id) \
- static struct lua_weak_info lua_ ## id ## _info = \
- { \
- .name = metatable_id, \
- .ispect = g_ ## id ## _ispect, \
- .ref = (lua_weak_ref_fn) id ## _weak_ref, \
- .unref = (lua_weak_unref_fn) id ## _weak_unref, \
- };
-
-#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
-
-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)
-
-// The global context is kind of fake and don't have any ref-counting,
-// however it's still very much an object
-static struct lua_weak_info lua_ctx_info =
-{
- .name = PROGRAM_NAME,
- .ispect = g_ctx_ispect,
-};
-
-// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-static int
-lua_user_gc (lua_State *L)
-{
- return lua_weak_gc (L, &lua_user_info);
-}
-
-static int
-lua_user_get_channels (lua_State *L)
-{
- 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, user->channels)
- {
- lua_weak_push (L, wrapper->plugin, iter->channel, &lua_channel_info);
- lua_rawseti (L, -2, i++);
- }
- return 1;
-}
-
-static luaL_Reg lua_user_table[] =
-{
- { "__gc", lua_user_gc },
- { "get_channels", lua_user_get_channels },
- { NULL, NULL }
-};
-
-// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-static int
-lua_channel_gc (lua_State *L)
-{
- return lua_weak_gc (L, &lua_channel_info);
-}
+ return lua_weak_gc (L, &lua_channel_info);
+}
static int
lua_channel_get_users (lua_State *L)
@@ -10026,6 +9723,309 @@ lua_plugin_connect (lua_State *L)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+// The script can create as many wait channels as wanted. They only actually
+// do anything once they get yielded to the main lua_resume() call.
+
+/// Identifier for the Lua metatable
+#define XLUA_WCHANNEL_METATABLE "wchannel"
+
+struct lua_wait_channel
+{
+ LIST_HEADER (struct lua_wait_channel)
+
+ struct lua_task *task; ///< The task we're active in
+
+ /// Check if the event is ready and eventually push values to the thread;
+ /// the channel then may release any resources
+ bool (*check) (struct lua_wait_channel *self);
+
+ /// Release all resources held by the subclass
+ void (*cleanup) (struct lua_wait_channel *self);
+};
+
+static int
+lua_wchannel_gc (lua_State *L)
+{
+ struct lua_wait_channel *self =
+ luaL_checkudata (L, 1, XLUA_WCHANNEL_METATABLE);
+ if (self->cleanup)
+ self->cleanup (self);
+ return 0;
+}
+
+static luaL_Reg lua_wchannel_table[] =
+{
+ { "__gc", lua_wchannel_gc },
+ { NULL, NULL }
+};
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+// A task encapsulates a thread so that wait channels yielded from its main
+// function get waited upon by the event loop
+
+#define XLUA_TASK_METATABLE "task" ///< Identifier for the Lua metatable
+
+struct lua_task
+{
+ LIST_HEADER (struct lua_task)
+
+ struct lua_plugin *plugin; ///< The plugin we belong to
+ lua_State *thread; ///< Lua thread
+ struct lua_wait_channel *active; ///< Channels we're waiting on
+ struct poller_idle idle; ///< Idle job
+};
+
+static void
+lua_task_unregister_channels (struct lua_task *self)
+{
+ LIST_FOR_EACH (struct lua_wait_channel, iter, self->active)
+ {
+ iter->task = NULL;
+ LIST_UNLINK (self->active, iter);
+ lua_cache_invalidate (self->plugin->L, iter);
+ }
+}
+
+static void
+lua_task_cancel_internal (struct lua_task *self)
+{
+ if (self->thread)
+ {
+ lua_cache_invalidate (self->plugin->L, self->thread);
+ self->thread = NULL;
+ }
+ lua_task_unregister_channels (self);
+ poller_idle_reset (&self->idle);
+
+ // The task no longer has to stay alive
+ lua_cache_invalidate (self->plugin->L, self);
+}
+
+static int
+lua_task_cancel (lua_State *L)
+{
+ struct lua_task *self = luaL_checkudata (L, 1, XLUA_TASK_METATABLE);
+ // We could also yield and make lua_task_resume() check "self->thread",
+ // however the main issue here is that the script should just return
+ luaL_argcheck (L, L != self->thread, 1,
+ "cannot cancel task from within itself");
+ lua_task_cancel_internal (self);
+ return 0;
+}
+
+#define lua_task_wakeup(self) poller_idle_set (&(self)->idle)
+
+static bool
+lua_task_schedule (struct lua_task *self, int n, struct error **e)
+{
+ lua_State *L = self->thread;
+ for (int i = -1; -i <= n; i--)
+ {
+ struct lua_wait_channel *channel =
+ luaL_testudata (L, i, XLUA_WCHANNEL_METATABLE);
+ if (!channel)
+ return error_set (e, "bad argument #%d to yield: %s", -i + n + 1,
+ "tasks can only yield wait channels");
+ if (channel->task)
+ return error_set (e, "bad argument #%d to yield: %s", -i + n + 1,
+ "wait channels can only be active in one task at most");
+ }
+ for (int i = -1; -i <= n; i--)
+ {
+ // Quietly ignore duplicate channels
+ struct lua_wait_channel *channel = lua_touserdata (L, i);
+ if (channel->task)
+ continue;
+
+ // By going in reverse the list ends up in the right order
+ channel->task = self;
+ LIST_PREPEND (self->active, channel);
+ lua_cache_store (self->plugin->L, channel, i);
+ }
+ lua_pop (L, n);
+
+ // There doesn't have to be a single channel
+ // We can also be waiting on a channel that is already ready
+ lua_task_wakeup (self);
+ return true;
+}
+
+static void
+lua_task_resume (struct lua_task *self, int index)
+{
+ lua_State *L = self->thread;
+ bool waiting_on_multiple = self->active && self->active->next;
+
+ // Since we've ended the wait, we don't need to hold on to them anymore
+ lua_task_unregister_channels (self);
+
+ // On the first run we also have the main function on the stack,
+ // before any initial arguments
+ int n = lua_gettop (L) - (lua_status (L) == LUA_OK);
+
+ // Pack the values in a table and prepend the index of the channel, so that
+ // the caller doesn't need to care about the number of return values
+ if (waiting_on_multiple)
+ {
+ lua_plugin_pack (L, n);
+ lua_pushinteger (L, index);
+ lua_insert (L, -2);
+ n = 2;
+ }
+
+ int res = lua_resume (L, NULL, n);
+ struct error *error = NULL;
+ if (res == LUA_YIELD)
+ {
+ // AFAIK we don't get any good error context information from here
+ if (lua_task_schedule (self, lua_gettop (L), &error))
+ return;
+ }
+ // For simplicity ignore any results from successful returns
+ else if (res != LUA_OK)
+ {
+ luaL_traceback (L, L, lua_tostring (L, -1), 0 /* or 1? */);
+ lua_plugin_process_error (self->plugin, lua_tostring (L, -1), &error);
+ lua_pop (L, 2);
+ }
+ if (error)
+ lua_plugin_log_error (self->plugin, "task", error);
+ lua_task_cancel_internal (self);
+}
+
+static void
+lua_task_check (struct lua_task *self)
+{
+ poller_idle_reset (&self->idle);
+
+ lua_Integer i = 0;
+ LIST_FOR_EACH (struct lua_wait_channel, iter, self->active)
+ {
+ i++;
+ if (iter->check (iter))
+ {
+ lua_task_resume (self, i);
+ return;
+ }
+ }
+ if (!self->active)
+ lua_task_resume (self, i);
+}
+
+// The task dies either when it finishes, it is cancelled, or at plugin unload
+static luaL_Reg lua_task_table[] =
+{
+ { "cancel", lua_task_cancel },
+ { "__gc", lua_task_cancel },
+ { NULL, NULL }
+};
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+struct lua_wait_timer
+{
+ struct lua_wait_channel super; ///< The structure we're deriving
+ struct poller_timer timer; ///< Timer event
+ bool expired; ///< Whether the timer has expired
+};
+
+static bool
+lua_wait_timer_check (struct lua_wait_channel *wchannel)
+{
+ struct lua_wait_timer *self =
+ CONTAINER_OF (wchannel, struct lua_wait_timer, super);
+ return self->super.task && self->expired;
+}
+
+static void
+lua_wait_timer_cleanup (struct lua_wait_channel *wchannel)
+{
+ struct lua_wait_timer *self =
+ CONTAINER_OF (wchannel, struct lua_wait_timer, super);
+ poller_timer_reset (&self->timer);
+}
+
+static void
+lua_wait_timer_dispatch (struct lua_wait_timer *self)
+{
+ self->expired = true;
+ if (self->super.task)
+ lua_task_wakeup (self->super.task);
+}
+
+static int
+lua_plugin_push_wait_timer (struct lua_plugin *plugin, lua_State *L,
+ lua_Integer timeout)
+{
+ struct lua_wait_timer *self = lua_newuserdata (L, sizeof *self);
+ luaL_setmetatable (L, XLUA_WCHANNEL_METATABLE);
+ memset (self, 0, sizeof *self);
+
+ self->super.check = lua_wait_timer_check;
+ self->super.cleanup = lua_wait_timer_cleanup;
+
+ poller_timer_init (&self->timer, &plugin->ctx->poller);
+ self->timer.dispatcher = (poller_timer_fn) lua_wait_timer_dispatch;
+ self->timer.user_data = self;
+
+ if (timeout)
+ poller_timer_set (&self->timer, timeout);
+ else
+ self->expired = true;
+ return 1;
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+static int
+lua_async_go (lua_State *L)
+{
+ struct lua_plugin *plugin = lua_touserdata (L, lua_upvalueindex (1));
+ luaL_checktype (L, 1, LUA_TFUNCTION);
+
+ lua_State *thread = lua_newthread (L);
+ lua_cache_store (L, thread, -1);
+ lua_pop (L, 1);
+
+ // Move the main function w/ arguments to the thread
+ lua_xmove (L, thread, lua_gettop (L));
+
+ struct lua_task *task = lua_newuserdata (L, sizeof *task);
+ luaL_setmetatable (L, XLUA_TASK_METATABLE);
+ memset (task, 0, sizeof *task);
+ task->plugin = plugin;
+ task->thread = thread;
+
+ poller_idle_init (&task->idle, &plugin->ctx->poller);
+ task->idle.dispatcher = (poller_idle_fn) lua_task_check;
+ task->idle.user_data = task;
+ poller_idle_set (&task->idle);
+
+ // Make sure the task doesn't get garbage collected and return it
+ lua_cache_store (L, task, -1);
+ return 1;
+}
+
+static int
+lua_async_timer_ms (lua_State *L)
+{
+ struct lua_plugin *plugin = lua_touserdata (L, lua_upvalueindex (1));
+ lua_Integer timeout = luaL_checkinteger (L, 1);
+ if (timeout < 0)
+ luaL_argerror (L, 1, "timeout mustn't be negative");
+ return lua_plugin_push_wait_timer (plugin, L, timeout);
+}
+
+static luaL_Reg lua_async_library[] =
+{
+ { "go", lua_async_go },
+ { "timer_ms", lua_async_timer_ms },
+ { NULL, NULL },
+};
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
static int
lua_plugin_get_screen_size (lua_State *L)
{
--
cgit v1.2.3-70-g09d2