/* * This file is part of LEM, a Lua Event Machine. * Copyright 2011 Emil Renner Berthing * * LEM is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of * the License, or (at your option) any later version. * * LEM is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with LEM. If not, see . */ #include #include #include #include #include #include #include "config.h" #include "lua/lua.h" #include "lua/lualib.h" #include "lua/lauxlib.h" #include "libev/ev.h" #include "macros.h" #if EV_USE_KQUEUE #define LEM_LOOPFLAGS EVBACKEND_KQUEUE #else #define LEM_LOOPFLAGS 0 #endif #ifdef NDEBUG #define lem_log_error(fmt, ...) fprintf(stderr, fmt "\n", ##__VA_ARGS__) #else #define lem_log_error lem_debug #endif #define LEM_INITIAL_QUEUESIZE 8 /* this must be a power of 2 */ #define LEM_THREADTABLE 1 struct lem_runqueue_slot { lua_State *T; int nargs; }; struct lem_runqueue { struct ev_idle w; unsigned long first; unsigned long last; unsigned long mask; struct lem_runqueue_slot *queue; int status; unsigned error : 1; }; #if EV_MULTIPLICITY struct ev_loop *lem_loop; #endif static lua_State *L; static struct lem_runqueue rq; static void oom() { static const char e[] = "Out of memory\n"; fprintf(stderr, e); #ifdef SIGQUIT raise(SIGQUIT); #endif _Exit(EXIT_FAILURE); } void * lem_xmalloc(size_t size) { void *p; p = malloc(size); if (p == NULL) oom(); return p; } static int ignore_sigpipe() { struct sigaction act; if (sigaction(SIGPIPE, NULL, &act)) { lem_log_error("error getting signal action: %s", strerror(errno)); return -1; } act.sa_handler = SIG_IGN; if (sigaction(SIGPIPE, &act, NULL)) { lem_log_error("error setting signal action: %s", strerror(errno)); return -1; } return 0; } lua_State * lem_newthread() { lua_State *T = lua_newthread(L); if (T == NULL) oom(); /* set thread_table[T] = true */ lua_pushboolean(L, 1); lua_rawset(L, LEM_THREADTABLE); return T; } void lem_forgetthread(lua_State *T) { /* set thread_table[T] = nil */ lua_pushthread(T); lua_xmove(T, L, 1); lua_pushnil(L); lua_rawset(L, LEM_THREADTABLE); } void lem_sethandler(lua_State *T) { /* push T to L */ lua_pushthread(T); lua_xmove(T, L, 1); /* move handler to L */ lua_xmove(T, L, 1); lua_rawset(L, LEM_THREADTABLE); } void lem_queue(lua_State *T, int nargs) { struct lem_runqueue_slot *slot; assert(T != NULL); lem_debug("enqueueing thread with %d argument%s", nargs, nargs == 1 ? "" : "s"); if (rq.first == rq.last) ev_idle_start(EV_G_ &rq.w); slot = &rq.queue[rq.last]; slot->T = T; slot->nargs = nargs; rq.last++; rq.last &= rq.mask; if (rq.first == rq.last) { unsigned long i; unsigned long j; struct lem_runqueue_slot *new_queue; lem_debug("expanding queue to %lu slots", 2*(rq.mask + 1)); new_queue = lem_xmalloc(2*(rq.mask + 1) * sizeof(struct lem_runqueue_slot)); i = 0; j = rq.first; do { new_queue[i] = rq.queue[j]; i++; j++; j &= rq.mask; } while (j != rq.first); free(rq.queue); rq.queue = new_queue; rq.first = 0; rq.last = i; rq.mask = 2*rq.mask + 1; } } static void runqueue_pop(EV_P_ struct ev_idle *w, int revents) { struct lem_runqueue_slot *slot; lua_State *T; int nargs; (void)revents; if (rq.first == rq.last) { /* queue is empty */ lem_debug("runqueue is empty"); ev_idle_stop(EV_A_ w); return; } lem_debug("running thread..."); slot = &rq.queue[rq.first]; T = slot->T; nargs = slot->nargs; rq.first++; rq.first &= rq.mask; /* run Lua thread */ switch (lua_resume(T, nargs)) { case 0: /* thread finished successfully */ lem_debug("thread finished successfully"); lem_forgetthread(T); return; case LUA_YIELD: /* thread yielded */ lem_debug("thread yielded"); return; case LUA_ERRERR: /* error running error handler */ lem_debug("thread errored while running error handler"); case LUA_ERRRUN: /* runtime error */ lem_debug("thread errored"); /* push T to L */ lua_pushthread(T); lua_xmove(T, L, 1); /* push thread_table[T] */ lua_pushvalue(L, -1); lua_rawget(L, LEM_THREADTABLE); if (lua_type(L, -1) == LUA_TFUNCTION) { lua_State *S = lem_newthread(); /* move error handler to S */ lua_xmove(L, S, 1); /* move error message to S */ lua_xmove(T, S, 1); /* queue thread */ lem_debug("queueing error handler: %s", lua_tostring(S, -1)); lem_queue(S, 1); /* thread_table[T] = nil */ lua_pushnil(L); lua_rawset(L, LEM_THREADTABLE); return; } lem_debug("no error handler"); /* move error message to L */ lua_xmove(T, L, 1); rq.error = 1; rq.status = EXIT_FAILURE; ev_unloop(EV_A_ EVUNLOOP_ALL); return; case LUA_ERRMEM: /* out of memory */ return oom(); default: /* this shouldn't happen */ lem_debug("lua_resume: unknown error"); lua_pushliteral(L, "unknown error"); rq.error = 1; rq.status = EXIT_FAILURE; ev_unloop(EV_A_ EVUNLOOP_ALL); return; } } void lem_exit(int status) { rq.status = status; ev_unloop(EV_G_ EVUNLOOP_ALL); } static int queue_file(int argc, char *argv[], int fidx) { lua_State *T = lem_newthread(); int i; switch (luaL_loadfile(T, argv[fidx])) { case 0: /* success */ break; case LUA_ERRMEM: oom(); default: lem_log_error("%s", lua_tostring(T, 1)); return -1; } lua_createtable(T, argc, 0); for (i = 0; i < argc; i++) { lua_pushstring(T, argv[i]); lua_rawseti(T, -2, i - fidx); } lua_setglobal(T, "arg"); lem_queue(T, 0); return 0; } int main(int argc, char *argv[]) { if (argc < 2) { lem_log_error("I need a file.."); return EXIT_FAILURE; } #if EV_MULTIPLICITY lem_loop = ev_default_loop(LEM_LOOPFLAGS); if (lem_loop == NULL) { #else if (!ev_default_loop(LEM_LOOPFLAGS)) { #endif lem_log_error("Error initializing event loop"); return EXIT_FAILURE; } if (ignore_sigpipe()) goto error; /* create main Lua state */ L = luaL_newstate(); if (L == NULL) { lem_log_error("Error initializing Lua state"); goto error; } luaL_openlibs(L); /* push thread table */ lua_newtable(L); /* initialize runqueue */ ev_idle_init(&rq.w, runqueue_pop); ev_idle_start(EV_G_ &rq.w); rq.queue = lem_xmalloc(LEM_INITIAL_QUEUESIZE * sizeof(struct lem_runqueue_slot)); rq.first = rq.last = 0; rq.mask = LEM_INITIAL_QUEUESIZE - 1; rq.error = 0; rq.status = EXIT_SUCCESS; /* load file */ if (queue_file(argc, argv, 1)) goto error; /* start the mainloop */ ev_loop(EV_G_ 0); lem_debug("event loop exited"); if (rq.error) { /* print error message */ lem_log_error("%s", lua_tostring(L, -1)); } /* shutdown Lua */ lua_close(L); /* free runqueue */ free(rq.queue); /* close default loop */ ev_default_destroy(); lem_debug("Bye o/"); return rq.status; error: if (L) lua_close(L); if (rq.queue) free(rq.queue); ev_default_destroy(); return EXIT_FAILURE; }