doap: Add XEP-0288
[prosody.git] / util-src / poll.c
blob21cb9581d143eb00cc00ef1e5c5f5bc60b241c54
2 /*
3 * Lua polling library
4 * Copyright (C) 2017-2018 Kim Alvefur
6 * This project is MIT licensed. Please see the
7 * COPYING file in the source package for more information.
9 */
11 #include <unistd.h>
12 #include <string.h>
13 #include <errno.h>
15 #ifdef __linux__
16 #define USE_EPOLL
17 #endif
19 #ifdef USE_EPOLL
20 #include <sys/epoll.h>
21 #ifndef MAX_EVENTS
22 #define MAX_EVENTS 64
23 #endif
24 #else
25 #include <sys/select.h>
26 #endif
28 #include <lualib.h>
29 #include <lauxlib.h>
31 #ifdef USE_EPOLL
32 #define STATE_MT "util.poll<epoll>"
33 #else
34 #define STATE_MT "util.poll<select>"
35 #endif
37 #if (LUA_VERSION_NUM == 501)
38 #define luaL_setmetatable(L, tname) luaL_getmetatable(L, tname); lua_setmetatable(L, -2)
39 #endif
42 * Structure to keep state for each type of API
44 typedef struct Lpoll_state {
45 int processed;
46 #ifdef USE_EPOLL
47 int epoll_fd;
48 struct epoll_event events[MAX_EVENTS];
49 #else
50 fd_set wantread;
51 fd_set wantwrite;
52 fd_set readable;
53 fd_set writable;
54 fd_set all;
55 fd_set err;
56 #endif
57 } Lpoll_state;
60 * Add an FD to be watched
62 static int Ladd(lua_State *L) {
63 struct Lpoll_state *state = luaL_checkudata(L, 1, STATE_MT);
64 int fd = luaL_checkinteger(L, 2);
66 int wantread = lua_toboolean(L, 3);
67 int wantwrite = lua_toboolean(L, 4);
69 if(fd < 0) {
70 lua_pushnil(L);
71 lua_pushstring(L, strerror(EBADF));
72 lua_pushinteger(L, EBADF);
73 return 3;
76 #ifdef USE_EPOLL
77 struct epoll_event event;
78 event.data.fd = fd;
79 event.events = (wantread ? EPOLLIN : 0) | (wantwrite ? EPOLLOUT : 0);
81 event.events |= EPOLLERR | EPOLLHUP | EPOLLRDHUP;
83 int ret = epoll_ctl(state->epoll_fd, EPOLL_CTL_ADD, fd, &event);
85 if(ret < 0) {
86 ret = errno;
87 lua_pushnil(L);
88 lua_pushstring(L, strerror(ret));
89 lua_pushinteger(L, ret);
90 return 3;
93 lua_pushboolean(L, 1);
94 return 1;
96 #else
98 if(fd > FD_SETSIZE) {
99 lua_pushnil(L);
100 lua_pushstring(L, strerror(EBADF));
101 lua_pushinteger(L, EBADF);
102 return 3;
105 if(FD_ISSET(fd, &state->all)) {
106 lua_pushnil(L);
107 lua_pushstring(L, strerror(EEXIST));
108 lua_pushinteger(L, EEXIST);
109 return 3;
112 FD_CLR(fd, &state->readable);
113 FD_CLR(fd, &state->writable);
114 FD_CLR(fd, &state->err);
116 FD_SET(fd, &state->all);
118 if(wantread) {
119 FD_SET(fd, &state->wantread);
121 else {
122 FD_CLR(fd, &state->wantread);
125 if(wantwrite) {
126 FD_SET(fd, &state->wantwrite);
128 else {
129 FD_CLR(fd, &state->wantwrite);
132 lua_pushboolean(L, 1);
133 return 1;
134 #endif
138 * Set events to watch for, readable and/or writable
140 static int Lset(lua_State *L) {
141 struct Lpoll_state *state = luaL_checkudata(L, 1, STATE_MT);
142 int fd = luaL_checkinteger(L, 2);
144 #ifdef USE_EPOLL
146 int wantread = lua_toboolean(L, 3);
147 int wantwrite = lua_toboolean(L, 4);
149 struct epoll_event event;
150 event.data.fd = fd;
151 event.events = (wantread ? EPOLLIN : 0) | (wantwrite ? EPOLLOUT : 0);
153 event.events |= EPOLLERR | EPOLLHUP | EPOLLRDHUP;
155 int ret = epoll_ctl(state->epoll_fd, EPOLL_CTL_MOD, fd, &event);
157 if(ret == 0) {
158 lua_pushboolean(L, 1);
159 return 1;
161 else {
162 ret = errno;
163 lua_pushnil(L);
164 lua_pushstring(L, strerror(ret));
165 lua_pushinteger(L, ret);
166 return 3;
169 #else
171 if(!FD_ISSET(fd, &state->all)) {
172 lua_pushnil(L);
173 lua_pushstring(L, strerror(ENOENT));
174 lua_pushinteger(L, ENOENT);
175 return 3;
178 if(!lua_isnoneornil(L, 3)) {
179 if(lua_toboolean(L, 3)) {
180 FD_SET(fd, &state->wantread);
182 else {
183 FD_CLR(fd, &state->wantread);
187 if(!lua_isnoneornil(L, 4)) {
188 if(lua_toboolean(L, 4)) {
189 FD_SET(fd, &state->wantwrite);
191 else {
192 FD_CLR(fd, &state->wantwrite);
196 lua_pushboolean(L, 1);
197 return 1;
198 #endif
202 * Remove FDs
204 static int Ldel(lua_State *L) {
205 struct Lpoll_state *state = luaL_checkudata(L, 1, STATE_MT);
206 int fd = luaL_checkinteger(L, 2);
208 #ifdef USE_EPOLL
210 struct epoll_event event;
211 event.data.fd = fd;
213 int ret = epoll_ctl(state->epoll_fd, EPOLL_CTL_DEL, fd, &event);
215 if(ret == 0) {
216 lua_pushboolean(L, 1);
217 return 1;
219 else {
220 ret = errno;
221 lua_pushnil(L);
222 lua_pushstring(L, strerror(ret));
223 lua_pushinteger(L, ret);
224 return 3;
227 #else
229 if(!FD_ISSET(fd, &state->all)) {
230 lua_pushnil(L);
231 lua_pushstring(L, strerror(ENOENT));
232 lua_pushinteger(L, ENOENT);
233 return 3;
236 FD_CLR(fd, &state->wantread);
237 FD_CLR(fd, &state->wantwrite);
238 FD_CLR(fd, &state->readable);
239 FD_CLR(fd, &state->writable);
240 FD_CLR(fd, &state->all);
241 FD_CLR(fd, &state->err);
243 lua_pushboolean(L, 1);
244 return 1;
245 #endif
250 * Check previously manipulated event state for FDs ready for reading or writing
252 static int Lpushevent(lua_State *L, struct Lpoll_state *state) {
253 #ifdef USE_EPOLL
255 if(state->processed > 0) {
256 state->processed--;
257 struct epoll_event event = state->events[state->processed];
258 lua_pushinteger(L, event.data.fd);
259 lua_pushboolean(L, event.events & (EPOLLIN | EPOLLHUP | EPOLLRDHUP | EPOLLERR));
260 lua_pushboolean(L, event.events & EPOLLOUT);
261 return 3;
264 #else
266 for(int fd = state->processed + 1; fd < FD_SETSIZE; fd++) {
267 if(FD_ISSET(fd, &state->readable) || FD_ISSET(fd, &state->writable) || FD_ISSET(fd, &state->err)) {
268 lua_pushinteger(L, fd);
269 lua_pushboolean(L, FD_ISSET(fd, &state->readable) | FD_ISSET(fd, &state->err));
270 lua_pushboolean(L, FD_ISSET(fd, &state->writable));
271 FD_CLR(fd, &state->readable);
272 FD_CLR(fd, &state->writable);
273 FD_CLR(fd, &state->err);
274 state->processed = fd;
275 return 3;
279 #endif
280 return 0;
284 * Wait for event
286 static int Lwait(lua_State *L) {
287 struct Lpoll_state *state = luaL_checkudata(L, 1, STATE_MT);
289 int ret = Lpushevent(L, state);
291 if(ret != 0) {
292 return ret;
295 lua_Number timeout = luaL_checknumber(L, 2);
296 luaL_argcheck(L, timeout >= 0, 1, "positive number expected");
298 #ifdef USE_EPOLL
299 ret = epoll_wait(state->epoll_fd, state->events, MAX_EVENTS, timeout * 1000);
300 #else
302 * select(2) mutates the fd_sets passed to it so in order to not
303 * have to recreate it manually every time a copy is made.
305 memcpy(&state->readable, &state->wantread, sizeof(fd_set));
306 memcpy(&state->writable, &state->wantwrite, sizeof(fd_set));
307 memcpy(&state->err, &state->all, sizeof(fd_set));
309 struct timeval tv;
310 tv.tv_sec = (time_t)timeout;
311 tv.tv_usec = ((suseconds_t)(timeout * 1000000)) % 1000000;
313 ret = select(FD_SETSIZE, &state->readable, &state->writable, &state->err, &tv);
314 #endif
316 if(ret == 0) {
317 lua_pushnil(L);
318 lua_pushstring(L, "timeout");
319 return 2;
321 else if(ret < 0 && errno == EINTR) {
322 lua_pushnil(L);
323 lua_pushstring(L, "signal");
324 return 2;
326 else if(ret < 0) {
327 ret = errno;
328 lua_pushnil(L);
329 lua_pushstring(L, strerror(ret));
330 lua_pushinteger(L, ret);
331 return 3;
335 * Search for the first ready FD and return it
337 #ifdef USE_EPOLL
338 state->processed = ret;
339 #else
340 state->processed = -1;
341 #endif
342 return Lpushevent(L, state);
345 #ifdef USE_EPOLL
347 * Return Epoll FD
349 static int Lgetfd(lua_State *L) {
350 struct Lpoll_state *state = luaL_checkudata(L, 1, STATE_MT);
351 lua_pushinteger(L, state->epoll_fd);
352 return 1;
356 * Close epoll FD
358 static int Lgc(lua_State *L) {
359 struct Lpoll_state *state = luaL_checkudata(L, 1, STATE_MT);
361 if(state->epoll_fd == -1) {
362 return 0;
365 if(close(state->epoll_fd) == 0) {
366 state->epoll_fd = -1;
368 else {
369 lua_pushstring(L, strerror(errno));
370 lua_error(L);
373 return 0;
375 #endif
378 * String representation
380 static int Ltos(lua_State *L) {
381 struct Lpoll_state *state = luaL_checkudata(L, 1, STATE_MT);
382 lua_pushfstring(L, "%s: %p", STATE_MT, state);
383 return 1;
387 * Create a new context
389 static int Lnew(lua_State *L) {
390 /* Allocate state */
391 Lpoll_state *state = lua_newuserdata(L, sizeof(Lpoll_state));
392 luaL_setmetatable(L, STATE_MT);
394 /* Initialize state */
395 #ifdef USE_EPOLL
396 state->epoll_fd = -1;
397 state->processed = 0;
399 int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
401 if(epoll_fd <= 0) {
402 lua_pushnil(L);
403 lua_pushstring(L, strerror(errno));
404 lua_pushinteger(L, errno);
405 return 3;
408 state->epoll_fd = epoll_fd;
409 #else
410 FD_ZERO(&state->wantread);
411 FD_ZERO(&state->wantwrite);
412 FD_ZERO(&state->readable);
413 FD_ZERO(&state->writable);
414 FD_ZERO(&state->all);
415 FD_ZERO(&state->err);
416 state->processed = FD_SETSIZE;
417 #endif
419 return 1;
423 * Open library
425 int luaopen_util_poll(lua_State *L) {
426 #if (LUA_VERSION_NUM > 501)
427 luaL_checkversion(L);
428 #endif
430 luaL_newmetatable(L, STATE_MT);
433 lua_pushliteral(L, STATE_MT);
434 lua_setfield(L, -2, "__name");
436 lua_pushcfunction(L, Ltos);
437 lua_setfield(L, -2, "__tostring");
439 lua_createtable(L, 0, 2);
441 lua_pushcfunction(L, Ladd);
442 lua_setfield(L, -2, "add");
443 lua_pushcfunction(L, Lset);
444 lua_setfield(L, -2, "set");
445 lua_pushcfunction(L, Ldel);
446 lua_setfield(L, -2, "del");
447 lua_pushcfunction(L, Lwait);
448 lua_setfield(L, -2, "wait");
449 #ifdef USE_EPOLL
450 lua_pushcfunction(L, Lgetfd);
451 lua_setfield(L, -2, "getfd");
452 #endif
454 lua_setfield(L, -2, "__index");
456 #ifdef USE_EPOLL
457 lua_pushcfunction(L, Lgc);
458 lua_setfield(L, -2, "__gc");
459 #endif
462 lua_createtable(L, 0, 3);
464 lua_pushcfunction(L, Lnew);
465 lua_setfield(L, -2, "new");
467 #define push_errno(named_error) lua_pushinteger(L, named_error);\
468 lua_setfield(L, -2, #named_error);
470 push_errno(EEXIST);
471 push_errno(ENOENT);
474 return 1;