diff --git a/CMakeLists.txt b/CMakeLists.txt index 103b3e00..d58ee674 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,6 +52,7 @@ option(LWS_WITHOUT_DEBUG "Don't compile debug related code" OFF) option(LWS_WITHOUT_EXTENSIONS "Don't compile with extensions" OFF) option(LWS_WITH_LATENCY "Build latency measuring code into the library" OFF) option(LWS_WITHOUT_DAEMONIZE "Don't build the daemonization api" OFF) +option(LWS_WITH_LIBEV "Compile without support for libev" OFF) # Allow the user to override installation directories. set(LWS_INSTALL_LIB_DIR lib CACHE PATH "Installation directory for libraries") @@ -115,6 +116,11 @@ else() set(_DEBUG 1) endif() +if (LWS_WITH_LIBEV) + set(LWS_USE_LIBEV 1) + set(LWS_NO_EXTERNAL_POLL 1) +endif() + if (MINGW) set(LWS_MINGW_SUPPORT 1) endif() @@ -458,6 +464,10 @@ if (LWS_WITH_SSL) endif() endif(LWS_WITH_SSL) +if (LWS_WITH_LIBEV) + list(APPEND LIB_LIST "ev") +endif(LWS_WITH_LIBEV) + # # Platform specific libs. # @@ -866,6 +876,7 @@ message(" LWS_WITHOUT_DEBUG = ${LWS_WITHOUT_DEBUG}") message(" LWS_WITHOUT_EXTENSIONS = ${LWS_WITHOUT_EXTENSIONS}") message(" LWS_WITH_LATENCY = ${LWS_WITH_LATENCY}") message(" LWS_WITHOUT_DAEMONIZE = ${LWS_WITHOUT_DAEMONIZE}") +message(" LWS_USE_LIBEV = ${LWS_USE_LIBEV}") message("---------------------------------------------------------------------") # These will be available to parent projects including libwebsockets using add_subdirectory() diff --git a/changelog b/changelog index f9ceaa23..e3c464f1 100644 --- a/changelog +++ b/changelog @@ -39,6 +39,10 @@ If you will use another thread for this, take a lot of care about managing your list of live wsi by doing it from ESTABLISHED and CLOSED callbacks (with your own locking). +If you configure cmake with -DLWS_WITH_LIBEV=1 then the code allowing the libev +eventloop instead of the default poll() one will also be compiled in. But to +use it, you must also set the LWS_SERVER_OPTION_LIBEV flag on the context +creation info struct options member. User api changes diff --git a/config.h.cmake b/config.h.cmake index 87bd9498..a8247a99 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -26,6 +26,9 @@ /* Turn off websocket extensions */ #cmakedefine LWS_NO_EXTENSIONS +/* Enable libev io loop */ +#cmakedefine LWS_USE_LIBEV + /* Turn on latency measuring code */ #cmakedefine LWS_LATENCY diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c index 0abd6451..08a18446 100644 --- a/lib/libwebsockets.c +++ b/lib/libwebsockets.c @@ -63,7 +63,13 @@ static int log_level = LLL_ERR | LLL_WARN | LLL_NOTICE; static void lwsl_emit_stderr(int level, const char *line); static void (*lwsl_emit)(int level, const char *line) = lwsl_emit_stderr; -static const char *library_version = LWS_LIBRARY_VERSION " " LWS_BUILD_HASH; +#ifdef LWS_USE_LIBEV +#define _LWS_EV_TAG " libev" +#else +#define _LWS_EV_TAG +#endif /* LWS_USE_LIBEV */ +static const char *library_version = + LWS_LIBRARY_VERSION " " LWS_BUILD_HASH _LWS_EV_TAG; static const char * const log_level_names[] = { "ERR", @@ -173,6 +179,11 @@ insert_wsi_socket_into_fds(struct libwebsocket_context *context, wsi->position_in_fds_table = context->fds_count; context->fds[context->fds_count].fd = wsi->sock; context->fds[context->fds_count].events = POLLIN; +#ifdef LWS_USE_LIBEV + if (context && context->io_loop && LWS_LIBEV_ENABLED(context)) + ev_io_start(context->io_loop, (struct ev_io *)&wsi->w_read); + +#endif /* LWS_USE_LIBEV */ context->fds[context->fds_count++].revents = 0; /* external POLL support via protocol 0 */ @@ -431,6 +442,13 @@ just_kill_connection: * delete socket from the internal poll list if still present */ +#ifdef LWS_USE_LIBEV + if (LWS_LIBEV_ENABLED(context)) { + ev_io_stop(context->io_loop,(struct ev_io *)&wsi->w_read); + ev_io_stop(context->io_loop,(struct ev_io *)&wsi->w_write); + } +#endif /* LWS_USE_LIBEV */ + remove_wsi_socket_from_fds(context, wsi); wsi->state = WSI_STATE_DEAD_SOCKET; @@ -865,9 +883,14 @@ user_service: #endif /* one shot */ - if (pollfd) + if (pollfd) { lws_change_pollfd(wsi, POLLOUT, 0); - +#ifdef LWS_USE_LIBEV + if (LWS_LIBEV_ENABLED(context)) + ev_io_stop(context->io_loop, + (struct ev_io *)&wsi->w_write); +#endif /* LWS_USE_LIBEV */ + } #ifndef LWS_NO_EXTENSIONS notify_action: #endif @@ -974,6 +997,7 @@ libwebsocket_service_fd(struct libwebsocket_context *context, time(&now); + /* TODO: if using libev, we should probably use timeout watchers... */ if (context->last_timeout_check_s != now) { context->last_timeout_check_s = now; @@ -1240,6 +1264,36 @@ handled: return n; } +#ifdef LWS_USE_LIBEV +LWS_VISIBLE void +libwebsocket_accept_cb(struct ev_loop *loop, struct ev_io *watcher, int revents) +{ + struct pollfd eventfd; + struct lws_io_watcher *lws_io = (struct lws_io_watcher*)watcher; + struct libwebsocket_context *context = lws_io->context; + + if (revents & EV_ERROR) + return; + + eventfd.fd = watcher->fd; + eventfd.revents = EV_NONE; + if (revents & EV_READ) + eventfd.revents |= POLLIN; + + if (revents & EV_WRITE) + eventfd.revents |= POLLOUT; + + libwebsocket_service_fd(context,&eventfd); +} + +LWS_VISIBLE void +libwebsocket_sigint_cb( + struct ev_loop *loop, struct ev_signal* watcher, int revents) +{ + ev_break(loop, EVBREAK_ALL); +} +#endif /* LWS_USE_LIBEV */ + /** * libwebsocket_context_destroy() - Destroy the websocket context @@ -1394,6 +1448,10 @@ libwebsocket_service(struct libwebsocket_context *context, int timeout_ms) if (context == NULL) return 1; +#ifdef LWS_USE_LIBEV + if (context->io_loop && LWS_LIBEV_ENABLED(context)) + ev_run(context->io_loop, 0); +#endif /* LWS_USE_LIBEV */ context->service_tid = context->protocols[0].callback(context, NULL, LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0); n = poll(context->fds, context->fds_count, timeout_ms); @@ -1453,6 +1511,64 @@ libwebsocket_cancel_service(struct libwebsocket_context *context) #endif } +#ifdef LWS_USE_LIBEV +LWS_VISIBLE int +libwebsocket_initloop( + struct libwebsocket_context *context, + struct ev_loop *loop) +{ + int status = 0; + int backend; + const char * backend_name; + struct ev_io *w_accept = (ev_io *)&context->w_accept; + struct ev_signal *w_sigint = (ev_signal *)&context->w_sigint; + + if (!loop) + loop = ev_default_loop(0); + + context->io_loop = loop; + + /* + * Initialize the accept w_accept with the listening socket + * and register a callback for read operations: + */ + ev_io_init(w_accept, libwebsocket_accept_cb, + context->listen_service_fd, EV_READ); + ev_io_start(context->io_loop,w_accept); + ev_signal_init(w_sigint, libwebsocket_sigint_cb, SIGINT); + ev_signal_start(context->io_loop,w_sigint); + backend = ev_backend(loop); + + switch (backend) { + case EVBACKEND_SELECT: + backend_name = "select"; + break; + case EVBACKEND_POLL: + backend_name = "poll"; + break; + case EVBACKEND_EPOLL: + backend_name = "epoll"; + break; + case EVBACKEND_KQUEUE: + backend_name = "kqueue"; + break; + case EVBACKEND_DEVPOLL: + backend_name = "/dev/poll"; + break; + case EVBACKEND_PORT: + backend_name = "Solaris 10 \"port\""; + break; + default: + backend_name = "Unknown libev backend"; + break; + }; + + lwsl_notice(" libev backend: %s\n", backend_name); + + return status; +} +#endif /* LWS_USE_LIBEV */ + #ifndef LWS_NO_EXTENSIONS int lws_any_extension_handled(struct libwebsocket_context *context, @@ -1584,6 +1700,10 @@ libwebsocket_callback_on_writable(struct libwebsocket_context *context, } lws_change_pollfd(wsi, 0, POLLOUT); +#ifdef LWS_USE_LIBEV + if (LWS_LIBEV_ENABLED(context)) + ev_io_start(context->io_loop, (struct ev_io *)&wsi->w_write); +#endif /* LWS_USE_LIBEV */ return 1; } @@ -2036,6 +2156,14 @@ libwebsocket_create_context(struct lws_context_creation_info *info) free(context); return NULL; } + +#ifdef LWS_USE_LIBEV + if (LWS_LIBEV_ENABLED(context)) { + context->w_accept.context = context; + context->w_sigint.context = context; + } +#endif /* LWS_USE_LIBEV */ + context->lws_lookup = (struct libwebsocket **) malloc(sizeof(struct libwebsocket *) * context->max_fds); if (context->lws_lookup == NULL) { @@ -2049,23 +2177,25 @@ 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; - } + if (!LWS_LIBEV_ENABLED(context)) { + #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 + /* 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; @@ -2140,7 +2270,7 @@ libwebsocket_create_context(struct lws_context_creation_info *info) } #ifndef LWS_NO_SERVER - if (info->port) { + if (info->port != CONTEXT_PORT_NO_LISTEN) { #ifdef LWS_OPENSSL_SUPPORT context->use_ssl = info->ssl_cert_filepath != NULL && @@ -2405,7 +2535,7 @@ libwebsocket_create_context(struct lws_context_creation_info *info) #ifndef LWS_NO_SERVER /* set up our external listening socket we serve on */ - if (info->port) { + if (info->port != CONTEXT_PORT_NO_LISTEN) { int sockfd; sockfd = socket(AF_INET, SOCK_STREAM, 0); @@ -2454,6 +2584,15 @@ libwebsocket_create_context(struct lws_context_creation_info *info) compatible_close(sockfd); goto bail; } + + struct sockaddr_in sin; + socklen_t len = sizeof(sin); + if (getsockname(sockfd, (struct sockaddr *)&sin, &len) == -1) + perror("getsockname"); + else + info->port = ntohs(sin.sin_port); + + context->listen_port = info->port; wsi = (struct libwebsocket *)malloc( sizeof(struct libwebsocket)); @@ -2524,7 +2663,7 @@ libwebsocket_create_context(struct lws_context_creation_info *info) */ m = LWS_EXT_CALLBACK_CLIENT_CONTEXT_CONSTRUCT; - if (info->port) + if (info->port != CONTEXT_PORT_NO_LISTEN) m = LWS_EXT_CALLBACK_SERVER_CONTEXT_CONSTRUCT; if (info->extensions) { diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h index 18da97d7..5bdd8b69 100644 --- a/lib/libwebsockets.h +++ b/lib/libwebsockets.h @@ -80,13 +80,17 @@ typedef SSIZE_T ssize_t; #endif +#ifdef LWS_USE_LIBEV +#include +#endif /* LWS_USE_LIBEV */ + #include #ifndef LWS_EXTERN #define LWS_EXTERN extern #endif -#define CONTEXT_PORT_NO_LISTEN 0 +#define CONTEXT_PORT_NO_LISTEN -1 #define MAX_MUX_RECURSION 2 enum lws_log_levels { @@ -144,7 +148,8 @@ LWS_VISIBLE LWS_EXTERN void lwsl_hexdump(void *buf, size_t len); enum libwebsocket_context_options { LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT = 2, LWS_SERVER_OPTION_SKIP_SERVER_CANONICAL_NAME = 4, - LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT = 8 + LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT = 8, + LWS_SERVER_OPTION_LIBEV = 16 }; enum libwebsocket_callback_reasons { @@ -968,6 +973,20 @@ libwebsocket_service(struct libwebsocket_context *context, int timeout_ms); LWS_VISIBLE LWS_EXTERN void libwebsocket_cancel_service(struct libwebsocket_context *context); +#ifdef LWS_USE_LIBEV +LWS_VISIBLE LWS_EXTERN int +libwebsocket_initloop( + struct libwebsocket_context *context, struct ev_loop *loop); + +LWS_VISIBLE void +libwebsocket_accept_cb(struct ev_loop *loop, struct ev_io *watcher, + int revents); + +LWS_VISIBLE void +libwebsocket_sigint_cb( + struct ev_loop *loop, struct ev_signal *watcher, int revents); +#endif /* LWS_USE_LIBEV */ + 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 0d02f987..f56aba5c 100644 --- a/lib/private-libwebsockets.h +++ b/lib/private-libwebsockets.h @@ -92,6 +92,10 @@ #include #include #include +#ifdef LWS_USE_LIBEV +#include +#endif /* LWS_USE_LIBEV */ + #include #include @@ -265,10 +269,27 @@ enum { struct libwebsocket_protocols; struct libwebsocket; +#ifdef LWS_USE_LIBEV +struct lws_io_watcher { + struct ev_io watcher; + struct libwebsocket_context* context; +}; + +struct lws_signal_watcher { + struct ev_signal watcher; + struct libwebsocket_context* context; +}; +#endif /* LWS_USE_LIBEV */ + struct libwebsocket_context { struct pollfd *fds; struct libwebsocket **lws_lookup; /* fd to wsi */ int fds_count; +#ifdef LWS_USE_LIBEV + struct ev_loop* io_loop; + struct lws_io_watcher w_accept; + struct lws_signal_watcher w_sigint; +#endif /* LWS_USE_LIBEV */ int max_fds; int listen_port; const char *iface; @@ -327,6 +348,13 @@ struct libwebsocket_context { void *user_space; }; +#ifdef LWS_USE_LIBEV +#define LWS_LIBEV_ENABLED(context) (context->options & LWS_SERVER_OPTION_LIBEV) +#else +#define LWS_LIBEV_ENABLED(context) (0) +#endif + + enum uri_path_states { URIPS_IDLE, URIPS_SEEN_SLASH, @@ -415,6 +443,10 @@ struct libwebsocket { /* lifetime members */ +#ifdef LWS_USE_LIBEV + struct lws_io_watcher w_read; + struct lws_io_watcher w_write; +#endif /* LWS_USE_LIBEV */ const struct libwebsocket_protocols *protocol; #ifndef LWS_NO_EXTENSIONS struct libwebsocket_extension * diff --git a/lib/server.c b/lib/server.c index 0178df2f..2e76cd6d 100644 --- a/lib/server.c +++ b/lib/server.c @@ -188,6 +188,9 @@ int lws_server_socket_service(struct libwebsocket_context *context, /* one shot */ lws_change_pollfd(wsi, POLLOUT, 0); +#ifdef LWS_USE_LIBEV + ev_io_stop(context->io_loop,(struct ev_io*)&(wsi->w_write)); +#endif /* LWS_USE_LIBEV */ if (wsi->state != WSI_STATE_HTTP_ISSUING_FILE) { n = user_callback_handle_rxflow( @@ -272,6 +275,18 @@ int lws_server_socket_service(struct libwebsocket_context *context, (context->protocols[0].callback)(context, new_wsi, LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED, NULL, NULL, 0); +#ifdef LWS_USE_LIBEV + new_wsi->w_read.context = context; + new_wsi->w_write.context = context; + /* + new_wsi->w_read.wsi = new_wsi; + new_wsi->w_write.wsi = new_wsi; + */ + struct ev_io* w_read = (struct ev_io*)&(new_wsi->w_read); + struct ev_io* w_write = (struct ev_io*)&(new_wsi->w_write); + ev_io_init(w_read,libwebsocket_accept_cb,accept_fd,EV_READ); + ev_io_init(w_write,libwebsocket_accept_cb,accept_fd,EV_WRITE); +#endif /* LWS_USE_LIBEV */ #ifdef LWS_OPENSSL_SUPPORT new_wsi->ssl = NULL; @@ -338,6 +353,9 @@ int lws_server_socket_service(struct libwebsocket_context *context, case LWS_CONNMODE_SSL_ACK_PENDING: lws_change_pollfd(wsi, POLLOUT, 0); +#ifdef LWS_USE_LIBEV + ev_io_stop(context->io_loop,(struct ev_io*)&(wsi->w_write)); +#endif /* LWS_USE_LIBEV */ lws_latency_pre(context, wsi); @@ -382,11 +400,17 @@ int lws_server_socket_service(struct libwebsocket_context *context, if (m == SSL_ERROR_WANT_READ) { lws_change_pollfd(wsi, 0, POLLIN); +#ifdef LWS_USE_LIBEV + ev_io_start(context->io_loop,(struct ev_io*)&(wsi->w_read)); +#endif /* LWS_USE_LIBEV */ lwsl_info("SSL_ERROR_WANT_READ\n"); break; } if (m == SSL_ERROR_WANT_WRITE) { lws_change_pollfd(wsi, 0, POLLOUT); +#ifdef LWS_USE_LIBEV + ev_io_start(context->io_loop,(struct ev_io*)&(wsi->w_write)); +#endif /* LWS_USE_LIBEV */ break; } lwsl_debug("SSL_accept failed skt %u: %s\n",