Mon, 11 Jun 2007 01:08:59 +0000
* Completed mostly working version
* Moved to a mode where addevent calls a callback rather than
it being instantiated within.
* If the callback returns -1, then no event is ever setup
* Otherwise the integer value is used to setup the event
* This allows for using coroutine.wrap rather than a cooked-up wrapper
* Tests work, although there are a few remaining issues:
* Need to figure a good way of preserving the event object,
not sure if current method is good enough, since the socket
is the only anchor, and it is only held inside the coro..
circular reference, something that Lua 'handles' well.
* Doing more than the maximum sockets the process is allows
causes strangeness to occur in libevent.. somehow
it is getting around to epoll_add which is causing valgrind
to barf.
1
31c782cfe7fe
* Adjusted licensing and added README.
Thomas Harning Jr <harningt@gmail.com>
parents:
0
diff
changeset
|
1 | /* LuaEvent - Copyright (C) 2007 Thomas Harning <harningt@gmail.com> |
31c782cfe7fe
* Adjusted licensing and added README.
Thomas Harning Jr <harningt@gmail.com>
parents:
0
diff
changeset
|
2 | * Licensed as LGPL - See doc/COPYING for details */ |
31c782cfe7fe
* Adjusted licensing and added README.
Thomas Harning Jr <harningt@gmail.com>
parents:
0
diff
changeset
|
3 | |
0 | 4 | #include "luaevent.h" |
5 | ||
6 | #include <lua.h> | |
7 | #include <lauxlib.h> | |
8 | ||
9 | #define EVENT_BASE_MT "EVENT_BASE_MT" | |
10 | #define EVENT_CALLBACK_ARG_MT "EVENT_CALLBACK_ARG_MT" | |
11 | #define EVENT_BASE_LOCATION 1 | |
12 | ||
13 | void setEventBase(lua_State* L, struct event_base* base) { | |
14 | struct event_base** pbase = lua_newuserdata(L, sizeof(base)); | |
15 | *pbase = base; | |
16 | luaL_getmetatable(L, EVENT_BASE_MT); | |
17 | lua_setmetatable(L, -2); | |
18 | lua_rawseti(L, LUA_ENVIRONINDEX, EVENT_BASE_LOCATION); | |
19 | } | |
20 | struct event_base* getEventBase(lua_State* L) { | |
21 | struct event_base* base; | |
22 | lua_rawgeti(L, LUA_ENVIRONINDEX, EVENT_BASE_LOCATION); | |
23 | base = *(struct event_base**)lua_topointer(L, -1); | |
24 | lua_pop(L, 1); | |
25 | return base; | |
26 | } | |
27 | ||
28 | void freeCallbackArgs(le_callback* arg) { | |
29 | if(arg->L) { | |
30 | lua_State* L = arg->L; | |
31 | arg->L = NULL; | |
32 | event_del(&arg->ev); | |
33 | luaL_unref(L, LUA_REGISTRYINDEX, arg->callbackRef); | |
34 | } | |
35 | } | |
36 | /* le_callback is allocated at the beginning of the coroutine in which it | |
37 | is used, no need to manually de-allocate */ | |
38 | ||
39 | /* Index for coroutine is fd as integer for *nix, as lightuserdata for Win */ | |
40 | static void luaevent_callback(int fd, short event, void* p) { | |
41 | le_callback* arg = p; | |
42 | lua_State* L = arg->L; | |
43 | int ret; | |
44 | lua_rawgeti(L, LUA_REGISTRYINDEX, arg->callbackRef); | |
45 | lua_pushinteger(L, event); | |
46 | lua_call(L, 1, 1); | |
47 | ret = lua_tointeger(L, -1); | |
48 | lua_pop(L, 1); | |
49 | if(ret == -1) { | |
50 | freeCallbackArgs(arg); | |
51 | } else { | |
52 | struct event *ev = &arg->ev; | |
53 | int newEvent = ret; | |
54 | if(newEvent != event) { // Need to hook up new event... | |
55 | event_del(ev); | |
56 | event_set(ev, fd, EV_PERSIST | newEvent, luaevent_callback, arg); | |
57 | event_add(ev, NULL); | |
58 | } | |
59 | } | |
60 | } | |
61 | ||
62 | static int luaevent_base_gc(lua_State* L) { | |
63 | struct event_base** pbase = luaL_checkudata(L, 1, EVENT_BASE_MT); | |
64 | if(*pbase) { | |
65 | event_base_free(*pbase); | |
66 | *pbase = NULL; | |
67 | } | |
68 | return 0; | |
69 | } | |
70 | ||
71 | static int luaevent_cb_gc(lua_State* L) { | |
72 | le_callback* arg = luaL_checkudata(L, 1, EVENT_CALLBACK_ARG_MT); | |
73 | freeCallbackArgs(arg); | |
74 | return 0; | |
75 | } | |
76 | ||
2
01b3a96ae760
* Completed mostly working version
Thomas Harning Jr <harningt@gmail.com>
parents:
1
diff
changeset
|
77 | static int luaevent_cb_getfd(lua_State* L) { |
01b3a96ae760
* Completed mostly working version
Thomas Harning Jr <harningt@gmail.com>
parents:
1
diff
changeset
|
78 | le_callback* arg = luaL_checkudata(L, 1, EVENT_CALLBACK_ARG_MT); |
01b3a96ae760
* Completed mostly working version
Thomas Harning Jr <harningt@gmail.com>
parents:
1
diff
changeset
|
79 | lua_pushinteger(L, arg->ev.ev_fd); |
01b3a96ae760
* Completed mostly working version
Thomas Harning Jr <harningt@gmail.com>
parents:
1
diff
changeset
|
80 | return 1; |
01b3a96ae760
* Completed mostly working version
Thomas Harning Jr <harningt@gmail.com>
parents:
1
diff
changeset
|
81 | } |
01b3a96ae760
* Completed mostly working version
Thomas Harning Jr <harningt@gmail.com>
parents:
1
diff
changeset
|
82 | |
0 | 83 | int getSocketFd(lua_State* L, int idx) { |
84 | int fd; | |
85 | luaL_checktype(L, idx, LUA_TUSERDATA); | |
86 | lua_getfield(L, idx, "getfd"); | |
87 | if(lua_isnil(L, -1)) | |
88 | return luaL_error(L, "Socket type missing 'getfd' method"); | |
89 | lua_pushvalue(L, idx); | |
90 | lua_call(L, 1, 1); | |
91 | fd = lua_tointeger(L, -1); | |
92 | lua_pop(L, 1); | |
93 | return fd; | |
94 | } | |
95 | ||
96 | /* Expected to be called at the beginning of the coro that uses it.. | |
97 | Value must be kept until coro is complete.... | |
98 | */ | |
2
01b3a96ae760
* Completed mostly working version
Thomas Harning Jr <harningt@gmail.com>
parents:
1
diff
changeset
|
99 | /* sock, callback */ |
0 | 100 | static int luaevent_addevent(lua_State* L) { |
2
01b3a96ae760
* Completed mostly working version
Thomas Harning Jr <harningt@gmail.com>
parents:
1
diff
changeset
|
101 | int fd, callbackRef; |
01b3a96ae760
* Completed mostly working version
Thomas Harning Jr <harningt@gmail.com>
parents:
1
diff
changeset
|
102 | int top, ret; |
0 | 103 | le_callback* arg; |
104 | fd = getSocketFd(L, 1); | |
2
01b3a96ae760
* Completed mostly working version
Thomas Harning Jr <harningt@gmail.com>
parents:
1
diff
changeset
|
105 | luaL_checktype(L, 2, LUA_TFUNCTION); |
01b3a96ae760
* Completed mostly working version
Thomas Harning Jr <harningt@gmail.com>
parents:
1
diff
changeset
|
106 | top = lua_gettop(L); |
01b3a96ae760
* Completed mostly working version
Thomas Harning Jr <harningt@gmail.com>
parents:
1
diff
changeset
|
107 | /* Preserve the callback function */ |
01b3a96ae760
* Completed mostly working version
Thomas Harning Jr <harningt@gmail.com>
parents:
1
diff
changeset
|
108 | lua_pushvalue(L, 2); |
0 | 109 | callbackRef = luaL_ref(L, LUA_REGISTRYINDEX); |
2
01b3a96ae760
* Completed mostly working version
Thomas Harning Jr <harningt@gmail.com>
parents:
1
diff
changeset
|
110 | |
01b3a96ae760
* Completed mostly working version
Thomas Harning Jr <harningt@gmail.com>
parents:
1
diff
changeset
|
111 | /* Call the callback with all arguments after it to get the loop primed.. */ |
01b3a96ae760
* Completed mostly working version
Thomas Harning Jr <harningt@gmail.com>
parents:
1
diff
changeset
|
112 | lua_call(L, top - 2, 1); |
01b3a96ae760
* Completed mostly working version
Thomas Harning Jr <harningt@gmail.com>
parents:
1
diff
changeset
|
113 | ret = lua_tointeger(L, -1); |
01b3a96ae760
* Completed mostly working version
Thomas Harning Jr <harningt@gmail.com>
parents:
1
diff
changeset
|
114 | lua_pop(L, 1); |
01b3a96ae760
* Completed mostly working version
Thomas Harning Jr <harningt@gmail.com>
parents:
1
diff
changeset
|
115 | if(ret == -1) { /* Done, no need to setup event */ |
01b3a96ae760
* Completed mostly working version
Thomas Harning Jr <harningt@gmail.com>
parents:
1
diff
changeset
|
116 | luaL_unref(L, LUA_REGISTRYINDEX, callbackRef); |
01b3a96ae760
* Completed mostly working version
Thomas Harning Jr <harningt@gmail.com>
parents:
1
diff
changeset
|
117 | return 0; |
01b3a96ae760
* Completed mostly working version
Thomas Harning Jr <harningt@gmail.com>
parents:
1
diff
changeset
|
118 | } |
0 | 119 | arg = lua_newuserdata(L, sizeof(*arg)); |
120 | luaL_getmetatable(L, EVENT_CALLBACK_ARG_MT); | |
121 | lua_setmetatable(L, -2); | |
122 | ||
123 | arg->L = L; | |
124 | arg->callbackRef = callbackRef; | |
2
01b3a96ae760
* Completed mostly working version
Thomas Harning Jr <harningt@gmail.com>
parents:
1
diff
changeset
|
125 | |
0 | 126 | /* Setup event... */ |
2
01b3a96ae760
* Completed mostly working version
Thomas Harning Jr <harningt@gmail.com>
parents:
1
diff
changeset
|
127 | event_set(&arg->ev, fd, ret | EV_PERSIST, luaevent_callback, arg); |
0 | 128 | event_base_set(getEventBase(L), &arg->ev); |
129 | event_add(&arg->ev, NULL); | |
130 | return 1; | |
131 | } | |
132 | ||
133 | static int luaevent_loop(lua_State* L) { | |
134 | int ret = event_base_loop(getEventBase(L), 0); | |
135 | lua_pushinteger(L, ret); | |
136 | return 1; | |
137 | } | |
138 | ||
139 | static luaL_Reg funcs[] = { | |
140 | { "addevent", luaevent_addevent }, | |
141 | { "loop", luaevent_loop }, | |
142 | { NULL, NULL } | |
143 | }; | |
144 | ||
145 | typedef struct { | |
146 | const char* name; | |
147 | int value; | |
148 | } namedInteger; | |
149 | ||
150 | static namedInteger consts[] = { | |
151 | {"LEAVE", -1}, | |
152 | {"EV_READ", EV_READ}, | |
153 | {"EV_WRITE", EV_WRITE}, | |
154 | {NULL, 0} | |
155 | }; | |
156 | ||
157 | void setNamedIntegers(lua_State* L, namedInteger* p) { | |
158 | while(p->name) { | |
159 | lua_pushinteger(L, p->value); | |
160 | lua_setfield(L, -2, p->name); | |
161 | p++; | |
162 | } | |
163 | } | |
164 | ||
165 | /* Verified ok */ | |
166 | int luaopen_luaevent_core(lua_State* L) { | |
167 | /* Setup environ table */ | |
168 | lua_createtable(L, 1, 0); | |
169 | lua_replace(L, LUA_ENVIRONINDEX); | |
170 | /* Setup metatable */ | |
171 | luaL_newmetatable(L, EVENT_BASE_MT); | |
172 | lua_pushcfunction(L, luaevent_base_gc); | |
173 | lua_setfield(L, -2, "__gc"); | |
174 | lua_pop(L, 1); | |
175 | luaL_newmetatable(L, EVENT_CALLBACK_ARG_MT); | |
176 | lua_pushcfunction(L, luaevent_cb_gc); | |
177 | lua_setfield(L, -2, "__gc"); | |
2
01b3a96ae760
* Completed mostly working version
Thomas Harning Jr <harningt@gmail.com>
parents:
1
diff
changeset
|
178 | lua_pushcfunction(L, luaevent_cb_getfd); |
01b3a96ae760
* Completed mostly working version
Thomas Harning Jr <harningt@gmail.com>
parents:
1
diff
changeset
|
179 | lua_setfield(L, -2, "getfd"); |
0 | 180 | lua_pop(L, 1); |
181 | ||
182 | setEventBase(L, event_init()); | |
183 | ||
184 | luaL_register(L, "luaevent.core", funcs); | |
185 | setNamedIntegers(L, consts); | |
186 | return 1; | |
187 | } |