event_callback: Fix segmentation fault
[luaevent.git] / src / event_callback.c
blobf5ad7cbfcabf575672e965620235d51804222017
1 /* LuaEvent - Copyright (C) 2007,2012 Thomas Harning <harningt@gmail.com>
2 * Licensed as LGPL - See doc/COPYING for details */
3 #include "event_callback.h"
4 #include <assert.h>
5 #include <lauxlib.h>
6 #include <string.h>
8 #define EVENT_CALLBACK_ARG_MT "EVENT_CALLBACK_ARG_MT"
10 void freeCallbackArgs(le_callback* arg, lua_State* L) {
11 if(arg->base) {
12 arg->base = NULL;
13 event_del(&arg->ev);
14 luaL_unref(L, LUA_REGISTRYINDEX, arg->callbackRef);
17 /* le_callback is allocated at the beginning of the coroutine in which it
18 is used, no need to manually de-allocate */
20 /* Index for coroutine is fd as integer for *nix, as lightuserdata for Win */
21 void luaevent_callback(int fd, short event, void* p) {
22 le_callback* cb = p;
23 lua_State* L;
24 int ret;
25 struct timeval new_tv = { 0, 0 };
26 assert(cb);
27 if(!cb->base)
28 return; /* Event has already been collected + destroyed */
29 assert(cb->base->loop_L);
30 L = cb->base->loop_L;
31 lua_rawgeti(L, LUA_REGISTRYINDEX, cb->callbackRef);
32 lua_pushinteger(L, event);
33 /* cb->base may be NULL after the pcall, if the event is destroyed */
34 le_base* base = cb->base;
35 if(lua_pcall(L, 1, 2, 0))
37 base->errorMessage = luaL_ref(L, LUA_REGISTRYINDEX);
38 event_base_loopbreak(base->base);
39 lua_pop(L, 1);
40 return;
42 if(!cb->base) {
43 lua_pop(L, 2);
44 return; /* event was destroyed during callback */
46 /* If nothing is returned, re-use the old event value */
47 ret = luaL_optinteger(L, -2, event);
48 /* Clone the old timeout value in case a new one wasn't set */
49 memcpy(&new_tv, &cb->timeout, sizeof(new_tv));
50 if(lua_isnumber(L, -1)) {
51 double newTimeout = lua_tonumber(L, -1);
52 if(newTimeout > 0) {
53 load_timeval(newTimeout, &new_tv);
56 lua_pop(L, 2);
57 if(ret == -1) {
58 freeCallbackArgs(cb, L);
59 } else {
60 struct event *ev = &cb->ev;
61 int newEvent = ret;
62 if( newEvent != event || (cb->timeout.tv_sec != new_tv.tv_sec || cb->timeout.tv_usec != new_tv.tv_usec) ) {
63 struct timeval *ptv = &cb->timeout;
64 cb->timeout = new_tv;
65 if(!cb->timeout.tv_sec && !cb->timeout.tv_usec)
66 ptv = NULL;
67 event_del(ev);
68 event_set(ev, fd, EV_PERSIST | newEvent, luaevent_callback, cb);
69 /* Assume cannot set a new timeout.. */
70 event_add(ev, ptv);
75 static int luaevent_cb_gc(lua_State* L) {
76 le_callback* arg = luaL_checkudata(L, 1, EVENT_CALLBACK_ARG_MT);
77 freeCallbackArgs(arg, L);
78 return 0;
81 le_callback* event_callback_push(lua_State* L, int baseIdx, int callbackIdx) {
82 le_callback* cb;
83 le_base *base = event_base_get(L, baseIdx);
84 luaL_checktype(L, callbackIdx, LUA_TFUNCTION);
85 cb = lua_newuserdata(L, sizeof(*cb));
86 luaL_getmetatable(L, EVENT_CALLBACK_ARG_MT);
87 lua_setmetatable(L, -2);
89 lua_pushvalue(L, callbackIdx);
90 cb->callbackRef = luaL_ref(L, LUA_REGISTRYINDEX);
91 cb->base = base;
92 memset(&cb->timeout, 0, sizeof(cb->timeout));
93 return cb;
96 void event_callback_register(lua_State* L) {
97 luaL_newmetatable(L, EVENT_CALLBACK_ARG_MT);
98 lua_pushcfunction(L, luaevent_cb_gc);
99 lua_setfield(L, -2, "__gc");
100 lua_newtable(L);
101 lua_pushcfunction(L, luaevent_cb_gc);
102 lua_setfield(L, -2, "close");
103 lua_setfield(L, -2, "__index");
104 lua_pop(L, 1);