From: Marek VavruĊĦa Date: Fri, 15 May 2015 10:30:18 +0000 (+0200) Subject: modules/lua: module API functions may return coroutines X-Git-Tag: v1.0.0-beta1~164 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=716ac5d0956ceb588e309f4f0841ef143dcf4b2c;p=thirdparty%2Fknot-resolver.git 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 --- diff --git a/daemon/ffimodule.c b/daemon/ffimodule.c index 6b967a948..c95201c84 100644 --- a/daemon/ffimodule.c +++ b/daemon/ffimodule.c @@ -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); } diff --git a/modules/README.rst b/modules/README.rst index 8c4864264..a8da3c50a 100644 --- a/modules/README.rst +++ b/modules/README.rst @@ -139,6 +139,9 @@ The modules follow the `Lua way `_, 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 `.