degesch: move the Lua async code within the file
This commit is contained in:
parent
cb9f187f80
commit
52d1ded7df
606
degesch.c
606
degesch.c
|
@ -8507,309 +8507,6 @@ 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);
|
|
||||||
|
|
||||||
/// 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 },
|
|
||||||
};
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
// Lua code can use weakly referenced wrappers for internal objects.
|
// Lua code can use weakly referenced wrappers for internal objects.
|
||||||
|
|
||||||
typedef struct weak_ref_link *
|
typedef struct weak_ref_link *
|
||||||
|
@ -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
|
static int
|
||||||
lua_plugin_get_screen_size (lua_State *L)
|
lua_plugin_get_screen_size (lua_State *L)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue