Skip to content
Snippets Groups Projects
Commit 716ac5d0 authored by Marek Vavruša's avatar Marek Vavruša
Browse files

modules/lua: module API functions may return coroutines

this allows for continuation of a function in the background,
a good use case for this is loading of chunks of data or I/O 
parent a1eda135
Branches
Tags
No related merge requests found
......@@ -33,34 +33,21 @@ static inline lua_State *l_ffi_preface(struct kr_module *module, const char *cal
lua_State *L = module->lib;
lua_rawgeti(L, LUA_REGISTRYINDEX, (intptr_t)module->data);
lua_getfield(L, -1, call);
lua_remove(L, -2);
lua_pushlightuserdata(L, module);
return L;
}
/** @internal Helper for calling the entrypoint. */
static inline int l_ffi_resume(lua_State *L, bool resume, int argc)
{
int status = resume ? l_resume(L, argc) : engine_pcall(L, argc);
switch(status) {
case LUA_YIELD: /* Continuation */
return kr_error(EAGAIN);
case 0: /* Finished */
return lua_tonumber(L, 1);
default: /* Error */
lua_pop(L, 1);
return kr_error(EIO);
}
}
/** @internal Continue with coroutine. */
static void l_ffi_resume_cb(uv_idle_t *check)
{
lua_State *L = check->data;
int status = l_ffi_resume(L, true, 0);
if (status != kr_error(EAGAIN)) {
int status = l_resume(L, 0);
if (status != LUA_YIELD) {
uv_idle_stop(check); /* Stop coroutine */
uv_close((uv_handle_t *)check, (uv_close_cb)free);
}
lua_pop(L, lua_gettop(L));
}
/** @internal Schedule deferred continuation. */
......@@ -78,9 +65,20 @@ static int l_ffi_defer(lua_State *L)
/** @internal Helper for calling the entrypoint. */
static inline int l_ffi_call(lua_State *L, int argc)
{
int status = l_ffi_resume(L, false, argc);
if (status == kr_error(EAGAIN)) {
return l_ffi_defer(L);
int status = lua_pcall(L, argc, LUA_MULTRET, 0);
if (status != 0) {
lua_pop(L, 1);
return kr_error(EIO);
}
int n = lua_gettop(L);
if (n > 0) {
if (lua_isthread(L, -1)) { /* Continuations */
status = l_ffi_defer(lua_tothread(L, -1));
} else if (lua_isnumber(L, -1)) { /* Return code */
status = lua_tonumber(L, 1);
}
lua_pop(L, n);
}
return status;
}
......@@ -94,13 +92,12 @@ static int l_ffi_init(struct kr_module *module)
static int l_ffi_deinit(struct kr_module *module)
{
lua_State *L = l_ffi_preface(module, "deinit");
/* @note Do not allow coroutine here. */
int ret = l_ffi_resume(L, false, 1);
int ret = l_ffi_call(L, 1);
/* Free the layer API wrapper */
lua_rawgeti(L, LUA_REGISTRYINDEX, (intptr_t)module->data);
lua_getfield(L, -1, "_layercdata");
free(lua_touserdata(L, -1));
lua_pop(L, 1);
lua_pop(L, 2);
/* Unref module and unset 'lib', so the module
* interface doesn't attempt to close it.
*/
......@@ -117,8 +114,11 @@ static int l_ffi_deinit(struct kr_module *module)
lua_State *L = module->lib; \
lua_rawgeti(L, LUA_REGISTRYINDEX, (intptr_t)module->data); \
lua_getfield(L, -1, "layer"); \
lua_remove(L, -2); \
lua_getfield(L, -1, (name)); \
lua_remove(L, -2); \
if (lua_isnil(L, -1)) { \
lua_pop(L, 1); \
return ctx->state; \
} \
lua_pushnumber(L, ctx->state);
......@@ -128,21 +128,21 @@ static int l_ffi_layer_begin(knot_layer_t *ctx, void *module_param)
LAYER_FFI_CALL(ctx, "begin");
lua_pushlightuserdata(L, module_param);
ctx->data = module_param;
return l_ffi_resume(L, false, 2);
return l_ffi_call(L, 2);
}
static int l_ffi_layer_reset(knot_layer_t *ctx)
{
LAYER_FFI_CALL(ctx, "reset");
lua_pushlightuserdata(L, ctx->data);
return l_ffi_resume(L, false, 2);
return l_ffi_call(L, 2);
}
static int l_ffi_layer_finish(knot_layer_t *ctx)
{
LAYER_FFI_CALL(ctx, "finish");
lua_pushlightuserdata(L, ctx->data);
return l_ffi_resume(L, false, 2);
return l_ffi_call(L, 2);
}
static int l_ffi_layer_consume(knot_layer_t *ctx, knot_pkt_t *pkt)
......@@ -150,7 +150,7 @@ static int l_ffi_layer_consume(knot_layer_t *ctx, knot_pkt_t *pkt)
LAYER_FFI_CALL(ctx, "consume");
lua_pushlightuserdata(L, ctx->data);
lua_pushlightuserdata(L, pkt);
return l_ffi_resume(L, false, 3);
return l_ffi_call(L, 3);
}
static int l_ffi_layer_produce(knot_layer_t *ctx, knot_pkt_t *pkt)
......@@ -158,7 +158,7 @@ static int l_ffi_layer_produce(knot_layer_t *ctx, knot_pkt_t *pkt)
LAYER_FFI_CALL(ctx, "produce");
lua_pushlightuserdata(L, ctx->data);
lua_pushlightuserdata(L, pkt);
return l_ffi_resume(L, false, 3);
return l_ffi_call(L, 3);
}
static int l_ffi_layer_fail(knot_layer_t *ctx, knot_pkt_t *pkt)
......@@ -166,7 +166,7 @@ static int l_ffi_layer_fail(knot_layer_t *ctx, knot_pkt_t *pkt)
LAYER_FFI_CALL(ctx, "fail");
lua_pushlightuserdata(L, ctx->data);
lua_pushlightuserdata(L, pkt);
return l_ffi_resume(L, false, 3);
return l_ffi_call(L, 3);
}
/** @internal Retrieve C layer api wrapper. */
......@@ -175,27 +175,26 @@ static const knot_layer_api_t* l_ffi_layer(struct kr_module *module)
lua_State *L = module->lib;
lua_rawgeti(L, LUA_REGISTRYINDEX, (intptr_t)module->data);
lua_getfield(L, -1, "_layer_capi");
if (lua_isnil(L, -1)) {
knot_layer_api_t *api = lua_touserdata(L, -1);
lua_pop(L, 1);
if (!api) {
/* Fabricate layer API wrapping the Lua functions */
knot_layer_api_t *api = malloc(sizeof(*api));
if (!api) {
return NULL;
if (api) {
api->begin = l_ffi_layer_begin;
api->finish = l_ffi_layer_finish;
api->consume = l_ffi_layer_consume;
api->produce = l_ffi_layer_produce;
api->reset = l_ffi_layer_reset;
api->fail = l_ffi_layer_fail;
api->data = module;
}
api->begin = l_ffi_layer_begin;
api->finish = l_ffi_layer_finish;
api->consume = l_ffi_layer_consume;
api->produce = l_ffi_layer_produce;
api->reset = l_ffi_layer_reset;
api->fail = l_ffi_layer_fail;
api->data = module;
/* Store the api in the registry. */
lua_pop(L, 1);
lua_pushlightuserdata(L, api);
lua_setfield(L, -2, "_layer_capi");
return api;
} else {
return lua_touserdata(L, -1);
}
lua_pop(L, 1); /* Clear module table */
return api;
}
#undef LAYER_FFI_CALL
......@@ -229,6 +228,7 @@ int ffimodule_register_lua(struct engine *engine, struct kr_module *module, cons
REGISTER_FFI_CALL(L, module->layer, "layer", &l_ffi_layer);
module->data = (void *)(intptr_t)luaL_ref(L, LUA_REGISTRYINDEX);
module->lib = L;
lua_pop(L, 1); /* Clear the module global */
return module->init(module);
}
......
......@@ -139,6 +139,9 @@ The modules follow the `Lua way <http://lua-users.org/wiki/ModuleDefinition>`_,
return counter
.. tip:: The API functions may return an integer value just like in other languages, but they may also return a coroutine that will be continued asynchronously. A good use case for this approach is is a deferred initialization,
e.g. loading a chunks of data or waiting for I/O.
The created module can be then loaded just like any other module, except it isn't very useful since it
doesn't provide any layer to capture events. The Lua module can however provide a processing layer, just
:ref:`like its C counterpart <lib-layers>`.
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment