libev port
authorAndrew Canaday <andrew.canaday@nytimes.com>
Sun, 23 Mar 2014 05:25:07 +0000 (13:25 +0800)
committerAndy Green <andy.green@linaro.org>
Sun, 23 Mar 2014 05:25:07 +0000 (13:25 +0800)
merged by andy@warmcat.com via https://github.com/gaby64/libwebsockets-libev

To use, you need to both

 - cmake ---> -DLWS_USE_LIBEV=1

 - info->options must have LWS_SERVER_OPTION_LIBEV set when creating the context

this is so a single library can be built for distros to support apps that use
normal polling and apps that use libev polling.

CMakeLists.txt
changelog
config.h.cmake
lib/libwebsockets.c
lib/libwebsockets.h
lib/private-libwebsockets.h
lib/server.c

index 103b3e0..d58ee67 100644 (file)
@@ -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()
index f9ceaa2..e3c464f 100644 (file)
--- 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
index 87bd949..a8247a9 100644 (file)
@@ -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
 
index 0abd645..08a1844 100644 (file)
@@ -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) {
index 18da97d..5bdd8b6 100644 (file)
@@ -80,13 +80,17 @@ typedef SSIZE_T ssize_t;
 
 #endif
 
+#ifdef LWS_USE_LIBEV
+#include <ev.h>
+#endif /* LWS_USE_LIBEV */
+
 #include <assert.h>
 
 #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);
index 0d02f98..f56aba5 100644 (file)
 #include <netinet/tcp.h>
 #include <arpa/inet.h>
 #include <poll.h>
+#ifdef LWS_USE_LIBEV
+#include <ev.h>
+#endif /* LWS_USE_LIBEV */
+
 #include <sys/mman.h>
 #include <sys/time.h>
 
@@ -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 *
index 0178df2..2e76cd6 100644 (file)
@@ -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",