/lasync.c
C | 792 lines | 666 code | 120 blank | 6 comment | 130 complexity | ec4376c0819b050f58567c9efa2dbd52 MD5 | raw file
- /*
- * lasync.c
- * libevent binding for Lua 5.1
- * Copyright 2007 Gary Ng<linux@garyng.com>
- * This code can be distributed under the LGPL license
- */
- #include <stdio.h>
- #include <memory.h>
- #include <fcntl.h>
- #include <assert.h>
- #include <errno.h>
- #ifndef _WIN32
- #include <sys/socket.h>
- #include <sys/wait.h>
- #include <sys/time.h>
- #else
- #include <windows.h>
- #include <time.h>
- #endif
- #include <errno.h>
- #include <event.h>
- #define LUA_LIB
- #include "lua.h"
- #include "lauxlib.h"
- typedef struct {
- struct event_base *base;
- int dispatcher;
- int is_select;
- int event_fd_map;
- int event_obj_map;
- int r_cnt;
- int w_cnt;
- int rtab;
- int wtab;
- int o_rtab;
- int o_wtab;
- int dispatcher_idx;
- int fd_map_idx;
- int event_map_idx;
- int auto_requeue;
- int event_objects;
- lua_State *L;
- } libevent_context;
- typedef struct {
- struct event ev;
- int obj;
- int event_obj;
- int active;
- int fd;
- int want;
- int once;
- double timeout;
- libevent_context *libevent;
- } event_context;
- libevent_context libevent_handle;
- #define MYVERSION "Libevent for " LUA_VERSION " 0.1"
- #define MYEVENT "Libevent object"
- #define MYEVENT_BASE "Libevent base object"
- static void Pevent_call(int fd, short ev, void *arg)
- {
- event_context *p = arg;
- libevent_context *libevent = p->libevent;
- lua_State *L = libevent->L;
- int top = lua_gettop(L);
- struct timeval tv, *ptv = NULL;
- double t = p->timeout;
- int loop_dispatcher = libevent->dispatcher_idx > 0 && !lua_isnoneornil(L, libevent->dispatcher_idx);
- if (t >= 0) {
- tv.tv_sec = (int) t;
- tv.tv_usec = (t - tv.tv_sec)*1000000;
- ptv = &tv;
- }
- if (p->active == 0) return;
- lua_rawgeti(L, libevent->event_map_idx, p->event_obj);
- #if 0
- lua_pushinteger(L, p->event_obj);
- lua_gettable(L, libevent->event_map_idx);
- #endif
- if (lua_isuserdata(L,-1) &&
- luaL_checkudata(L,-1,MYEVENT) &&
- lua_touserdata(L,-1) == p) {
-
- unsigned char *actions[4]={NULL, NULL, NULL, NULL};
- int i;
- if (libevent->auto_requeue && !p->once) {
- event_add(&p->ev, ptv);
- }
- if (ev & EV_READ & p->want) actions[0] = "readable";
- if (ev & EV_WRITE & p->want) actions[1] = "writable";
- if (ev & EV_TIMEOUT & p->want) actions[2] = "timeout";
- if (ev & EV_SIGNAL & p->want) actions[3] = "signal";
- for (i = 0; i < sizeof(actions)/sizeof(unsigned char *); i++) {
- unsigned char *action;
- if (action = actions[i]) {
- lua_pushstring(L, action);
- if (loop_dispatcher) {
- lua_gettable(L, libevent->dispatcher_idx);
- if (!lua_isnil(L, -1)) {
- lua_pushvalue(L, libevent->dispatcher_idx);
- lua_pushvalue(L, -3);
- lua_call(L, 2, 0);
- } else {
- lua_pop(L,1);
- }
- } else {
- lua_gettable(L, -2);
- if (!lua_isnil(L, -1)) {
- lua_pushvalue(L, -2);
- lua_call(L, 1, 0);
- } else {
- lua_pop(L,1);
- }
- }
- }
- }
- }
- lua_pop(L,1);
- }
- void Pevent_select(int fd, short ev, void *arg)
- {
- event_context *p = arg;
- libevent_context *libevent = p->libevent;
- lua_State *L = libevent->L;
- int top = lua_gettop(L);
- if (fd == -1 || p->active == 0) return;
- lua_pushinteger(L, p->obj);
- lua_gettable(L, libevent->fd_map_idx);
- if (!lua_isnil(L, -1)) {
- if (ev & EV_READ && p->want & EV_READ) {
- p->want &= ~EV_READ;
- if (1 || !lua_isnil(L, -1)) { /* interested */
- libevent->r_cnt++;
- lua_pushinteger(L, libevent->r_cnt);
- lua_pushvalue(L, -2);
- lua_settable(L, libevent->o_rtab);
- lua_pushvalue(L, -1);
- lua_pushinteger(L, libevent->r_cnt);
- lua_settable(L, libevent->o_rtab);
- }
- }
- if (ev & EV_WRITE && p->want & EV_WRITE) {
- p->want &= ~EV_WRITE;
- if (1 || !lua_isnil(L, -1)) { /* interested */
- libevent->w_cnt++;
- lua_pushvalue(L, -1);
- lua_pushinteger(L, libevent->w_cnt);
- lua_settable(L, libevent->o_wtab);
- lua_pushinteger(L, libevent->w_cnt);
- lua_pushvalue(L, -2);
- lua_settable(L, libevent->o_wtab);
- }
- }
- }
- lua_pop(L,1);
- }
- static void Pevent_cb(int fd, short ev, void *arg)
- {
- event_context *p = arg;
- libevent_context *libevent = p->libevent;
- if (libevent->is_select) {
- Pevent_select(fd, ev, arg);
- } else {
- Pevent_call(fd, ev, arg);
- }
- }
- static event_context *Pevent(lua_State *L, int i, char *who)
- {
- if (!lua_isuserdata(L,i)) return NULL;
- if (luaL_checkudata(L,i,MYEVENT)==NULL) {
- return NULL;
- #if 0
- luaL_typerror(L,i,MYEVENT);
- #endif
- }
- return lua_touserdata(L,i);
- }
- static void Pevent_close(lua_State *L)
- {
- event_context *p = Pevent(L, 1,"event close");
- if (p->active) {
- event_del(&p->ev);
- lua_rawgeti(L, LUA_REGISTRYINDEX, p->libevent->event_fd_map);
- luaL_unref(L, -1, p->obj);
- lua_rawgeti(L, LUA_REGISTRYINDEX, p->libevent->event_obj_map);
- luaL_unref(L, -1, p->event_obj);
- p->event_obj = -1;
- p->obj = -1;
- lua_pop(L,2);
- }
- p->active = 0;
- }
- static int Levent_settimeout(lua_State *L)
- {
- event_context *p = Pevent(L, 1,"settimeout");
- int t = luaL_optinteger(L,2, -1);
- if (!p) return 0;
- if ((t < 0 && p->timeout >= 0) && (p->want & (EV_READ | EV_WRITE)) == 0) {
- event_del(&p->ev);
- p->active = 0;
- }
- else if ((t >= 0 && p->timeout < 0) && (p->want & (EV_READ | EV_WRITE)) == 0) {
- struct timeval tv, *ptv = NULL;
- if (t >= 0) {
- tv.tv_sec = (int) t;
- tv.tv_usec = (t - tv.tv_sec)*1000000;
- ptv = &tv;
- }
- if(p->fd >=0) event_add(&p->ev, ptv);
- else evtimer_add(&p->ev, ptv);
- p->active = 1;
- }
- p->timeout = t;
- return 0;
- }
- static int Levent_want(lua_State *L)
- {
- event_context *p = Pevent(L, 1,"want");
- int want = luaL_optinteger(L,2, EV_READ | EV_WRITE | EV_TIMEOUT | EV_SIGNAL);
- if (!p) return 0;
- p->want |= want;
- if (p->want & (EV_READ | EV_WRITE) || p->timeout >= 0.0) {
- struct timeval tv, *ptv = NULL;
- double t = p->timeout;
- if (t >= 0) {
- tv.tv_sec = (int) t;
- tv.tv_usec = (t - tv.tv_sec)*1000000;
- ptv = &tv;
- }
- if(p->fd >=0) event_add(&p->ev, ptv);
- else evtimer_add(&p->ev, ptv);
- p->active = 1;
- }
- return 0;
- }
- static int Levent_skip(lua_State *L)
- {
- event_context *p = Pevent(L, 1,"skip");
- int skip = luaL_optinteger(L, 2,0);
- if (!p) return 0;
- p->want &= ~skip;
- if ((p->want & (EV_READ | EV_WRITE)) == 0 && p->timeout < 0) {
- event_del(&p->ev);
- p->active = 0;
- }
- return 0;
- }
- static int Levent_gc(lua_State *L)
- {
- event_context *p = Pevent(L, 1,"gc");
- p->libevent->event_objects -=1;
- Pevent_close(L);
- #if 0
- lua_rawgeti(L, LUA_REGISTRYINDEX, p->libevent->event_fd_map);
- luaL_unref(L, -1, p->obj);
- lua_rawgeti(L, LUA_REGISTRYINDEX, p->libevent->event_obj_map);
- luaL_unref(L, -1, p->event_obj);
- #endif
- return 0;
- }
- static int Levent_object(lua_State *L)
- {
- event_context *p = Pevent(L, 1,"object");
- if (!p) return 0;
- lua_rawgeti(L, LUA_REGISTRYINDEX, p->libevent->event_fd_map);
- lua_rawgeti(L, -1, p->obj);
- if (lua_isnil(L, -1) && p->fd >= 0) {
- p->obj = -1;
- Pevent_close(L);
- }
- return 1;
- }
- static int Levent_getfd(lua_State *L)
- {
- event_context *p = Pevent(L, 1,"fd");
- if (!p) return 0;
- lua_pushnumber(L, p->fd);
- return 1;
- }
- static int Levent_close(lua_State *L)
- {
- #if 1
- Pevent_close(L);
- #endif
- return 0;
- }
- static int Levent_requeue(lua_State *L)
- {
- event_context *p = Pevent(L, 1,"requeue");
- struct timeval tv, *ptv = NULL;
- double t = p->timeout;
- if (t >= 0) {
- tv.tv_sec = (int) t;
- tv.tv_usec = (t - tv.tv_sec)*1000000;
- ptv = &tv;
- }
- if(p->fd >=0) event_add(&p->ev, ptv);
- else evtimer_add(&p->ev, ptv);
- return 0;
- }
- static int Levent_open(lua_State *L)
- {
- event_context *p = Pevent(L, 1,"open");
- double t = luaL_optnumber(L, 2, p->fd < 0 ? 0.1 : -1);
- struct timeval tv, *ptv = NULL;
- if (!p->active && p->event_obj >= 0) {
- if (t >= 0) {
- tv.tv_sec = (int) t;
- tv.tv_usec = (t - tv.tv_sec)*1000000;
- ptv = &tv;
- }
- if(p->fd >= 0) event_add(&p->ev, ptv);
- else evtimer_add(&p->ev, ptv);
- p->active = 1;
- p->timeout = t;
- }
-
- lua_pushvalue(L,1);
- return 1;
- }
- static int Levent_base(lua_State *L)
- {
- int auto_requeue = luaL_optinteger(L,2,1);
- libevent_context **p = lua_newuserdata(L, sizeof(libevent_context*));
- *p = &libevent_handle;
- (*p)->auto_requeue = auto_requeue;
- luaL_getmetatable(L, MYEVENT_BASE);
- lua_setmetatable(L,-2);
- if (lua_istable(L,1) || lua_isuserdata(L,1)) {
- lua_pushvalue(L,1);
- (*p)->dispatcher = luaL_ref(L, LUA_REGISTRYINDEX);
- } else {
- (*p)->dispatcher = -1;
- }
- return 1;
- }
- static libevent_context **Pevent_base(lua_State *L, int i)
- {
- if (luaL_checkudata(L,i,MYEVENT_BASE)==NULL) {
- luaL_typerror(L,i,MYEVENT_BASE);
- }
- return lua_touserdata(L,i);
- }
- static int Levent_base_close(lua_State *L)
- {
- libevent_context *libevent = *Pevent_base(L, 1);
- if (libevent->dispatcher != -1) {
- luaL_unref(L, LUA_REGISTRYINDEX, libevent->dispatcher);
- libevent->dispatcher = -1;
- }
- }
- static int Levent_loopexit(lua_State *L)
- {
- struct timeval tv, *ptv = NULL;
- libevent_context *libevent = *Pevent_base(L, 1);
- tv.tv_sec = (int) 0;
- tv.tv_usec = 0;
- ptv = &tv;
-
- event_base_loopexit(libevent->base, NULL);
- return 0;
- }
- static int Levent_loop(lua_State *L)
- {
- libevent_context *libevent = *Pevent_base(L, 1);
- int once = luaL_optinteger(L,3,0);
- int flags = once ? EVLOOP_NONBLOCK | EVLOOP_ONCE : 0;
- int ret;
- int top = lua_gettop(L);
- libevent->r_cnt = 0;
- libevent->w_cnt = 0;
- libevent->rtab = -1;
- libevent->wtab = -1;
- libevent->o_rtab = -1;
- libevent->o_wtab = -1;
- libevent->L = L;
- libevent->is_select = 0;
- lua_settop(L, 4);
- if (lua_istable(L,2) || lua_isuserdata(L,2)) {
- lua_pushvalue(L,2);
- } else if (libevent->dispatcher > 0)
- lua_rawgeti(L, LUA_REGISTRYINDEX, libevent->dispatcher); /* don't do it in the call back */
- else
- lua_pushnil(L);
- libevent->dispatcher_idx = lua_gettop(L);
- lua_rawgeti(L, LUA_REGISTRYINDEX, libevent->event_fd_map); /* don't do it in the call back */
- libevent->fd_map_idx = lua_gettop(L);
- lua_rawgeti(L, LUA_REGISTRYINDEX, libevent->event_obj_map); /* don't do it in the call back */
- libevent->event_map_idx = lua_gettop(L);
- ret = event_base_loop(libevent->base, flags);
- if (ret < 0) {
- lua_pushnil(L);
- lua_pushnumber(L,ret);
- lua_pushstring(L,strerror(errno));
- return 3;
- }
- else {
- lua_pushnumber(L,1);
- return 1;
- }
- }
- static int Levent_select(lua_State *L)
- {
- libevent_context *libevent = *Pevent_base(L, 1);
- double t = luaL_optnumber(L, 4, -1);
- int flags = EVLOOP_ONCE;
- int i = 0;
- int itab, rtab, wtab;
- event_context tout;
- struct timeval tv, *ptv = NULL;
- if (t >= 0.0) {
- tv.tv_sec = (int) t;
- tv.tv_usec = (t - tv.tv_sec)*1000000;
- tout.libevent = libevent;
- evtimer_set(&tout.ev, Pevent_cb, &tout);
- evtimer_add(&tout.ev, &tv);
- flags |= EVLOOP_NONBLOCK;
- ptv = &tv;
- }
- lua_settop(L, 4);
- lua_rawgeti(L, LUA_REGISTRYINDEX, libevent->event_fd_map); /* don't do it in the call back */
- libevent->fd_map_idx = lua_gettop(L);
- lua_newtable(L); rtab = lua_gettop(L);
- lua_newtable(L); wtab = lua_gettop(L);
- libevent->r_cnt = 0;
- libevent->w_cnt = 0;
- libevent->rtab = 2;
- libevent->wtab = 3;
- libevent->o_rtab = rtab;
- libevent->o_wtab = wtab;
- libevent->L = L;
- libevent->is_select = 1;
- libevent->dispatcher_idx = -1;
- i=0;
- while (++i) {
- int dirty = 0;
- lua_pushnumber(L,i);
- lua_gettable(L, 2);
- if (lua_isnil(L,-1)) {
- lua_pop(L,1);
- break;
- }
- lua_pushstring(L,"dirty");
- lua_gettable(L,-2);
- if (!lua_isnil(L, -1)) {
- lua_pushvalue(L,-2);
- lua_call(L, 1, 1);
- dirty = lua_toboolean(L, -1);
- }
- lua_pop(L,1);
- if (dirty) {
- lua_pushnumber(L, ++libevent->r_cnt);
- lua_pushvalue(L,-2);
- lua_settable(L, libevent->o_rtab);
- lua_pushnumber(L, libevent->r_cnt);
- lua_settable(L, libevent->o_rtab);
- } else {
- lua_pushstring(L,"event");
- lua_gettable(L,-2);
- if (!lua_isnil(L, -1)) {
- event_context *p = Pevent(L, -1, "select");
- p->want = EV_READ;
- }
- lua_pop(L,2);
- }
- }
- i=0;
- while (++i) {
- lua_pushnumber(L,i);
- lua_gettable(L, 3);
- if (lua_isnil(L,-1)) {
- lua_pop(L,1);
- break;
- }
- lua_pushstring(L,"event");
- lua_gettable(L,-2);
- if (!lua_isnil(L, -1)) {
- event_context *p = Pevent(L, -1, "select");
- p->want |= EV_WRITE;
- }
- lua_pop(L,2);
- }
- event_base_loop(libevent->base, flags);
- if (t >= 0.0) evtimer_del(&tout.ev);
- return 2;
- }
- static int Levent_state(lua_State *L)
- {
- libevent_context *libevent = *Pevent_base(L, 1);
- lua_pushnumber(L, libevent->event_objects);
- return 1;
- }
- static int Levent(lua_State *L)
- {
- libevent_context *libevent = *Pevent_base(L, 1);
- const int fd = luaL_checkinteger(L, 2);
- event_context *p;
- int map_idx,event_map_idx;
- if (lua_isnoneornil(L,3)) luaL_error(L,"lasync.event: parameter #2 must be an object");
- lua_settop(L, 4);
- lua_rawgeti(L, LUA_REGISTRYINDEX, libevent->event_fd_map);
- map_idx = lua_gettop(L);
- lua_rawgeti(L, LUA_REGISTRYINDEX, libevent->event_obj_map);
- event_map_idx = lua_gettop(L);
- p = lua_newuserdata(L,sizeof(event_context));
- if (lua_isnoneornil(L,4)) {
- p->once = fd < 0;
- }
- else {
- p->once = luaL_checkinteger(L,4);
- }
- p->timeout = fd < 0 ? 0.1 : -1;
- p->fd = fd;
- p->active = 0;
- if (fd < 0)
- p->want = EV_TIMEOUT;
- else
- p->want = EV_READ | EV_WRITE | EV_SIGNAL | EV_TIMEOUT;
- p->libevent = libevent;
- if (fd >= 0) event_set(&p->ev, fd, EV_READ | EV_WRITE, Pevent_cb, p);
- else evtimer_set(&p->ev, Pevent_cb, p);
- luaL_getmetatable(L, MYEVENT);
- lua_setmetatable(L,-2);
-
- lua_pushvalue(L, 3);
- p->obj = luaL_ref(L, map_idx);
- lua_pushvalue(L, -1);
- p->event_obj = luaL_ref(L, event_map_idx);
- libevent->event_objects += 1;
- return 1;
- }
- static int Lprobe_read(lua_State *L)
- {
- #ifndef _WIN32
- int fd = luaL_checkinteger(L,1);
- int i = 0;
- struct {
- char buf[1];
- struct msghdr msg;
- struct iovec iov;
- } msg;
- int ret;
- memset(&msg,0,sizeof(msg));
- msg.iov.iov_base = msg.buf;
- msg.iov.iov_len = sizeof(msg.buf);
- msg.msg.msg_iov = &msg.iov;
- msg.msg.msg_iovlen = 1;
- for (i = 0; i < 1; i++) {
- ret = sendmsg(fd, &msg.msg, i == 0 ? MSG_PEEK : 0);
- if (ret <= 0) {
- if (errno != EOPNOTSUPP || i == 1) {
- lua_pushnil(L);
- if (errno == EAGAIN || errno == EWOULDBLOCK) lua_pushstring(L,"timeout");
- else lua_pushstring(L,"closed");
- return 2;
- }
- } else {
- lua_pushboolean(L, 1);
- return 1;
- }
- }
- #else
- return 0;
- #endif
- }
- static int Lprobe(lua_State *L)
- {
- #ifndef _WIN32
- int fd = luaL_checkinteger(L,1);
- int i = 0;
- struct {
- char buf[1];
- struct msghdr msg;
- struct iovec iov;
- } msg;
- int ret;
- memset(&msg,0,sizeof(msg));
- msg.iov.iov_base = msg.buf;
- msg.iov.iov_len = sizeof(msg.buf);
- msg.msg.msg_iov = &msg.iov;
- msg.msg.msg_iovlen = 1;
- for (i = 0; i < 2; i++) {
- ret = sendmsg(fd, &msg.msg, i == 0 ? MSG_OOB : 0);
- if (ret <= 0) {
- if (errno != EOPNOTSUPP || i == 1) {
- lua_pushnil(L);
- if (errno == EAGAIN || errno == EWOULDBLOCK) lua_pushstring(L,"timeout");
- else lua_pushstring(L,"closed");
- return 2;
- }
- } else {
- lua_pushboolean(L, 1);
- return 1;
- }
- }
- #else
- return 0;
- #endif
- }
- static const luaL_reg Rm[] = {
- { "probe", Lprobe },
- { "probe_read", Lprobe_read},
- { "handle", Levent_base },
- { NULL, NULL }
- };
- static const luaL_reg Rbase[] = {
- { "event", Levent },
- { "state", Levent_state },
- { "select", Levent_select},
- { "loop", Levent_loop},
- { "loopexit", Levent_loopexit},
- { NULL, NULL }
- };
- static const luaL_reg Revent[] = {
- { "__gc", Levent_gc },
- { "object", Levent_object},
- { "getfd", Levent_getfd},
- { "close", Levent_close},
- { "want", Levent_want},
- { "settimeout", Levent_settimeout},
- { "skip", Levent_skip},
- { "open", Levent_open},
- { "requeue", Levent_requeue},
- { NULL, NULL },
- };
- LUA_API int luaopen_lasync(lua_State *L)
- {
- struct event_base *base;
- base = event_init();
- if (!base) {
- luaL_error(L,"luaevent: failed to allocate libevent handle base");
- }
- libevent_handle.base = base;
- luaL_newmetatable(L,MYEVENT);
- lua_pushliteral(L,"__index");
- lua_pushvalue(L,-2);
- lua_settable(L,-3);
- luaL_openlib(L,NULL,Revent,0);
- luaL_newmetatable(L,MYEVENT_BASE);
- lua_pushliteral(L,"__index");
- lua_pushvalue(L,-2);
- lua_settable(L,-3);
- luaL_openlib(L,NULL,Rbase,0);
- lua_newtable(L); /* fdmap */
- lua_newtable(L); /* fdmap metatable {__mode="kv"} */
- lua_pushliteral(L,"__mode");
- lua_pushstring(L,"v");
- lua_settable(L,-3);
- lua_setmetatable(L, -2);
- libevent_handle.event_fd_map = luaL_ref(L, LUA_REGISTRYINDEX);
- lua_newtable(L); /* event map */
- lua_newtable(L); /* event map metatable {__mode="kv"} */
- lua_pushliteral(L,"__mode");
- lua_pushstring(L,"v");
- lua_settable(L,-3);
- lua_setmetatable(L, -2);
- libevent_handle.event_obj_map = luaL_ref(L, LUA_REGISTRYINDEX);
-
- libevent_handle.L = L;
- libevent_handle.dispatcher = -1;
- luaL_openlib(L,"lasync",Rm,0);
- lua_pushliteral(L,"version"); /** version */
- lua_pushliteral(L,MYVERSION);
- lua_settable(L,-3);
- lua_pushliteral(L,"EV_READ");
- lua_pushinteger(L,EV_READ);
- lua_settable(L,-3);
- lua_pushliteral(L,"EV_WRITE");
- lua_pushinteger(L,EV_WRITE);
- lua_settable(L,-3);
- lua_pushliteral(L,"EV_TIMEOUT");
- lua_pushinteger(L,EV_TIMEOUT);
- lua_settable(L,-3);
- lua_pushliteral(L,"EV_SIGNAL");
- lua_pushinteger(L,EV_SIGNAL);
- lua_settable(L,-3);
- return 1;
- }