src/lua_thread.c
branchnew-lib-errors
changeset 218 5229a5d098b2
parent 217 7728d6ec3abf
child 219 cefec18b8268
equal deleted inserted replaced
217:7728d6ec3abf 218:5229a5d098b2
     1 #include "lua_thread.h"
       
     2 #include "log.h"
       
     3 
       
     4 #include <string.h>
       
     5 #include <assert.h>
       
     6 
       
     7 #include <lua5.1/lauxlib.h>
       
     8 
       
     9 
       
    10 void lua_thread_init (struct lua_thread *thread, struct nexus_lua *lua, lua_thread_cb cb_func, void *cb_arg)
       
    11 {
       
    12     // zero
       
    13     memset(thread, 0, sizeof(*thread));
       
    14 
       
    15     // store
       
    16     thread->lua = lua;
       
    17     thread->cb_func = cb_func;
       
    18     thread->cb_arg = cb_arg;
       
    19 }
       
    20 
       
    21 /**
       
    22  * Thread completed, cleanup and call user callback with given error (NULL for success).
       
    23  */
       
    24 static void lua_thread_done (struct lua_thread *thread, error_t *err)
       
    25 {
       
    26     // remove from registry so thread can be GC'd
       
    27     // XXX: unsafe
       
    28     lua_pushthread(thread->L);
       
    29     lua_pushnil(thread->L);
       
    30     lua_settable(thread->L, LUA_REGISTRYINDEX);
       
    31 
       
    32     // drop our ref
       
    33     thread->L = NULL;
       
    34 
       
    35     // call
       
    36     thread->cb_func(err, thread->cb_arg);
       
    37 }
       
    38 
       
    39 /**
       
    40  * Execute the lua_resume with zero arguments. If the thread finished, this returns zero, if it yielded, >0. For
       
    41  * errors, returns -err_t.
       
    42  */
       
    43 static int lua_thread_resume (lua_State *L, struct lua_thread *thread, error_t *err)
       
    44 {
       
    45     int ret;
       
    46 
       
    47     (void) thread;
       
    48     
       
    49     // invoke it
       
    50     switch ((ret = lua_resume(L, 0))) {
       
    51         case LUA_YIELD:
       
    52             // ok, running, wait
       
    53             return 1;
       
    54 
       
    55         case 0:
       
    56             // done
       
    57             return 0;
       
    58 
       
    59         default:
       
    60             // let caller handle
       
    61             // XXX: backtrace...
       
    62             return -nexus_lua_error(L, ret, err);
       
    63     }
       
    64 }
       
    65 
       
    66 struct lua_thread_start_ctx {
       
    67     struct lua_thread *thread;
       
    68     const char *chunk;
       
    69 };
       
    70 
       
    71 /**
       
    72  * Create a new thread, set it up, and run the initial resume, returning the boolean result of that
       
    73  * (true for yielded, false for done).
       
    74  */
       
    75 static int _lua_thread_start (lua_State *L)
       
    76 {
       
    77     struct lua_thread_start_ctx *ctx;
       
    78     lua_State *TL;
       
    79     error_t err;
       
    80     int ret;
       
    81 
       
    82     // read the ctx argument off the stack
       
    83     if ((ctx = lua_touserdata(L, 1)) == NULL)
       
    84         return luaL_error(L, "lua_touserdata");
       
    85     else
       
    86         lua_pop(L, 1);
       
    87     
       
    88     // check
       
    89     assert(!ctx->thread->L);
       
    90 
       
    91     // create new thread
       
    92     TL = lua_newthread(L);
       
    93  
       
    94     // push the lua_thread as the value
       
    95     lua_pushlightuserdata(L, ctx->thread);
       
    96 
       
    97     // store L -> lua_thread mapping in the registry
       
    98     lua_settable(L, LUA_REGISTRYINDEX);
       
    99 
       
   100     // load the chunk as a lua function into the thread's state
       
   101     if (nexus_lua_error(TL, luaL_loadstring(TL, ctx->chunk), &err))
       
   102         goto error;
       
   103 
       
   104     // initial resume on the loaded chunk
       
   105     if ((ret = lua_thread_resume(TL, ctx->thread, &err)) < 0)
       
   106         goto error;
       
   107 
       
   108     // yielded?
       
   109     if (ret) 
       
   110         // store
       
   111         ctx->thread->L = TL;
       
   112     
       
   113     // pop thread to release for GC
       
   114     lua_pop(L, 1);
       
   115 
       
   116     return 0;
       
   117         
       
   118 error:
       
   119     // pop thread to release for GC
       
   120     lua_pop(L, 1);
       
   121 
       
   122     // drop ref
       
   123     ctx->thread->L = NULL;
       
   124 
       
   125     // pass on error
       
   126     return luaL_error(L, "%s", error_msg(&err));
       
   127 }
       
   128 
       
   129 int lua_thread_start (struct lua_thread *thread, const char *chunk, error_t *err)
       
   130 {
       
   131     struct lua_thread_start_ctx ctx = { thread, chunk };
       
   132 
       
   133     // sanity-check
       
   134     assert(!thread->L);
       
   135 
       
   136     // use protected mode to setup
       
   137     if (nexus_lua_error(thread->lua->st, lua_cpcall(thread->lua->st, _lua_thread_start, &ctx), err))
       
   138         return -ERROR_CODE(err);
       
   139 
       
   140     // return true if yielded, false if done
       
   141     return (thread->L != NULL);
       
   142 }
       
   143 
       
   144 int lua_thread_yield_state (lua_State *L)
       
   145 {
       
   146     return lua_yield(L, 0);
       
   147 }
       
   148 
       
   149 static int _lua_thread_resume_state (lua_State *L)
       
   150 {
       
   151     struct lua_thread *thread, **thread_ptr;
       
   152 
       
   153     // read the ctx argument off the stack
       
   154     if ((thread_ptr = lua_touserdata(L, 1)) == NULL)
       
   155         return luaL_error(L, "lua_touserdata");
       
   156     else
       
   157         lua_pop(L, 1);
       
   158 
       
   159     // lookup the thread's context
       
   160     lua_pushthread(L);
       
   161     lua_gettable(L, LUA_REGISTRYINDEX);
       
   162     
       
   163     // not registered?
       
   164     if (lua_isnil(L, -1))
       
   165         return luaL_error(L, "not a registered lua_thread state");
       
   166 
       
   167     else if (lua_isboolean(L, -1) && !lua_toboolean(L, -1))
       
   168         return luaL_error(L, "lua_thread was aborted");
       
   169 
       
   170     // get it as a pointer
       
   171     if ((thread = lua_touserdata(L, -1)) == NULL)
       
   172         return luaL_error(L, "lua_touserdata");
       
   173     else
       
   174         lua_pop(L, 1);
       
   175 
       
   176     // store it
       
   177     *thread_ptr = thread;
       
   178 
       
   179     // ok
       
   180     return 0;
       
   181 }
       
   182 
       
   183 void lua_thread_resume_state (lua_State *L)
       
   184 {
       
   185     struct lua_thread *thread = NULL;
       
   186     error_t err;
       
   187     int ret;
       
   188 
       
   189     // execute in protected mode
       
   190     if (nexus_lua_error(L, lua_cpcall(L, _lua_thread_resume_state, &thread), &err))
       
   191         return log_warn_error(&err, "%p", L);
       
   192 
       
   193     // handle the resume
       
   194     if ((ret = lua_thread_resume(L, thread, &err)) < 0)
       
   195         // notify user
       
   196         lua_thread_done(thread, &err);
       
   197 
       
   198     // finished?
       
   199     else if (ret == 0)
       
   200         lua_thread_done(thread, NULL);
       
   201 
       
   202 }
       
   203 
       
   204 static int _lua_thread_abort (lua_State *L)
       
   205 {
       
   206     struct lua_thread *thread;
       
   207 
       
   208     // get context arg
       
   209     if ((thread = lua_touserdata(L, -1)) == NULL)
       
   210         luaL_error(L, "lua_thread_resume_state: lua_touserdata");
       
   211     else
       
   212         lua_pop(L, 1);
       
   213 
       
   214     // no mechanism to actually abort the underlying wait yet
       
   215     (void) thread;
       
   216 
       
   217     // invalidate in registry for later lua_thread_resume_state
       
   218     lua_pushthread(L);
       
   219     lua_pushboolean(L, false);
       
   220     lua_settable(L, LUA_REGISTRYINDEX);
       
   221 
       
   222     // not much else we can do
       
   223     return 0;
       
   224 }
       
   225 
       
   226 void lua_thread_abort (struct lua_thread *thread)
       
   227 {
       
   228     error_t err;
       
   229 
       
   230     if (!thread->L)
       
   231         // no thread executing
       
   232         return;
       
   233     
       
   234     // use protected mode
       
   235     if (nexus_lua_error(thread->L, lua_cpcall(thread->L, _lua_thread_abort, thread), &err))
       
   236         log_warn_error(&err, "_lua_thread_abort");
       
   237 
       
   238     // unset
       
   239     thread->L = NULL;
       
   240 }
       
   241