src/event_callback.c

1
/* LuaEvent - Copyright (C) 2007 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>
7
 
8
#define EVENT_CALLBACK_ARG_MT "EVENT_CALLBACK_ARG_MT"
9
 
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);
15
	}
16
}
17
/* le_callback is allocated at the beginning of the coroutine in which it
18
is used, no need to manually de-allocate */
19
 
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
	lua_call(L, 1, 2);
34
	if(!cb->base)
35
		return; /* event was destroyed during callback */
36
	ret = lua_tointeger(L, -2);
37
	if(lua_isnumber(L, -1))
38
	{
39
		double newTimeout = lua_tonumber(L, -1);
40
		if(newTimeout>0)
41
			load_timeval(newTimeout, &new_tv);
42
	}
43
	lua_pop(L, 2);
44
	if(ret == -1) {
45
		freeCallbackArgs(cb, L);
46
	} else {
47
		struct event *ev = &cb->ev;
48
		int newEvent = ret;
49
       		if( newEvent != event || (cb->timeout.tv_sec != new_tv.tv_sec || cb->timeout.tv_usec != new_tv.tv_usec) )
50
       		{
51
			struct timeval *ptv = &cb->timeout;
52
			cb->timeout = new_tv;
53
			if(!cb->timeout.tv_sec && !cb->timeout.tv_usec)
54
				ptv = NULL;
55
			event_del(ev);
56
			event_set(ev, fd, EV_PERSIST | newEvent, luaevent_callback, cb);
57
			/* Assume cannot set a new timeout.. */
58
			event_add(ev, ptv);
59
		}
60
	}
61
}
62
 
63
static int luaevent_cb_gc(lua_State* L) {
64
	le_callback* arg = luaL_checkudata(L, 1, EVENT_CALLBACK_ARG_MT);
65
	freeCallbackArgs(arg, L);
66
	return 0;
67
}
68
 
69
le_callback* event_callback_push(lua_State* L, int baseIdx, int callbackIdx) {
70
	le_callback* cb;
71
	le_base *base = event_base_get(L, baseIdx);
72
	luaL_checktype(L, callbackIdx, LUA_TFUNCTION);
73
	cb = lua_newuserdata(L, sizeof(*cb));
74
	luaL_getmetatable(L, EVENT_CALLBACK_ARG_MT);
75
	lua_setmetatable(L, -2);
76
 
77
	lua_pushvalue(L, callbackIdx);
78
	cb->callbackRef = luaL_ref(L, LUA_REGISTRYINDEX);
79
	cb->base = base;
80
	memset(&cb->timeout, 0, sizeof(cb->timeout));
81
	return cb;
82
}
83
 
84
int event_callback_register(lua_State* L) {
85
	luaL_newmetatable(L, EVENT_CALLBACK_ARG_MT);
86
	lua_pushcfunction(L, luaevent_cb_gc);
87
	lua_setfield(L, -2, "__gc");
88
	lua_newtable(L);
89
	lua_pushcfunction(L, luaevent_cb_gc);
90
	lua_setfield(L, -2, "close");
91
	lua_setfield(L, -2, "__index");
92
	lua_pop(L, 1);
93
	return 0;
94
}