summaryrefslogblamecommitdiffstats
path: root/bin/lem.c
blob: d1f9747ff7f8deeec7c5f0bee44ef16a9188db84 (plain) (tree)
1
2
3
4
5
6
7
8
9

                                                 
                                           
  

                                                                 


                                                                 

                                                             
                                                                
                                                      
  

                                                                       




                   
                   


                   


                     
 
                
                   
 
                 
                                                           
     
                                      

      





                                                                        



                
                                                               

                         
                          





                         
                                        


                           






                              
                                      
 

           
 
                                                  




















                            
                                                      


                             


                                  
 


                                                                 






                          
                   























                                        






                                     

                                  
                                       




                                                         
                                
                                          







                                  

                               

                                                    
                                                                          

















                                                                    



           








                                              

                                                  
                                       




                      
                                                       






                                                             
                                      

                                            




                                       





                                   

                            
                                             
                                                       
                                                          








                                                                        

                                                      

                                            
                                
                      

                                            
                      


                                                       
                                                    
                      
         
                               

 

                 



                                            
                             

              


                                      
                                                   

                                             
                                  





                        
                                                             













                                             








                                                  


                            
                   
                                                  

                               
                                              
      
                                                                    


                                    




                                                                       




                                   
                                                                   







                                 
                             
                                  



                                                            
 
                                   
                          
                                                                    


                           




                                      
                        

                                       
                                                             
                                           
                                                              




                           
                       
 



                                  
                             
      

                                                                       



                             

                               


                                  
                             
      

                            
/*
 * This file is part of LEM, a Lua Event Machine.
 * Copyright 2011-2013 Emil Renner Berthing
 *
 * LEM is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Lesser 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with LEM.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <assert.h>
#include <sys/time.h>
#include <time.h>
#include <pthread.h>

#include <lem.h>
#include <lualib.h>

#if EV_USE_KQUEUE
#define LEM_LOOPFLAGS (EVFLAG_NOSIGMASK | EVBACKEND_KQUEUE)
#else
#define LEM_LOOPFLAGS EVFLAG_NOSIGMASK
#endif

#ifdef NDEBUG
#define lem_log_error(fmt, ...) fprintf(stderr, fmt "\n", ##__VA_ARGS__)
#else
#define lem_log_error lem_debug
#endif

#ifndef LUA_OK
#define LUA_OK 0
#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;
	struct lem_runqueue_slot *queue;
	unsigned int first;
	unsigned int last;
	unsigned int mask;
};

#if EV_MULTIPLICITY
struct ev_loop *lem_loop;
#endif
static lua_State *L;
static struct lem_runqueue rq;
static int exit_status = EXIT_SUCCESS;

static void
oom(void)
{
	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
setsignal(int signal, void (*handler)(int), int flags)
{
	struct sigaction act;

	act.sa_handler = handler;
	sigemptyset(&act.sa_mask);
	act.sa_flags = flags;

	if (sigaction(signal, &act, NULL)) {
		lem_log_error("lem: error setting signal %d: %s",
		              signal, strerror(errno));
		return -1;
	}

	return 0;
}

lua_State *
lem_newthread(void)
{
	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_exit(int status)
{
	exit_status = status;
	ev_unloop(LEM_ EVUNLOOP_ALL);
}

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(LEM_ &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 int i;
		unsigned int j;
		struct lem_runqueue_slot *new_queue;

		lem_debug("expanding queue to %u 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
thread_error(lua_State *T)
{
	const char *msg = lua_tostring(T, -1);

	if (msg)
		luaL_traceback(L, T, msg, 0);
}

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, collecting..");
#if 0
		if (lua_gc(L, LUA_GCSTEP, 0)) {
			lem_debug("done collecting");
			ev_idle_stop(EV_A_ w);
		}
#else
		ev_idle_stop(EV_A_ w);
		lua_gc(L, LUA_GCCOLLECT, 0);
#endif
		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, NULL, nargs)) {
	case LUA_OK: /* 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_ERRGCMM:
		lem_debug("error in __gc metamethod");
	case LUA_ERRRUN: /* runtime error */
		lem_debug("thread errored");
		thread_error(T);
		break;

	case LUA_ERRMEM: /* out of memory */
		oom();

	default: /* this shouldn't happen */
		lem_debug("lua_resume: unknown error");
		lua_pushliteral(L, "unknown error");
		break;
	}
	lem_exit(EXIT_FAILURE);
}

#include "pool.c"

static int
queue_file(int argc, char *argv[], int fidx)
{
	lua_State *T = lem_newthread();
	const char *filename;
	int i;

	if (fidx < argc)
		filename = argv[fidx];
	else
		filename = LEM_LDIR "lem/repl.lua";

	switch (luaL_loadfile(T, filename)) {
	case LUA_OK: /* success */
		break;

	case LUA_ERRMEM:
		oom();

	default:
		lem_log_error("lem: %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;
}

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
static inline void
runqueue_wait_init(void)
{
	ev_idle_init(&rq.w, runqueue_pop);
}
#pragma GCC diagnostic pop

int
main(int argc, char *argv[])
{
#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("lem: error initializing event loop");
		return EXIT_FAILURE;
	}

	if (setsignal(SIGPIPE, SIG_IGN, 0)
#if !EV_CHILD_ENABLE
	    || setsignal(SIGCHLD, SIG_DFL, SA_NOCLDSTOP | SA_NOCLDWAIT)
#endif
	   )
		goto error;

	/* create main Lua state */
	L = luaL_newstate();
	if (L == NULL) {
		lem_log_error("lem: error initializing Lua state");
		goto error;
	}
	luaL_openlibs(L);

	/* push thread table */
	lua_newtable(L);

	/* initialize runqueue */
	runqueue_wait_init();
	ev_idle_start(LEM_ &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;

	/* initialize threadpool */
	if (pool_init()) {
		lem_log_error("lem: error initializing threadpool");
		goto error;
	}

	/* load file */
	if (queue_file(argc, argv, 1))
		goto error;

	/* start the mainloop */
	ev_loop(LEM_ 0);
	lem_debug("event loop exited");

	/* if there is an error message left on L print it */
	if (lua_type(L, -1) == LUA_TSTRING)
		lem_log_error("lem: %s", lua_tostring(L, -1));

	/* shutdown Lua */
	lua_close(L);

	/* free runqueue */
	free(rq.queue);

	/* destroy loop */
#if EV_MULTIPLICITY
	ev_loop_destroy(lem_loop);
#else
	ev_default_destroy();
#endif
	lem_debug("Bye %s", exit_status == EXIT_SUCCESS ? "o/" : ":(");
	return exit_status;

error:
	if (L)
		lua_close(L);
	if (rq.queue)
		free(rq.queue);
#if EV_MULTIPLICITY
	ev_loop_destroy(lem_loop);
#else
	ev_default_destroy();
#endif
	return EXIT_FAILURE;
}