/* * Arguments: ..., function, [arguments (any) ...] * Returns: [results (any) ...] */ static int levq_sync_call (lua_State *L, struct event_queue *evq, struct evq_sync_op *op, const int is_sched_add, const int fn_idx) { op->L = L; op->fn_idx = fn_idx; op->status = 0; op->is_sched_add = is_sched_add; op->next = evq->sync_op; evq->sync_op = op; evq_signal(evq, EVQ_SIGEVQ); if (is_sched_add) { lua_pushlightuserdata(L, op); /* sync_op_ludata */ return 1; } else { struct sys_thread *td = sys_thread_get(); const int old_top = fn_idx - 1; if (!td) luaL_argerror(L, 0, "Threading not initialized"); op->td = td; sys_thread_suspend(td, TIMEOUT_INFINITE); /* wait result */ if (op->status) lua_error(L); return lua_gettop(L) - old_top; } }
/* * Arguments: function, [arguments (any) ...] * Returns: [thread_udata] */ static int thread_run (lua_State *L) { struct sys_thread *td, *vmtd = sys_thread_get(); if (!vmtd) luaL_argerror(L, 0, "Threading not initialized"); luaL_checktype(L, 1, LUA_TFUNCTION); td = sys_thread_new(L, vmtd, NULL, 1); if (!td) goto err; lua_insert(L, 1); /* thread_udata */ /* function and arguments */ { const int n = lua_gettop(L) - 1; luaL_checkstack(td->L, n, NULL); lua_xmove(L, td->L, n); } if (!sys_thread_create(td, 0)) { return 1; } sys_thread_del(td); err: return sys_seterror(L, 0); }
/* * Arguments: [stack_size (number)] * Returns: [boolean] */ static int thread_init (lua_State *L) { const size_t stack_size = (size_t) luaL_optinteger(L, 1, THREAD_STACK_SIZE); 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_thread_get(); if (!td) { td = thread_newvm(L, NULL, 0); if (!td) goto err; sys_thread_set(td); sys_vm2_enter(td); } td->vmtd->stack_size = stack_size; lua_pushboolean(L, 1); return 1; err: return sys_seterror(L, 0); }
/* * Arguments: dpool_udata, data_items (any) ... */ static int dpool_put (lua_State *L) { struct sys_thread *td = sys_thread_get(); 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_rawgetp(L, 1, (void *) DPOOL_PUTONFULL); lua_insert(L, 2); lua_call(L, 1 + nput, LUA_MULTRET); nput = lua_gettop(L) - 1; if (!nput) return 0; } else { do { const int res = thread_event_wait(&dp->tev, td, TIMEOUT_INFINITE); sys_thread_check(td); if (res) 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); sys_thread_switch(0); 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; if (!dp->n++) { thread_event_signal(&dp->tev); } } return 0; }
int sys_eintr (void) { #ifndef _WIN32 if (SYS_ERRNO == EINTR) { struct sys_thread *td = sys_thread_get(); return !(td && td->flags); } #endif return 0; }
/* * Returns: thread_udata, is_main (boolean) */ static int thread_self (lua_State *L) { struct sys_thread *td = sys_thread_get(); if (!td) luaL_argerror(L, 0, "Threading not initialized"); lua_rawgetp(L, LUA_REGISTRYINDEX, THREAD_KEY_ADDRESS); lua_rawgetp(L, -1, td); lua_pushboolean(L, thread_isvm(td)); return 2; }
static int thread_switch_wrap (lua_State *L) { struct sys_thread *td = sys_thread_get(); (void) L; if (td) { sys_thread_switch(td); sys_thread_check(td, L); } return 0; }
int sys_thread_suspend (struct sys_thread *td, const msec_t timeout) { int res; lua_assert(td && td == sys_thread_get()); td->suspended = 1; do { res = thread_cond_wait_vm(&td->cond, td, timeout); } while (td->suspended && !res); return res; }
void sys_vm_leave (lua_State *L) { (void) L; if (g_TLSIndex != INVALID_TLS_INDEX) { struct sys_thread *td = sys_thread_get(); if (td) { sys_vm2_preleave(td); thread_critsect_leave(td->vmcsp); } } }
void sys_vm_enter (lua_State *L) { if (g_TLSIndex != INVALID_TLS_INDEX) { struct sys_thread *td = sys_thread_get(); if (td) { thread_critsect_enter(td->vmcsp); sys_vm2_postenter(td); if (td->flags) sys_thread_check(td, L); } } }
/* * Arguments: [timeout (milliseconds)] * Returns: [boolean] */ static int thread_suspend_wrap (lua_State *L) { struct sys_thread *td = sys_thread_get(); const msec_t timeout = lua_isnoneornil(L, 1) ? TIMEOUT_INFINITE : (msec_t) lua_tointeger(L, 1); const int res = sys_thread_suspend(td, timeout); if (res >= 0) { if (res == 1) { lua_pushboolean(L, 0); return 1; /* timed out */ } lua_pushboolean(L, 1); return 1; } return sys_seterror(L, 0); }
/* * Arguments: thread_udata, * [success/failure (boolean) | status (number)] */ static int thread_set_terminate (lua_State *L) { struct sys_thread *td = checkudata(L, 1, THREAD_TYPENAME); const lua_Integer status = !lua_isboolean(L, 2) ? lua_tointeger(L, 2) : (lua_toboolean(L, 2) ? EXIT_SUCCESS : EXIT_FAILURE); td = thread_unfake(td); td->exit_status = status; td->flags = SYS_THREAD_TERMINATE; if (td == sys_thread_get()) thread_exit(td); else (void) thread_cancel_syncio(td->tid); return 0; }
/* * Arguments: thread_udata, [recover/interrupt (boolean)] */ static int thread_set_interrupt (lua_State *L) { struct sys_thread *td = checkudata(L, 1, THREAD_TYPENAME); const int recover = lua_toboolean(L, 2); td = thread_unfake(td); if (recover) { td->flags &= ~SYS_THREAD_INTERRUPT; return 0; } td->flags = SYS_THREAD_INTERRUPT; if (td == sys_thread_get()) sys_thread_check(td, L); else (void) thread_cancel_syncio(td->tid); return 0; }
/* * Arguments: ecb_udata, [membuf_udata, count (number)] * Returns: [string | count (number)] */ static int ecb_read (lua_State *L) { LPEXTENSION_CONTROL_BLOCK ecb = lua_unboxpointer(L, 1, ECB_TYPENAME); size_t n = !lua_isnumber(L, -1) ? ~((size_t) 0) : (size_t) lua_tointeger(L, -1); const size_t len = n; /* how much total to read */ size_t rlen; /* how much to read */ int nr; /* number of bytes actually read */ struct sys_thread *td = sys_thread_get(); struct sys_buffer sb; char buf[SYS_BUFSIZE]; int res = 0; sys_buffer_write_init(L, 2, &sb, buf, sizeof(buf)); do { rlen = (n <= sb.size) ? n : sb.size; if (td) sys_vm2_leave(td); { DWORD l; nr = ecb->ReadClient(ecb->ConnID, sb.ptr.w, &l) ? (int) l : -1; } if (td) sys_vm2_enter(td); if (nr == -1) break; n -= nr; /* still have to read `n' bytes */ } while ((n != 0L && nr == (int) rlen) /* until end of count or eof */ && sys_buffer_write_next(L, &sb, buf, 0)); if (nr <= 0 && len == n) { res = -1; } else { if (!sys_buffer_write_done(L, &sb, buf, nr)) lua_pushinteger(L, len - n); } if (td) sys_thread_check(td); if (!res) return 1; return sys_seterror(L, 0); }
/* * Arguments: dpool_udata, [timeout (milliseconds)] * Returns: data_items (any) ... */ static int dpool_get (lua_State *L) { struct sys_thread *td = sys_thread_get(); struct data_pool *dp = checkudata(L, 1, DPOOL_TYPENAME); const msec_t timeout = lua_isnoneornil(L, 2) ? TIMEOUT_INFINITE : (msec_t) lua_tointeger(L, 2); int nput; if (!td) luaL_argerror(L, 0, "Threading not initialized"); lua_settop(L, 1); lua_getfenv(L, 1); /* storage */ lua_insert(L, 1); if ((dp->flags & DPOOL_GETONEMPTY) && !dp->n) { lua_rawgetp(L, 1, (void *) DPOOL_GETONEMPTY); lua_insert(L, 2); lua_call(L, 1, LUA_MULTRET); nput = lua_gettop(L) - 1; if (nput) return nput; } for (; ; ) { /* get from storage */ if (dp->n) { const int idx = dp->idx + 1; int i; lua_rawgeti(L, 1, idx); nput = lua_tointeger(L, -1); lua_pushnil(L); lua_rawseti(L, 1, idx); dp->idx = idx + nput; for (i = dp->idx; i > idx; --i) { lua_rawgeti(L, 1, i); lua_pushnil(L); lua_rawseti(L, 1, i); } if (dp->idx == dp->top) dp->idx = dp->top = 0; if (dp->n-- == dp->max) { thread_event_signal(&dp->tev); } return nput; } /* wait signal */ { int res; dp->nwaits++; res = thread_event_wait(&dp->tev, td, timeout); dp->nwaits--; sys_thread_check(td); if (res) { if (res == 1) { lua_pushboolean(L, 0); return 1; /* timed out */ } return sys_seterror(L, 0); } } /* get directly from another thread */ nput = dp->nput; if (nput) { dp->nput = 0; luaL_checkstack(L, nput, NULL); lua_xmove(dp->td->L, L, nput); return nput; } } }
/* * Arguments: pipe_udata, message_items (any) ... * Returns: [pipe_udata | timedout (false)] */ static int pipe_put (lua_State *L) { struct sys_thread *td = sys_thread_get(); struct pipe_ref *pr = checkudata(L, 1, PIPE_TYPENAME); struct pipe *pp = pr->pipe; thread_critsect_t *csp = pipe_critsect_ptr(pp); struct message msg; if (!td) luaL_argerror(L, 0, "Threading not initialized"); pipe_msg_build(L, &msg, 2); /* construct the message */ /* write message to buffer */ thread_critsect_enter(csp); for (; ; ) { struct pipe_buf *pb = pp->wbuf; struct pipe_buf buf = *pb; const int wrapped = (buf.end < buf.begin); const unsigned int len = (wrapped ? buf.begin : buf.len) - buf.end; if (msg.size > len) { if (!wrapped && buf.begin > msg.size) { /* wrap the buffer */ struct message *mp = pipe_buf_ptr(pb, buf.end); mp->size = 0; /* sentinel tag */ buf.end = 0; } else if (pb != buf.next_buf && !buf.next_buf->begin && !buf.next_buf->end) { /* use next free buffer */ pb = pp->wbuf = buf.next_buf; buf = *pb; } else { const unsigned int buf_size = pp->buf_size; struct pipe_buf *wpb = (2 * buf_size <= pp->buf_max_size) ? malloc(buf_size) : NULL; if (wpb) { /* allocate new buffer */ buf.begin = buf.end = 0; buf.len = buf_size - PIPE_BUF_EXTRASIZE; /* buf->next_buf is already correct */ pb->next_buf = wpb; pp->buf_size = buf_size * 2; pp->wbuf = wpb; pb = wpb; } else { /* wait `get' signal */ int res; pp->signal_on_get++; res = pipe_cond_wait(&pp->get_cond, csp, td, pr->put_timeout); if (--pp->signal_on_get) { (void) thread_cond_signal(&pp->get_cond); } if (!res) continue; thread_critsect_leave(csp); sys_thread_check(td); if (res == 1) { lua_pushboolean(L, 0); return 1; /* timed out */ } return sys_seterror(L, 0); } } } memcpy(pipe_buf_ptr(pb, buf.end), &msg, msg.size); buf.end += msg.size; *pb = buf; pp->nmsg++; break; } if (pp->signal_on_put) { (void) thread_cond_signal(&pp->put_cond); } thread_critsect_leave(csp); lua_settop(L, 1); return 1; }
/* * Arguments: pipe_udata, [timeout (milliseconds)] * Returns: [pipe_udata | timedout (false), message_items (any) ...] */ static int pipe_get (lua_State *L) { struct sys_thread *td = sys_thread_get(); struct pipe *pp = lua_unboxpointer(L, 1, PIPE_TYPENAME); const msec_t timeout = lua_isnoneornil(L, 2) ? TIMEOUT_INFINITE : (msec_t) lua_tointeger(L, 2); thread_critsect_t *csp = pipe_critsect_ptr(pp); struct message msg; if (!td) luaL_argerror(L, 0, "Threading not initialized"); /* read message from buffer */ thread_critsect_enter(csp); for (; ; ) { if (pp->nmsg) { struct pipe_buf *pb = pp->rbuf; struct pipe_buf buf = *pb; struct message *mp = pipe_buf_ptr(pb, buf.begin); if (!mp->size) { /* buffer is wrapped */ mp = pipe_buf_ptr(pb, 0); buf.begin = 0; } memcpy(&msg, mp, mp->size); buf.begin += mp->size; if (buf.begin == buf.end) { buf.begin = buf.end = 0; if (pp->nmsg > 1) pp->rbuf = buf.next_buf; } else if (buf.begin == buf.len) { buf.begin = 0; } *pb = buf; if (--pp->nmsg && pp->signal_on_put) { (void) thread_cond_signal(&pp->put_cond); } } else { /* wait `put' signal */ int res; pp->signal_on_put++; res = pipe_cond_wait(&pp->put_cond, csp, td, timeout); pp->signal_on_put--; if (!res) continue; thread_critsect_leave(csp); sys_thread_check(td); if (res == 1) { lua_pushboolean(L, 0); return 1; /* timed out */ } return sys_seterror(L, 0); } break; } if (pp->signal_on_get) { (void) thread_cond_signal(&pp->get_cond); } thread_critsect_leave(csp); lua_settop(L, 1); return 1 + pipe_msg_parse(L, &msg); /* deconstruct the message */ }
/* * Arguments: options (table: {1..n: library names, "cpu": number}), * filename (string) | function_dump (string), * [arguments (string | number | boolean | ludata | share_object) ...] * Returns: [thread_udata] */ static int thread_runvm (lua_State *L) { const char *path = luaL_checkstring(L, 2); struct sys_thread *vmtd = sys_thread_get(); struct sys_thread *td, *faketd; lua_State *NL; unsigned int loadlibs = ~0U; /* load all standard libraries */ int is_affin = 0, cpu = 0; if (!vmtd) luaL_argerror(L, 0, "Threading not initialized"); /* options */ if (lua_istable(L, 1)) { unsigned int libs = 0; int i; for (i = 1; ; ++i) { const char *s; lua_rawgeti(L, 1, i); s = lua_tostring(L, -1); if (!s || !*s) { if (s) loadlibs = 0; /* don't load any libraries */ lua_pop(L, 1); break; } libs |= 1 << luaL_checkoption(L, -1, NULL, stdlib_names); lua_pop(L, 1); } if (libs) loadlibs = libs; /* CPU affinity */ lua_getfield(L, 1, "cpu"); if (lua_type(L, -1) == LUA_TNUMBER) { cpu = (int) lua_tointeger(L, -1); is_affin = 1; } lua_pop(L, 1); } td = thread_newvm(NULL, vmtd, loadlibs); if (!td) goto err; faketd = sys_thread_new(L, vmtd, td, 1); if (!faketd) goto err; lua_replace(L, 1); /* fake thread_udata */ if (is_affin) td->vmtd->cpu = cpu; NL = td->L; /* function */ if (path[0] == LUA_SIGNATURE[0] ? luaL_loadbuffer(NL, path, lua_rawlen(L, ARG_LAST), "thread") : luaL_loadfile(NL, path)) { lua_pushstring(L, lua_tostring(NL, -1)); /* error message */ lua_close(NL); lua_error(L); } /* arguments */ { int i, top = lua_gettop(L); luaL_checkstack(NL, top + LUA_MINSTACK, "too many arguments"); for (i = ARG_LAST + 1; i <= top; ++i) { switch (lua_type(L, i)) { case LUA_TSTRING: { size_t len; const char *s = lua_tolstring(L, i, &len); lua_pushlstring(NL, s, len); } 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: lua_pushlightuserdata(NL, lua_touserdata(L, i)); break; case LUA_TUSERDATA: if (!luaL_getmetafield(L, i, THREAD_XDUP_TAG)) luaL_argerror(L, i, "shareable object expected"); lua_pushvalue(L, i); lua_pushlightuserdata(L, NL); lua_call(L, 2, 0); break; case LUA_TNIL: lua_pushnil(NL); break; default: luaL_argerror(L, i, "primitive type expected"); } } } if (!sys_thread_create(td, is_affin)) { faketd->tid = td->tid; lua_settop(L, 1); return 1; } lua_close(NL); err: return sys_seterror(L, 0); }