From 1ee57f6fe8a67eb6d7952ae3dca3f4f161b40108 Mon Sep 17 00:00:00 2001 From: Patrick Gansterer Date: Thu, 6 Mar 2014 11:57:50 +0100 Subject: [PATCH] Add libwebsocket_cancel_service() to let a pending libwebsocket_service() return Use poll() with a pipe instead of ppoll() to allow the stop polling on all UNIX platforms. --- lib/libwebsockets.c | 100 +++++++++++++++++++++++++------------------- lib/libwebsockets.h | 7 ++-- lib/private-libwebsockets.h | 14 +++++-- test-server/test-server.c | 6 +-- 4 files changed, 75 insertions(+), 52 deletions(-) diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c index a280f8a..3cc5635 100644 --- a/lib/libwebsockets.c +++ b/lib/libwebsockets.c @@ -90,16 +90,6 @@ static const char * const log_level_names[] = { #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 * @@ -1276,6 +1266,8 @@ libwebsocket_context_destroy(struct libwebsocket_context *context) for (n = 0; n < context->fds_count; n++) { struct libwebsocket *wsi = context->lws_lookup[context->fds[n].fd]; + if (!wsi) + continue; libwebsocket_close_and_free_session(context, wsi, LWS_CLOSE_STATUS_NOSTATUS /* no protocol close */); n--; @@ -1313,6 +1305,8 @@ libwebsocket_context_destroy(struct libwebsocket_context *context) #if defined(WIN32) || defined(_WIN32) #else + close(context->dummy_pipe_fds[0]); + close(context->dummy_pipe_fds[1]); close(context->fd_random); #endif @@ -1392,33 +1386,18 @@ libwebsocket_service(struct libwebsocket_context *context, int timeout_ms) { int n; int m; -#ifdef LWS_HAS_PPOLL - struct timespec timeout_ts; - sigset_t sigmask; -#endif + char buf; /* 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, + context->service_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; - - sigprocmask(SIG_BLOCK, NULL, &sigmask); - sigdelset(&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 + context->service_tid = 0; + if (n == 0) /* poll timeout */ { libwebsocket_service_fd(context, NULL); return 0; @@ -1436,6 +1415,13 @@ libwebsocket_service(struct libwebsocket_context *context, int timeout_ms) for (n = 0; n < context->fds_count; n++) { if (!context->fds[n].revents) continue; +#ifndef _WIN32 + if (context->fds[n].fd == context->dummy_pipe_fds[0]) { + if (read(context->fds[n].fd, &buf, 1) != 1) + lwsl_err("Cannot read from dummy pipe."); + continue; + } +#endif m = libwebsocket_service_fd(context, &context->fds[n]); if (m < 0) return -1; @@ -1447,6 +1433,25 @@ libwebsocket_service(struct libwebsocket_context *context, int timeout_ms) return 0; } +/** + * libwebsocket_cancel_service() - Cancel servicing of pending websocket activity + * @context: Websocket context + * + * This function let a call to libwebsocket_service() waiting for a timeout + * immediately return. + * + * At the moment this functionality cannot be used on Windows. + */ +LWS_VISIBLE void +libwebsocket_cancel_service(struct libwebsocket_context *context) +{ +#ifndef _WIN32 + char buf = 0; + if (write(context->dummy_pipe_fds[1], &buf, sizeof(buf)) != 1) + lwsl_err("Cannot write to dummy pipe."); +#endif +} + #ifndef LWS_NO_EXTENSIONS int lws_any_extension_handled(struct libwebsocket_context *context, @@ -1498,10 +1503,8 @@ 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 + int sampled_tid; struct libwebsocket_pollargs pa; pa.fd = wsi->sock; @@ -1518,26 +1521,22 @@ lws_change_pollfd(struct libwebsocket *wsi, int _and, int _or) LWS_CALLBACK_CHANGE_MODE_POLL_FD, wsi->user_space, (void *) &pa, 0); - -#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 + * ... and the service thread is waiting ... + * then cancel it to force a restart 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) { + sampled_tid = context->service_tid; + if (sampled_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); + if (tid != sampled_tid) + libwebsocket_cancel_service(context); } } -#endif context->protocols[0].callback(context, wsi, LWS_CALLBACK_UNLOCK_POLL, @@ -2049,7 +2048,24 @@ libwebsocket_create_context(struct lws_context_creation_info *info) memset(context->lws_lookup, 0, sizeof(struct libwebsocket *) * context->max_fds); +#ifdef _WIN32 context->fds_count = 0; +#else + if (pipe(context->dummy_pipe_fds)) { + lwsl_err("Unable to create pipe\n"); + free(context->lws_lookup); + free(context->fds); + free(context); + return NULL; + } + + /* use the read end of pipe as first item */ + context->fds[0].fd = context->dummy_pipe_fds[0]; + context->fds[0].events = POLLIN; + context->fds[0].revents = 0; + context->fds_count = 1; +#endif + #ifndef LWS_NO_EXTENSIONS context->extensions = info->extensions; #endif diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h index 64db7a0..18da97d 100644 --- a/lib/libwebsockets.h +++ b/lib/libwebsockets.h @@ -69,10 +69,6 @@ typedef SSIZE_T ssize_t; #else // NOT WIN32 -/* to get ppoll() */ -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif #include #include @@ -969,6 +965,9 @@ libwebsocket_context_destroy(struct libwebsocket_context *context); LWS_VISIBLE LWS_EXTERN int libwebsocket_service(struct libwebsocket_context *context, int timeout_ms); +LWS_VISIBLE LWS_EXTERN void +libwebsocket_cancel_service(struct libwebsocket_context *context); + LWS_VISIBLE LWS_EXTERN int libwebsocket_service_fd(struct libwebsocket_context *context, struct pollfd *pollfd); diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h index 4c340a1..af5a026 100644 --- a/lib/private-libwebsockets.h +++ b/lib/private-libwebsockets.h @@ -91,9 +91,6 @@ #include #include #include - -/* to get ppoll() */ -#define __USE_GNU #include #include #include @@ -296,6 +293,17 @@ struct libwebsocket_context { int listen_service_fd; int listen_service_extraseen; + /* + * set to the Thread ID that's doing the service loop just before entry + * to poll indicates service thread likely idling in poll() + * volatile because other threads may check it as part of processing + * for pollfd event change. + */ + volatile int service_tid; +#ifndef _WIN32 + int dummy_pipe_fds[2]; +#endif + int ka_time; int ka_probes; int ka_interval; diff --git a/test-server/test-server.c b/test-server/test-server.c index 1e86146..4259f9d 100644 --- a/test-server/test-server.c +++ b/test-server/test-server.c @@ -59,6 +59,7 @@ struct pollfd *pollfds; int *fd_lookup; int count_pollfds; static volatile int force_exit = 0; +static struct libwebsocket_context *context; /* * This demo server shows how to use libwebsockets for one or more @@ -476,8 +477,7 @@ bail: * if you will call "libwebsocket_callback_on_writable" * from a different thread, return the caller thread ID * here so lws can use this information to work out if it - * should signal the ppoll() loop to exit and restart early - * (only applies if the library has LWS_HAS_PPOLL + * should signal the poll() loop to exit and restart early */ /* return pthread_getthreadid_np(); */ @@ -732,6 +732,7 @@ static struct libwebsocket_protocols protocols[] = { void sighandler(int sig) { force_exit = 1; + libwebsocket_cancel_service(context); } static struct option options[] = { @@ -755,7 +756,6 @@ int main(int argc, char **argv) char key_path[1024]; int n = 0; int use_ssl = 0; - struct libwebsocket_context *context; int opts = 0; char interface_name[128] = ""; const char *iface = NULL; -- 2.7.4