Use ppoll to allow signals to wake service loop early on change of pollfd event
To enable this code you need to force LWS_HAS_PPOLL to de defined.
#defining it at the top of libwebsockets.c is enough.
Signed-off-by: Andy Green <andy.green@linaro.org>
diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c
index 687bad6..6dc4d1d 100644
--- a/lib/libwebsockets.c
+++ b/lib/libwebsockets.c
@@ -37,6 +37,8 @@
#include <netdb.h>
#endif
+#include <sys/types.h>
+
#ifdef LWS_OPENSSL_SUPPORT
int openssl_websocket_private_data_index;
#endif
@@ -83,6 +85,17 @@
struct libwebsocket *wsi, struct pollfd *pollfd);
#endif
+
+#ifdef LWS_HAS_PPOLL
+/*
+ * set to the Thread ID that's doing the service loop just before entry to ppoll
+ * indicates service thread likely idling in ppoll()
+ * volatile because other threads may check it as part of processing for pollfd
+ * event change.
+ */
+static volatile int lws_idling_ppoll_tid;
+#endif
+
/**
* lws_get_library_version: get version and git hash library built from
*
@@ -1325,15 +1338,32 @@
{
int n;
int m;
+#ifdef LWS_HAS_PPOLL
+ struct timespec timeout_ts;
+ sigset_t sigmask;
+#endif
/* stay dead once we are dead */
if (context == NULL)
return 1;
+#ifdef LWS_HAS_PPOLL
+ lws_idling_ppoll_tid = context->protocols[0].callback(context, NULL,
+ LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0);
+
+ timeout_ts.tv_sec = timeout_ms / 1000;
+ timeout_ts.tv_nsec = timeout_ms % 1000;
+ sigemptyset(&sigmask);
+ sigaddset(&sigmask, SIGUSR2);
+
/* wait for something to need service */
+ n = ppoll(context->fds, context->fds_count, &timeout_ts, &sigmask);
+ lws_idling_ppoll_tid = 0;
+#else
n = poll(context->fds, context->fds_count, timeout_ms);
+#endif
if (n == 0) /* poll timeout */ {
libwebsocket_service_fd(context, NULL);
return 0;
@@ -1408,13 +1438,19 @@
lws_change_pollfd(struct libwebsocket *wsi, int _and, int _or)
{
struct libwebsocket_context *context = wsi->protocol->owning_server;
+ int events;
+#ifdef LWS_HAS_PPOLL
+ int tid;
+ int sampled_ppoll_tid;
+#endif
context->protocols[0].callback(context, wsi,
LWS_CALLBACK_LOCK_POLL,
wsi->user_space, (void *)(long)wsi->sock, 0);
- context->fds[wsi->position_in_fds_table].events &= ~_and;
- context->fds[wsi->position_in_fds_table].events |= _or;
+ events = context->fds[wsi->position_in_fds_table].events;
+
+ context->fds[wsi->position_in_fds_table].events = (events & ~_and) | _or;
/* external POLL support via protocol 0 */
if (_and)
@@ -1427,6 +1463,26 @@
LWS_CALLBACK_SET_MODE_POLL_FD,
wsi->user_space, (void *)(long)wsi->sock, _or);
+#ifdef LWS_HAS_PPOLL
+ /*
+ * if we changed something in this pollfd...
+ * ... and we're running in a different thread context
+ * than the service thread...
+ * ... and the service thread is waiting in ppoll()...
+ * then fire a SIGUSR2 at the service thread to force it to
+ * restart the ppoll() with our changed events
+ */
+ if (events != context->fds[wsi->position_in_fds_table].events) {
+ sampled_ppoll_tid = lws_idling_ppoll_tid;
+ if (sampled_ppoll_tid) {
+ tid = context->protocols[0].callback(context, NULL,
+ LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0);
+ if (tid != sampled_ppoll_tid)
+ kill(sampled_ppoll_tid, SIGUSR2);
+ }
+ }
+#endif
+
context->protocols[0].callback(context, wsi,
LWS_CALLBACK_UNLOCK_POLL,
wsi->user_space, (void *)(long)wsi->sock, 0);
@@ -1747,6 +1803,13 @@
return n;
}
+/*
+ * This is just used to interrupt poll waiting
+ * we don't have to do anything with it.
+ */
+static void lws_sigusr2(int sig)
+{
+}
/**
* libwebsocket_create_context() - Create the websocket handler
@@ -2036,6 +2099,8 @@
goto bail;
}
+ signal(SIGUSR2, lws_sigusr2);
+
#ifdef SSL_OP_NO_COMPRESSION
SSL_CTX_set_options(context->ssl_ctx, SSL_OP_NO_COMPRESSION);
#endif
@@ -2349,6 +2414,7 @@
}
}
#endif
+
return context;
bail: