terom@203: #include "lua_thread.h" terom@203: terom@203: #include terom@203: #include terom@203: terom@203: #include terom@203: terom@203: terom@203: void lua_thread_init (struct lua_thread *thread, struct nexus_lua *lua, lua_thread_cb cb_func, void *cb_arg) terom@203: { terom@203: // zero terom@203: memset(thread, 0, sizeof(*thread)); terom@203: terom@203: // store terom@203: thread->lua = lua; terom@203: thread->cb_func = cb_func; terom@203: thread->cb_arg = cb_arg; terom@203: } terom@203: terom@203: /** terom@203: * Thread completed, cleanup and call user callback with given error (NULL for success). terom@203: */ terom@203: static void lua_thread_done (lua_State *L, struct lua_thread *thread, error_t *err) terom@203: { terom@203: assert(L == thread->L); terom@203: terom@203: // remove from registry so thread can be GC'd terom@203: lua_pushthread(L); terom@203: lua_pushnil(L); terom@203: lua_settable(L, LUA_REGISTRYINDEX); terom@203: terom@203: // drop our ref terom@203: thread->L = NULL; terom@203: terom@203: // call terom@203: thread->cb_func(thread, err, thread->cb_arg); terom@203: } terom@203: terom@203: /** terom@203: * Execute the lua_resume with zero arguments, invoking the callback on succesful completion, and returning any terom@203: * error. terom@203: */ terom@203: static err_t lua_thread_resume (lua_State *L, struct lua_thread *thread, error_t *err) terom@203: { terom@203: int ret; terom@203: terom@203: // invoke it terom@203: switch ((ret = lua_resume(L, 0))) { terom@203: case LUA_YIELD: terom@203: // ok, running, wait terom@203: return SUCCESS; terom@203: terom@203: case 0: terom@203: // done, notify user terom@203: lua_thread_done(L, thread, NULL); terom@203: terom@203: return SUCCESS; terom@203: terom@203: default: terom@203: // let caller handle terom@203: // XXX: backtrace... terom@203: return nexus_lua_error(L, ret, err); terom@203: } terom@203: } terom@203: terom@203: struct lua_thread_start_ctx { terom@203: struct lua_thread *thread; terom@203: const char *chunk; terom@203: }; terom@203: terom@203: /** terom@203: * Setup the given newthread state, context, and load/run the given chunk terom@203: */ terom@203: static int lua_thread_setup (lua_State *L) terom@203: { terom@203: struct lua_thread_start_ctx *ctx; terom@203: error_t err; terom@203: terom@203: // read the ctx argument off the stack terom@203: if ((ctx = lua_touserdata(L, 1)) == NULL) terom@203: luaL_error(L, "lua_touserdata"); terom@203: terom@203: // push the thread state as the key terom@203: lua_pushthread(L); terom@203: terom@203: // push the lua_thread as the value terom@203: lua_pushlightuserdata(L, ctx->thread); terom@203: terom@203: // store L -> lua_thread mapping in the registry terom@203: lua_settable(L, LUA_REGISTRYINDEX); terom@203: terom@203: // clean up the stack terom@203: lua_pop(L, 1); terom@203: terom@203: // load the chunk as a lua function into the thread's state terom@203: if (nexus_lua_error(L, luaL_loadstring(L, ctx->chunk), &err)) terom@203: luaL_error(L, "%s", error_msg(&err)); terom@203: terom@203: // initial resume on the loaded chunk terom@203: if (lua_thread_resume(L, ctx->thread, &err)) terom@203: luaL_error(L, "%s", error_msg(&err)); terom@203: terom@203: // ok terom@203: return 0; terom@203: } terom@203: terom@203: static int _lua_thread_start (lua_State *L) terom@203: { terom@203: struct lua_thread_start_ctx *ctx; terom@203: lua_State *TL; terom@203: error_t err; terom@203: terom@203: // read the ctx argument off the stack terom@203: if ((ctx = lua_touserdata(L, 1)) == NULL) terom@203: luaL_error(L, "lua_touserdata"); terom@203: terom@203: // create new thread terom@203: TL = lua_newthread(L); terom@203: terom@203: // create thread and do setup on it terom@203: if (nexus_lua_error(TL, lua_cpcall(TL, lua_thread_setup, ctx), &err)) { terom@203: // cleanup stack (ctx, TL, error message) terom@203: lua_pop(L, 3); terom@203: terom@203: // pass on error terom@203: luaL_error(L, "lua_thread_setup: %s", error_msg(&err)); terom@203: } terom@203: terom@203: // drop the thread for GC, we don't need it, and also our ctx arg terom@203: lua_pop(L, 2); terom@203: terom@203: // store terom@203: ctx->thread->L = TL; terom@203: terom@203: return 0; terom@203: } terom@203: terom@203: err_t lua_thread_start (struct lua_thread *thread, const char *chunk, error_t *err) terom@203: { terom@203: struct lua_thread_start_ctx ctx = { thread, chunk }; terom@203: terom@203: // sanity-check terom@203: assert(!thread->L); terom@203: terom@203: // use protected mode to setup terom@203: if (nexus_lua_error(thread->lua->st, lua_cpcall(thread->lua->st, _lua_thread_start, &ctx), err)) terom@203: return ERROR_CODE(err); terom@203: terom@203: // ok terom@203: return SUCCESS; terom@203: } terom@203: terom@203: void lua_thread_resume_state (lua_State *L) terom@203: { terom@203: struct lua_thread *thread; terom@203: error_t err; terom@203: terom@203: // lookup the thread's context terom@203: lua_pushthread(L); terom@203: lua_gettable(L, LUA_REGISTRYINDEX); terom@203: terom@203: // get it as a pointer terom@203: if ((thread = lua_touserdata(L, -1)) == NULL) terom@203: luaL_error(L, "lua_thread_resume_state: lua_touserdata"); terom@203: terom@203: // handle the resume terom@203: if (lua_thread_resume(L, thread, &err)) terom@203: // notify user terom@203: lua_thread_done(thread->L, thread, &err); terom@203: } terom@203: