/* * Arguments: evq_udata, ev_ludata, [callback (function)] * Returns: evq_udata | callback (function) */ static int levq_callback (lua_State *L) { struct event_queue *evq = checkudata(L, 1, EVQ_TYPENAME); struct event *ev = levq_toevent(L, 2); lua_State *NL = evq->L; const int top = lua_gettop(L); lua_assert(ev && !event_deleted(ev)); if (top < 3) { lua_rawgeti(NL, EVQ_CORO_CALLBACK, ev->ev_id); lua_xmove(NL, L, 1); } else { ev->flags &= ~(EVENT_CALLBACK | EVENT_CALLBACK_CORO); if (!lua_isnoneornil(L, 3)) { ev->flags |= EVENT_CALLBACK | (lua_isthread(L, 3) ? EVENT_CALLBACK_CORO : 0); } lua_settop(L, 3); lua_xmove(L, NL, 1); lua_rawseti(NL, EVQ_CORO_CALLBACK, ev->ev_id); lua_settop(L, 1); } return 1; }
/* * Arguments: evq_udata, ev_ludata, [reuse_fd (boolean)] * Returns: [evq_udata] */ static int levq_del (lua_State *L) { struct event_queue *evq = checkudata(L, 1, EVQ_TYPENAME); struct event *ev = levq_toevent(L, 2); const int reuse_fd = lua_toboolean(L, 3); int res = 0; lua_assert(ev); #undef ARG_LAST #define ARG_LAST 1 #ifdef EVQ_POST_INIT if (ev == evq->ev_post) evq->ev_post = NULL; #endif if (!event_deleted(ev)) res = evq_del(ev, reuse_fd); if (!(ev->flags & (EVENT_ACTIVE | EVENT_DELETE))) levq_del_event(evq, ev); ev->flags |= EVENT_DELETE; if (!res) { lua_settop(L, 1); return 1; } return sys_seterror(L, 0); }
/* * Arguments: evq_udata, ev_ludata, [manually/auto-reset (boolean)] * Returns: [evq_udata] */ static int levq_timeout_manual (lua_State *L) { struct event_queue *evq = checkudata(L, 1, EVQ_TYPENAME); struct event *ev = levq_toevent(L, 2); const int manual = lua_toboolean(L, 3); (void) evq; lua_assert(ev && !event_deleted(ev) && !(ev->flags & EVENT_WINMSG)); if (manual) ev->flags |= EVENT_TIMEOUT_MANUAL; else ev->flags &= ~EVENT_TIMEOUT_MANUAL; lua_settop(L, 1); return 1; }
/* * Arguments: evq_udata, ev_ludata, events (string: "r", "w") * Returns: [evq_udata] */ static int levq_mod_socket (lua_State *L) { struct event *ev = levq_toevent(L, 2); const char *evstr = luaL_checkstring(L, 3); const unsigned int rw_flags = (evstr[0] == 'r') ? EVENT_READ : EVENT_WRITE; lua_assert(ev && !event_deleted(ev) && (ev->flags & EVENT_SOCKET) && rw_flags != (ev->flags & (EVENT_READ | EVENT_WRITE))); if (!evq_modify(ev, rw_flags)) { ev->flags &= ~(EVENT_READ | EVENT_WRITE); ev->flags |= rw_flags; lua_settop(L, 1); return 1; } return sys_seterror(L, 0); }
/* * Arguments: evq_udata, ev_ludata, [timeout (milliseconds)] * Returns: [evq_udata] */ static int levq_timeout (lua_State *L) { struct event_queue *evq = checkudata(L, 1, EVQ_TYPENAME); struct event *ev = levq_toevent(L, 2); const msec_t timeout = lua_isnoneornil(L, 3) ? TIMEOUT_INFINITE : (msec_t) lua_tointeger(L, 3); lua_assert(ev && !event_deleted(ev) && !(ev->flags & EVENT_WINMSG)); /* reserve place for timeout_queue */ if (!evq->ev_free) evq->ev_free = levq_new_event(evq); if (!evq_set_timeout(ev, timeout)) { lua_settop(L, 1); return 1; } return sys_seterror(L, 0); }
/* * Arguments: evq_udata */ static int levq_done (lua_State *L) { struct event_queue *evq = checkudata(L, 1, EVQ_TYPENAME); struct event *buffers[EVQ_ENV_BUF_MAX + 1]; /* cache */ lua_State *NL = evq->L; memset(buffers, 0, sizeof(buffers)); #undef ARG_LAST #define ARG_LAST 1 /* delete object events */ lua_pushnil(NL); while (lua_next(NL, EVQ_CORO_UDATA)) { const int ev_id = lua_tointeger(NL, -2); const int buf_idx = getmaxbit( (ev_id | ((1 << EVQ_ENV_BUF_IDX) - 1)) + 1); const int nmax = (1 << buf_idx); struct event *ev = buffers[buf_idx]; if (!ev) { lua_rawgeti(NL, EVQ_CORO_ENV, buf_idx - EVQ_ENV_BUF_IDX + 1); ev = lua_touserdata(NL, -1); lua_pop(NL, 1); /* pop events buffer */ buffers[buf_idx] = ev; } ev += ev_id - ((nmax - 1) & ~((1 << EVQ_ENV_BUF_IDX) - 1)); if (!event_deleted(ev)) evq_del(ev, 0); lua_pop(NL, 1); /* pop value */ } evq_done(evq); return 0; }
static void signal_handler (const int signo) { #ifdef USE_KQUEUE (void) signo; #else struct event **sig_evp; if (signo == SYS_SIGINTR) return; sig_evp = signal_gethead(signo); if (!sig_evp) return; pthread_mutex_lock(&g_Signal.cs); { struct event *ev = *sig_evp; for (; ev; ev = ev->next_object) { if (!event_deleted(ev)) evq_signal(ev->evq, signo); } } pthread_mutex_unlock(&g_Signal.cs); #endif }
/* * Arguments: evq_udata, [timeout (milliseconds), once (boolean), * fetch (boolean)] * Returns: [evq_udata | timeout (false)] * | * Returns: [ev_ludata, obj_udata, event (string: "r", "w", "t", "e"), * eof_status (number)] */ static int levq_loop (lua_State *L) { struct event_queue *evq = checkudata(L, 1, EVQ_TYPENAME); const msec_t timeout = (lua_type(L, 2) != LUA_TNUMBER) ? TIMEOUT_INFINITE : (msec_t) lua_tointeger(L, 2); const int once = lua_toboolean(L, 3); const int fetch = lua_toboolean(L, 4); #undef ARG_LAST #define ARG_LAST 1 lua_settop(L, ARG_LAST); { lua_State *NL = evq->L; lua_pushvalue(NL, EVQ_CORO_CALLBACK); lua_pushvalue(NL, EVQ_CORO_UDATA); lua_xmove(NL, L, 2); } #ifdef EVQ_POST_INIT if (evq->ev_post) { evq_post_init(evq->ev_post); evq->ev_post = NULL; } #endif while (!evq_is_empty(evq)) { struct event *ev; if (evq->stop) { evq->stop = 0; break; } /* process synchronous operations */ if (evq->sync_op) { struct evq_sync_op *op = evq->sync_op; evq->sync_op = NULL; levq_sync_process(L, evq, op); } if (!evq->ev_ready) { const int res = evq_wait(evq, timeout); if (res == EVQ_TIMEOUT) { lua_pushboolean(L, 0); return 1; } if (res == EVQ_FAILED) return sys_seterror(L, 0); } ev = evq->ev_ready; if (!ev) continue; do { const unsigned int ev_flags = ev->flags; /* clear EVENT_ACTIVE and EVENT_*_RES flags */ ev->flags &= EVENT_MASK; evq->ev_ready = ev->next_ready; if (ev_flags & EVENT_DELETE) { /* postponed deletion of active event */ levq_del_event(evq, ev); } else { if ((ev_flags & EVENT_CALLBACK) || fetch) { const int ev_id = ev->ev_id; /* callback function */ lua_rawgeti(L, ARG_LAST+1, ev_id); /* arguments */ if (!(ev_flags & EVENT_CALLBACK_SCHED)) { lua_pushvalue(L, 1); /* evq_udata */ lua_pushlightuserdata(L, ev); /* ev_ludata */ lua_rawgeti(L, ARG_LAST+2, ev_id); /* obj_udata */ } if (ev_flags & EVENT_EOF_MASK_RES) { lua_pushliteral(L, "e"); lua_pushinteger(L, (int) ev_flags >> EVENT_EOF_SHIFT_RES); } else { lua_pushstring(L, (ev_flags & EVENT_TIMEOUT_RES) ? "t" : (ev_flags & EVENT_WRITE_RES) ? "w" : "r"); lua_pushnil(L); } } if ((ev_flags & EVENT_ONESHOT) && !event_deleted(ev)) evq_del(ev, 1); if (event_deleted(ev)) levq_del_event(evq, ev); /* deletion of oneshot event */ #ifdef EVQ_POST_INIT else evq->ev_post = ev; #endif if (ev_flags & EVENT_CALLBACK_SCHED) { /* callback function: coroutine */ lua_State *co = lua_tothread(L, ARG_LAST+3); lua_xmove(L, co, 2); lua_pop(L, 1); /* pop coroutine */ sys_sched_event_ready(co, ev); } else if (ev_flags & EVENT_CALLBACK_CORO) { lua_State *co = lua_tothread(L, ARG_LAST+3); lua_xmove(L, co, 5); lua_pop(L, 1); /* pop coroutine */ switch (lua_resume(co, L, 5)) { case 0: lua_settop(co, 0); if (!event_deleted(ev)) { evq_del(ev, 0); levq_del_event(evq, ev); #ifdef EVQ_POST_INIT evq->ev_post = NULL; #endif } break; case LUA_YIELD: lua_settop(co, 0); break; default: lua_xmove(co, L, 1); /* error message */ lua_error(L); } } else if (ev_flags & EVENT_CALLBACK) lua_call(L, 5, 0); else if (fetch) return 4; #ifdef EVQ_POST_INIT if (evq->ev_post) { evq_post_init(evq->ev_post); evq->ev_post = NULL; } #endif } ev = evq->ev_ready; } while (ev);