/* * Returns: [thread_ludata] */ static int thread_init (lua_State *L) { struct sys_thread *td; /* TLS Index */ if (g_TLSIndex == INVALID_TLS_INDEX) { #ifndef _WIN32 const int res = pthread_key_create(&g_TLSIndex, NULL); if (res) { errno = res; goto err; } #else if ((g_TLSIndex = TlsAlloc()) == INVALID_TLS_INDEX) goto err; #endif } /* VM Mutex */ td = sys_get_thread(); if (!td) { if (vmthread_new(L, (void *) &td)) goto err; thread_settable(L, L, td->tid); sys_set_thread(td); sys_vm2_enter(td); } lua_pushlightuserdata(L, td->vmtd); return 1; err: return sys_seterror(L, 0); }
void sys_vm_leave (void) { if (g_TLSIndex == INVALID_TLS_INDEX) return; sys_vm2_leave(sys_get_thread()); }
/* * Arguments: dpool_udata, data_items (any) ... */ static int dpool_put (lua_State *L) { struct sys_thread *td = sys_get_thread(); struct data_pool *dp = checkudata(L, 1, DPOOL_TYPENAME); int nput = lua_gettop(L) - 1; if (!td) luaL_argerror(L, 0, "Threading not initialized"); if (!nput) luaL_argerror(L, 2, "data expected"); lua_getfenv(L, 1); /* storage */ lua_insert(L, 1); if (dp->n >= dp->max) { if (dp->flags & DPOOL_PUTONFULL) { lua_pushlightuserdata(L, (void *) DPOOL_PUTONFULL); lua_rawget(L, 1); lua_insert(L, 2); lua_call(L, 1 + nput, LUA_MULTRET); nput = lua_gettop(L) - 1; if (!nput) return 0; } else do { if (thread_event_wait(&dp->tev, TIMEOUT_INFINITE)) return sys_seterror(L, 0); } while (dp->n >= dp->max); } /* Try directly move data between threads */ if (dp->nwaits && !dp->td) { dp->td = td; dp->nput = nput; thread_event_signal(&dp->tev); thread_yield(L); dp->td = NULL; if (!dp->nput) return 0; /* moved to thread */ dp->nput = 0; } /* Keep data in the storage */ { int top = dp->top; lua_pushinteger(L, nput); do { lua_rawseti(L, 1, ++top); } while (nput--); dp->top = top; /* notify event_queue */ if (!dp->n++ && dp->trigger) sys_trigger_notify(&dp->trigger, SYS_EVREAD); thread_event_signal(&dp->tev); } return 0; }
/* * Returns: [thread_ludata, main thread_ludata] */ static int thread_self (lua_State *L) { struct sys_thread *td = sys_get_thread(); if (!td) return 0; lua_pushlightuserdata(L, td); lua_pushlightuserdata(L, td->vmtd); return 2; }
void sys_vm_enter (void) { struct sys_thread *td; if (g_TLSIndex == INVALID_TLS_INDEX) return; td = sys_get_thread(); sys_vm2_enter(td); if (td && td->interrupted) { lua_pushlightuserdata(td->L, &g_TLSIndex); lua_error(td->L); } }
static int channel_put (lua_State *L) { struct sys_thread *td = sys_get_thread(); struct channel *chan = checkudata(L, 1, CHANNEL_TYPENAME); int nput = lua_gettop(L) - 1; if (!td) luaL_argerror(L, 0, "Threading not initialized"); if (!nput) luaL_argerror(L, 2, "data expected"); lua_getfenv(L, 1); /* get the storage table */ lua_insert(L, 1); sys_vm_leave(); thread_critsect_enter(&chan->mutex); thread_cond_signal(&chan->put); /* move the data to storage */ { int top = chan->top; lua_pushinteger(L, nput); do { lua_rawseti(L, 1, ++top); } while (nput--); chan->top = top; chan->n++; } while (chan->n > chan->max) { thread_cond_wait(&chan->get, &chan->mutex, TIMEOUT_INFINITE); } thread_critsect_leave(&chan->mutex); sys_vm_enter(); return 0; }
/* * Arguments: filename (string) | function_dump (string), * [arguments (string | number | boolean | lightuserdata) ...] * Returns: [thread_ludata] */ static int thread_runvm (lua_State *L) { const char *path = luaL_checkstring(L, 1); lua_State *NL = NULL; struct sys_vmthread *vmtd = (struct sys_vmthread *) sys_get_thread(); #ifndef _WIN32 pthread_attr_t attr; #else HANDLE hThr; #endif int res = 0; if (!vmtd) luaL_argerror(L, 0, "Threading not initialized"); NL = luaL_newstate(); if (!NL) goto err; thread_openlibs(NL); if (path[0] == LUA_SIGNATURE[0] ? luaL_loadbuffer(NL, path, lua_rawlen(L, 1), "thread") : luaL_loadfile(NL, path)) { lua_pushstring(L, lua_tostring(NL, -1)); /* error message */ lua_close(NL); lua_error(L); } /* Arguments */ lua_pushlightuserdata(NL, vmtd); /* master */ { int i, top = lua_gettop(L); for (i = 2; i <= top; ++i) { switch (lua_type(L, i)) { case LUA_TSTRING: lua_pushstring(NL, lua_tostring(L, i)); break; case LUA_TNUMBER: lua_pushnumber(NL, lua_tonumber(L, i)); break; case LUA_TBOOLEAN: lua_pushboolean(NL, lua_toboolean(L, i)); break; case LUA_TLIGHTUSERDATA: case LUA_TUSERDATA: lua_pushlightuserdata(NL, lua_touserdata(L, i)); break; default: luaL_argerror(L, i, "primitive type expected"); } } } if (vmthread_new(NL, &vmtd)) goto err_clean; #ifndef _WIN32 res = pthread_attr_init(&attr); if (res) goto err_clean; pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); res = pthread_create(&vmtd->td.tid, &attr, (thread_func_t) thread_startvm, vmtd); pthread_attr_destroy(&attr); if (!res) { #else hThr = (HANDLE) _beginthreadex(NULL, 0, (thread_func_t) thread_startvm, vmtd, 0, &vmtd->td.tid); if (hThr) { CloseHandle(hThr); #endif lua_pushlightuserdata(L, vmtd); return 1; } err_clean: lua_close(NL); err: return sys_seterror(L, res); } /* * Arguments: thread_ludata */ static int thread_interrupt (lua_State *L) { struct sys_thread *td = lua_touserdata(L, 1); td->interrupted = 1; #ifndef _WIN32 pthread_kill(td->tid, SYS_SIGINTR); #endif return 0; } static int thread_yield (lua_State *L) { (void) L; sys_vm_leave(); #ifndef _WIN32 sched_yield(); #else Sleep(0); #endif sys_vm_enter(); return 0; }
/* * Arguments: function, [arguments (any) ...] * Returns: [thread_ludata] */ static int thread_run (lua_State *L) { struct sys_thread *vmtd = sys_get_thread(); lua_State *NL; struct sys_thread *td; #ifndef _WIN32 pthread_attr_t attr; int res = 0; #else HANDLE hThr; const int res = 0; #endif if (!vmtd) luaL_argerror(L, 0, "Threading not initialized"); luaL_checktype(L, 1, LUA_TFUNCTION); NL = lua_newthread(L); if (!NL) goto err; td = lua_newuserdata(L, sizeof(struct sys_thread)); memset(td, 0, sizeof(struct sys_thread)); td->mutex = vmtd->mutex; td->L = NL; td->vmtd = vmtd->vmtd; #ifndef _WIN32 if ((res = pthread_attr_init(&attr)) || (res = pthread_attr_setstacksize(&attr, THREAD_STACK_SIZE))) goto err; pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); res = pthread_create(&td->tid, &attr, (thread_func_t) thread_start, td); pthread_attr_destroy(&attr); if (!res) { #else hThr = (HANDLE) _beginthreadex(NULL, THREAD_STACK_SIZE, (thread_func_t) thread_start, td, 0, &td->tid); if (hThr) { CloseHandle(hThr); #endif thread_settable(L, NL, td->tid); /* save thread to avoid GC */ lua_xmove(L, NL, lua_gettop(L)); /* move function and args to NL */ lua_pushlightuserdata(L, td); return 1; } err: return sys_seterror(L, res); } /* * Arguments: function, master (thread_ludata), * [arguments (string | number | boolean | lightuserdata) ...], * thread, thread_udata */ static THREAD_FUNC_API thread_startvm (struct sys_thread *td) { lua_State *L = td->L; thread_settable(L, L, td->tid); sys_set_thread(td); sys_vm2_enter(td); if (lua_pcall(L, lua_gettop(L) - 1, 0, 0)) { if (td->interrupted && lua_touserdata(L, -1) == &g_TLSIndex) lua_pop(L, 1); else lua_error(L); } /* notify event_queue */ if (td->trigger) sys_trigger_notify(&td->trigger, SYS_EVEOF | SYS_EVDEL); lua_close(L); return 0; }