aboutsummaryrefslogtreecommitdiffstats
path: root/util-src/poll.c
diff options
context:
space:
mode:
Diffstat (limited to 'util-src/poll.c')
-rw-r--r--util-src/poll.c217
1 files changed, 182 insertions, 35 deletions
diff --git a/util-src/poll.c b/util-src/poll.c
index 0ca0cf28..81caa953 100644
--- a/util-src/poll.c
+++ b/util-src/poll.c
@@ -1,7 +1,7 @@
/*
* Lua polling library
- * Copyright (C) 2017-2018 Kim Alvefur
+ * Copyright (C) 2017-2022 Kim Alvefur
*
* This project is MIT licensed. Please see the
* COPYING file in the source package for more information.
@@ -12,8 +12,15 @@
#include <string.h>
#include <errno.h>
-#ifdef __linux__
+#if defined(__linux__)
#define USE_EPOLL
+#define POLL_BACKEND "epoll"
+#elif defined(__unix__)
+#define USE_POLL
+#define POLL_BACKEND "poll"
+#else
+#define USE_SELECT
+#define POLL_BACKEND "select"
#endif
#ifdef USE_EPOLL
@@ -21,22 +28,28 @@
#ifndef MAX_EVENTS
#define MAX_EVENTS 64
#endif
-#else
+#endif
+#ifdef USE_POLL
+#include <poll.h>
+#ifndef MAX_EVENTS
+#define MAX_EVENTS 10000
+#endif
+#endif
+#ifdef USE_SELECT
#include <sys/select.h>
#endif
#include <lualib.h>
#include <lauxlib.h>
-#ifdef USE_EPOLL
-#define STATE_MT "util.poll<epoll>"
-#else
-#define STATE_MT "util.poll<select>"
-#endif
+#define STATE_MT "util.poll<" POLL_BACKEND ">"
#if (LUA_VERSION_NUM == 501)
#define luaL_setmetatable(L, tname) luaL_getmetatable(L, tname); lua_setmetatable(L, -2)
#endif
+#if (LUA_VERSION_NUM < 504)
+#define luaL_pushfail lua_pushnil
+#endif
/*
* Structure to keep state for each type of API
@@ -46,7 +59,12 @@ typedef struct Lpoll_state {
#ifdef USE_EPOLL
int epoll_fd;
struct epoll_event events[MAX_EVENTS];
-#else
+#endif
+#ifdef USE_POLL
+ nfds_t count;
+ struct pollfd events[MAX_EVENTS];
+#endif
+#ifdef USE_SELECT
fd_set wantread;
fd_set wantwrite;
fd_set readable;
@@ -59,7 +77,7 @@ typedef struct Lpoll_state {
/*
* Add an FD to be watched
*/
-int Ladd(lua_State *L) {
+static int Ladd(lua_State *L) {
struct Lpoll_state *state = luaL_checkudata(L, 1, STATE_MT);
int fd = luaL_checkinteger(L, 2);
@@ -67,7 +85,7 @@ int Ladd(lua_State *L) {
int wantwrite = lua_toboolean(L, 4);
if(fd < 0) {
- lua_pushnil(L);
+ luaL_pushfail(L);
lua_pushstring(L, strerror(EBADF));
lua_pushinteger(L, EBADF);
return 3;
@@ -84,7 +102,7 @@ int Ladd(lua_State *L) {
if(ret < 0) {
ret = errno;
- lua_pushnil(L);
+ luaL_pushfail(L);
lua_pushstring(L, strerror(ret));
lua_pushinteger(L, ret);
return 3;
@@ -93,17 +111,44 @@ int Ladd(lua_State *L) {
lua_pushboolean(L, 1);
return 1;
-#else
+#endif
+#ifdef USE_POLL
+
+ for(nfds_t i = 0; i < state->count; i++) {
+ if(state->events[i].fd == fd) {
+ luaL_pushfail(L);
+ lua_pushstring(L, strerror(EEXIST));
+ lua_pushinteger(L, EEXIST);
+ return 3;
+ }
+ }
+
+ if(state->count >= MAX_EVENTS) {
+ luaL_pushfail(L);
+ lua_pushstring(L, strerror(EMFILE));
+ lua_pushinteger(L, EMFILE);
+ return 3;
+ }
+
+ state->events[state->count].fd = fd;
+ state->events[state->count].events = (wantread ? POLLIN : 0) | (wantwrite ? POLLOUT : 0);
+ state->events[state->count].revents = 0;
+ state->count++;
+
+ lua_pushboolean(L, 1);
+ return 1;
+#endif
+#ifdef USE_SELECT
if(fd > FD_SETSIZE) {
- lua_pushnil(L);
+ luaL_pushfail(L);
lua_pushstring(L, strerror(EBADF));
lua_pushinteger(L, EBADF);
return 3;
}
if(FD_ISSET(fd, &state->all)) {
- lua_pushnil(L);
+ luaL_pushfail(L);
lua_pushstring(L, strerror(EEXIST));
lua_pushinteger(L, EEXIST);
return 3;
@@ -137,7 +182,7 @@ int Ladd(lua_State *L) {
/*
* Set events to watch for, readable and/or writable
*/
-int Lset(lua_State *L) {
+static int Lset(lua_State *L) {
struct Lpoll_state *state = luaL_checkudata(L, 1, STATE_MT);
int fd = luaL_checkinteger(L, 2);
@@ -160,18 +205,41 @@ int Lset(lua_State *L) {
}
else {
ret = errno;
- lua_pushnil(L);
+ luaL_pushfail(L);
lua_pushstring(L, strerror(ret));
lua_pushinteger(L, ret);
return 3;
}
-#else
+#endif
+#ifdef USE_POLL
+ int wantread = lua_toboolean(L, 3);
+ int wantwrite = lua_toboolean(L, 4);
+
+ for(nfds_t i = 0; i < state->count; i++) {
+ struct pollfd *event = &state->events[i];
+
+ if(event->fd == fd) {
+ event->events = (wantread ? POLLIN : 0) | (wantwrite ? POLLOUT : 0);
+ lua_pushboolean(L, 1);
+ return 1;
+ } else if(event->fd == -1) {
+ break;
+ }
+ }
+
+ luaL_pushfail(L);
+ lua_pushstring(L, strerror(ENOENT));
+ lua_pushinteger(L, ENOENT);
+ return 3;
+#endif
+#ifdef USE_SELECT
if(!FD_ISSET(fd, &state->all)) {
- lua_pushnil(L);
+ luaL_pushfail(L);
lua_pushstring(L, strerror(ENOENT));
lua_pushinteger(L, ENOENT);
+ return 3;
}
if(!lua_isnoneornil(L, 3)) {
@@ -200,7 +268,7 @@ int Lset(lua_State *L) {
/*
* Remove FDs
*/
-int Ldel(lua_State *L) {
+static int Ldel(lua_State *L) {
struct Lpoll_state *state = luaL_checkudata(L, 1, STATE_MT);
int fd = luaL_checkinteger(L, 2);
@@ -217,18 +285,54 @@ int Ldel(lua_State *L) {
}
else {
ret = errno;
- lua_pushnil(L);
+ luaL_pushfail(L);
lua_pushstring(L, strerror(ret));
lua_pushinteger(L, ret);
return 3;
}
-#else
+#endif
+#ifdef USE_POLL
+
+ if(state->count == 0) {
+ luaL_pushfail(L);
+ lua_pushstring(L, strerror(ENOENT));
+ lua_pushinteger(L, ENOENT);
+ return 3;
+ }
+
+ /*
+ * Move the last item on top of the removed one
+ */
+ struct pollfd *last = &state->events[state->count - 1];
+
+ for(nfds_t i = 0; i < state->count; i++) {
+ struct pollfd *event = &state->events[i];
+
+ if(event->fd == fd) {
+ event->fd = last->fd;
+ event->events = last->events;
+ event->revents = last->revents;
+ last->fd = -1;
+ state->count--;
+
+ lua_pushboolean(L, 1);
+ return 1;
+ }
+ }
+
+ luaL_pushfail(L);
+ lua_pushstring(L, strerror(ENOENT));
+ lua_pushinteger(L, ENOENT);
+ return 3;
+#endif
+#ifdef USE_SELECT
if(!FD_ISSET(fd, &state->all)) {
- lua_pushnil(L);
+ luaL_pushfail(L);
lua_pushstring(L, strerror(ENOENT));
lua_pushinteger(L, ENOENT);
+ return 3;
}
FD_CLR(fd, &state->wantread);
@@ -247,7 +351,7 @@ int Ldel(lua_State *L) {
/*
* Check previously manipulated event state for FDs ready for reading or writing
*/
-int Lpushevent(lua_State *L, struct Lpoll_state *state) {
+static int Lpushevent(lua_State *L, struct Lpoll_state *state) {
#ifdef USE_EPOLL
if(state->processed > 0) {
@@ -259,7 +363,24 @@ int Lpushevent(lua_State *L, struct Lpoll_state *state) {
return 3;
}
-#else
+#endif
+#ifdef USE_POLL
+
+ for(int i = state->processed - 1; i >= 0; i--) {
+ struct pollfd *event = &state->events[i];
+
+ if(event->fd != -1 && event->revents != 0) {
+ lua_pushinteger(L, event->fd);
+ lua_pushboolean(L, event->revents & (POLLIN | POLLHUP | POLLERR));
+ lua_pushboolean(L, event->revents & POLLOUT);
+ event->revents = 0;
+ state->processed = i;
+ return 3;
+ }
+ }
+
+#endif
+#ifdef USE_SELECT
for(int fd = state->processed + 1; fd < FD_SETSIZE; fd++) {
if(FD_ISSET(fd, &state->readable) || FD_ISSET(fd, &state->writable) || FD_ISSET(fd, &state->err)) {
@@ -281,7 +402,7 @@ int Lpushevent(lua_State *L, struct Lpoll_state *state) {
/*
* Wait for event
*/
-int Lwait(lua_State *L) {
+static int Lwait(lua_State *L) {
struct Lpoll_state *state = luaL_checkudata(L, 1, STATE_MT);
int ret = Lpushevent(L, state);
@@ -295,7 +416,11 @@ int Lwait(lua_State *L) {
#ifdef USE_EPOLL
ret = epoll_wait(state->epoll_fd, state->events, MAX_EVENTS, timeout * 1000);
-#else
+#endif
+#ifdef USE_POLL
+ ret = poll(state->events, state->count, timeout * 1000);
+#endif
+#ifdef USE_SELECT
/*
* select(2) mutates the fd_sets passed to it so in order to not
* have to recreate it manually every time a copy is made.
@@ -312,18 +437,20 @@ int Lwait(lua_State *L) {
#endif
if(ret == 0) {
+ /* Is this an error? */
lua_pushnil(L);
lua_pushstring(L, "timeout");
return 2;
}
else if(ret < 0 && errno == EINTR) {
+ /* Is this an error? */
lua_pushnil(L);
lua_pushstring(L, "signal");
return 2;
}
else if(ret < 0) {
ret = errno;
- lua_pushnil(L);
+ luaL_pushfail(L);
lua_pushstring(L, strerror(ret));
lua_pushinteger(L, ret);
return 3;
@@ -334,7 +461,11 @@ int Lwait(lua_State *L) {
*/
#ifdef USE_EPOLL
state->processed = ret;
-#else
+#endif
+#ifdef USE_POLL
+ state->processed = state->count;
+#endif
+#ifdef USE_SELECT
state->processed = -1;
#endif
return Lpushevent(L, state);
@@ -344,7 +475,7 @@ int Lwait(lua_State *L) {
/*
* Return Epoll FD
*/
-int Lgetfd(lua_State *L) {
+static int Lgetfd(lua_State *L) {
struct Lpoll_state *state = luaL_checkudata(L, 1, STATE_MT);
lua_pushinteger(L, state->epoll_fd);
return 1;
@@ -353,7 +484,7 @@ int Lgetfd(lua_State *L) {
/*
* Close epoll FD
*/
-int Lgc(lua_State *L) {
+static int Lgc(lua_State *L) {
struct Lpoll_state *state = luaL_checkudata(L, 1, STATE_MT);
if(state->epoll_fd == -1) {
@@ -375,7 +506,7 @@ int Lgc(lua_State *L) {
/*
* String representation
*/
-int Ltos(lua_State *L) {
+static int Ltos(lua_State *L) {
struct Lpoll_state *state = luaL_checkudata(L, 1, STATE_MT);
lua_pushfstring(L, "%s: %p", STATE_MT, state);
return 1;
@@ -384,7 +515,7 @@ int Ltos(lua_State *L) {
/*
* Create a new context
*/
-int Lnew(lua_State *L) {
+static int Lnew(lua_State *L) {
/* Allocate state */
Lpoll_state *state = lua_newuserdata(L, sizeof(Lpoll_state));
luaL_setmetatable(L, STATE_MT);
@@ -397,14 +528,26 @@ int Lnew(lua_State *L) {
int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
if(epoll_fd <= 0) {
- lua_pushnil(L);
+ luaL_pushfail(L);
lua_pushstring(L, strerror(errno));
lua_pushinteger(L, errno);
return 3;
}
state->epoll_fd = epoll_fd;
-#else
+#endif
+#ifdef USE_POLL
+ state->processed = -1;
+ state->count = 0;
+
+ for(nfds_t i = 0; i < MAX_EVENTS; i++) {
+ state->events[i].fd = -1;
+ state->events[i].events = 0;
+ state->events[i].revents = 0;
+ }
+
+#endif
+#ifdef USE_SELECT
FD_ZERO(&state->wantread);
FD_ZERO(&state->wantwrite);
FD_ZERO(&state->readable);
@@ -466,8 +609,12 @@ int luaopen_util_poll(lua_State *L) {
lua_setfield(L, -2, #named_error);
push_errno(EEXIST);
+ push_errno(EMFILE);
push_errno(ENOENT);
+ lua_pushliteral(L, POLL_BACKEND);
+ lua_setfield(L, -2, "api");
+
}
return 1;
}