util.poll: Add support for the poll() API
authorKim Alvefur <zash@zash.se>
Wed, 23 Feb 2022 20:31:03 +0100
changeset 12320 6bb2f660f689
parent 12319 cf2086a1bd45
child 12321 b4f2027ef917
util.poll: Add support for the poll() API Might be better than select(), more portable than epoll.
CHANGES
teal-src/util/poll.d.tl
util-src/poll.c
--- a/CHANGES	Sun Feb 27 14:36:43 2022 +0100
+++ b/CHANGES	Wed Feb 23 20:31:03 2022 +0100
@@ -56,6 +56,7 @@
 -   MUC: support for XEP-0421 occupant identifiers
 -   `prosodyctl check connectivity` via observe.jabber.network
 -   libunbound for DNS queries
+-   The POSIX poll() API used by server_epoll on \*nix other than Linux
 
 ## Changes
 
--- a/teal-src/util/poll.d.tl	Sun Feb 27 14:36:43 2022 +0100
+++ b/teal-src/util/poll.d.tl	Wed Feb 23 20:31:03 2022 +0100
@@ -17,10 +17,12 @@
 
 local record lib
 	new : function () : state
+	EEXIST : integer
+	EMFILE : integer
 	ENOENT : integer
-	EEXIST : integer
 	enum api_backend
 		"epoll"
+		"poll"
 		"select"
 	end
 	api : api_backend
--- a/util-src/poll.c	Sun Feb 27 14:36:43 2022 +0100
+++ b/util-src/poll.c	Wed Feb 23 20:31:03 2022 +0100
@@ -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.
@@ -15,6 +15,9 @@
 #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"
@@ -26,6 +29,12 @@
 #define MAX_EVENTS 64
 #endif
 #endif
+#ifdef USE_POLL
+#include <poll.h>
+#ifndef MAX_EVENTS
+#define MAX_EVENTS 10000
+#endif
+#endif
 #ifdef USE_SELECT
 #include <sys/select.h>
 #endif
@@ -51,6 +60,10 @@
 	int epoll_fd;
 	struct epoll_event events[MAX_EVENTS];
 #endif
+#ifdef USE_POLL
+	nfds_t count;
+	struct pollfd events[MAX_EVENTS];
+#endif
 #ifdef USE_SELECT
 	fd_set wantread;
 	fd_set wantwrite;
@@ -99,6 +112,32 @@
 	return 1;
 
 #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) {
@@ -173,6 +212,27 @@
 	}
 
 #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)) {
@@ -232,6 +292,40 @@
 	}
 
 #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)) {
@@ -270,6 +364,22 @@
 	}
 
 #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++) {
@@ -307,6 +417,9 @@
 #ifdef USE_EPOLL
 	ret = epoll_wait(state->epoll_fd, state->events, MAX_EVENTS, timeout * 1000);
 #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
@@ -349,6 +462,9 @@
 #ifdef USE_EPOLL
 	state->processed = ret;
 #endif
+#ifdef USE_POLL
+	state->processed = state->count;
+#endif
 #ifdef USE_SELECT
 	state->processed = -1;
 #endif
@@ -420,6 +536,17 @@
 
 	state->epoll_fd = epoll_fd;
 #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);
@@ -482,6 +609,7 @@
 		lua_setfield(L, -2, #named_error);
 
 		push_errno(EEXIST);
+		push_errno(EMFILE);
 		push_errno(ENOENT);
 
 		lua_pushliteral(L, POLL_BACKEND);