libuv integration
authorAndy Green <andy.green@linaro.org>
Sun, 14 Feb 2016 01:27:41 +0000 (09:27 +0800)
committerAndy Green <andy.green@linaro.org>
Sun, 14 Feb 2016 01:31:13 +0000 (09:31 +0800)
This gets the libuv stuff plumbed in and working.

Currently it's only workable for some service thread, and there
is an isolated valgrind problem left

==28425== 128 bytes in 1 blocks are definitely lost in loss record 3 of 3
==28425==    at 0x4C28C50: malloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==28425==    by 0x4C2AB1E: realloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==28425==    by 0x58BBB27: maybe_resize (core.c:748)
==28425==    by 0x58BBB27: uv__io_start (core.c:787)
==28425==    by 0x58C1B80: uv__signal_loop_once_init (signal.c:225)
==28425==    by 0x58C1B80: uv_signal_init (signal.c:260)
==28425==    by 0x58BF7A6: uv_loop_init (loop.c:66)
==28425==    by 0x4157F5: lws_uv_initloop (libuv.c:89)
==28425==    by 0x405536: main (test-server-libuv.c:284)

libuv wants to sign off on all libuv 'handles' that will close, and
callback to do the close confirmation asynchronously.  The wsi close function
is adapted when libuv is in use to work with libuv accordingly and exit the uv
loop the number of remaining wsi is zero.

Signed-off-by: Andy Green <andy.green@linaro.org>
20 files changed:
CMakeLists.txt
README.coding.md
changelog
lib/client-handshake.c
lib/client.c
lib/context.c
lib/extension.c
lib/libev.c
lib/libuv.c [new file with mode: 0644]
lib/libwebsockets.c
lib/libwebsockets.h
lib/lws-plat-unix.c
lib/pollfd.c
lib/private-libwebsockets.h
lib/server.c
lib/service.c
lib/ssl.c
lws_config.h.in
test-server/test-server-libev.c
test-server/test-server-libuv.c

index f191117..791ca0a 100644 (file)
@@ -54,6 +54,7 @@ option(LWS_USE_CYASSL "Use CyaSSL replacement for OpenSSL. When setting this, yo
 option(LWS_USE_WOLFSSL "Use wolfSSL replacement for OpenSSL. When setting this, you also need to specify LWS_WOLFSSL_LIBRARIES and LWS_WOLFSSL_INCLUDE_DIRS" OFF)
 option(LWS_WITH_ZLIB "Include zlib support (required for extensions)" ON)
 option(LWS_WITH_LIBEV "Compile with support for libev" OFF)
+option(LWS_WITH_LIBUV "Compile with support for libuv" OFF)
 option(LWS_USE_BUNDLED_ZLIB "Use bundled zlib version (Windows only)" ${LWS_USE_BUNDLED_ZLIB_DEFAULT})
 option(LWS_SSL_CLIENT_USE_OS_CA_CERTS "SSL support should make use of the OS-installed CA root certs" ON)
 option(LWS_WITHOUT_BUILTIN_GETIFADDRS "Don't use the BSD getifaddrs implementation from libwebsockets if it is missing (this will result in a compilation error) ... The default is to assume that your libc provides it. On some systems such as uclibc it doesn't exist." OFF)
@@ -135,6 +136,8 @@ set(LWS_WOLFSSL_LIBRARIES CACHE PATH "Path to the wolfSSL library")
 set(LWS_WOLFSSL_INCLUDE_DIRS CACHE PATH "Path to the wolfSSL include directory")
 set(LWS_LIBEV_LIBRARIES CACHE PATH "Path to the libev library")
 set(LWS_LIBEV_INCLUDE_DIRS CACHE PATH "Path to the libev include directory")
+set(LWS_LIBUV_LIBRARIES CACHE PATH "Path to the libuv library")
+set(LWS_LIBUV_INCLUDE_DIRS CACHE PATH "Path to the libuv include directory")
 
 if (NOT LWS_WITH_SSL)
        set(LWS_WITHOUT_BUILTIN_SHA1 OFF)
@@ -187,6 +190,15 @@ if (LWS_WITH_LIBEV)
        endif()
 endif()
 
+if (LWS_WITH_LIBUV)
+       if ("${LWS_LIBUV_LIBRARIES}" STREQUAL "" OR "${LWS_LIBUV_INCLUDE_DIRS}" STREQUAL "")
+       else()
+               set(LIBUV_LIBRARIES ${LWS_LIBUV_LIBRARIES})
+               set(LIBUV_INCLUDE_DIRS ${LWS_LIBUV_INCLUDE_DIRS})
+               set(LIBUV_FOUND 1)
+       endif()
+endif()
+
 # FIXME: This must be runtime-only option.
 # The base dir where the test-apps look for the SSL certs.
 set(LWS_OPENSSL_CLIENT_CERTS ../share CACHE PATH "Server SSL certificate directory")
@@ -233,6 +245,10 @@ if (LWS_WITH_LIBEV)
        set(LWS_USE_LIBEV 1)
 endif()
 
+if (LWS_WITH_LIBUV)
+       set(LWS_USE_LIBUV 1)
+endif()
+
 if (LWS_IPV6)
        set(LWS_USE_IPV6 1)
 endif()
@@ -461,7 +477,12 @@ endif()
 if (LWS_WITH_LIBEV)
        list(APPEND SOURCES
                lib/libev.c)
-endif(LWS_WITH_LIBEV)
+endif()
+
+if (LWS_WITH_LIBUV)
+       list(APPEND SOURCES
+               lib/libuv.c)
+endif()
 
 # Add helper files for Windows.
 if (WIN32)
@@ -676,6 +697,20 @@ if (LWS_WITH_LIBEV)
        list(APPEND LIB_LIST ${LIBEV_LIBRARIES})
 endif(LWS_WITH_LIBEV)
 
+if (LWS_WITH_LIBUV)
+       if (NOT LIBUV_FOUND)
+               find_path(LIBUV_INCLUDE_DIRS NAMES uv.h)
+               find_library(LIBUV_LIBRARIES NAMES uv)
+               if(LIBUV_INCLUDE_DIRS AND LIBUV_LIBRARIES)
+                       set(LIBUV_FOUND 1)
+               endif()
+       endif()
+       message("libuv include dir: ${LIBUV_INCLUDE_DIRS}")
+       message("libuv libraries: ${LIBUV_LIBRARIES}")
+       include_directories("${LIBUV_INCLUDE_DIRS}")
+       list(APPEND LIB_LIST ${LIBUV_LIBRARIES})
+endif()
+
 #
 # Platform specific libs.
 #
@@ -808,6 +843,15 @@ if (NOT LWS_WITHOUT_TESTAPPS)
                                        "test-server/test-server-mirror.c"
                                        "test-server/test-server-echogen.c")
                        endif()
+                       if (UNIX AND NOT ((CMAKE_C_COMPILER_ID MATCHES "Clang") OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang"))
+                               AND LWS_WITH_LIBUV)
+                               create_test_app(test-server-libuv
+                                       "test-server/test-server-libuv.c"
+                                       "test-server/test-server-http.c"
+                                       "test-server/test-server-dumb-increment.c"
+                                       "test-server/test-server-mirror.c"
+                                       "test-server/test-server-echogen.c")
+                       endif()
                endif()
 
                #
@@ -1134,6 +1178,7 @@ 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(" LWS_USE_LIBUV = ${LWS_USE_LIBUV}")
 message(" LWS_IPV6 = ${LWS_IPV6}")
 message(" LWS_WITH_HTTP2 = ${LWS_WITH_HTTP2}")
 message(" LWS_MBED3 = ${LWS_MBED3}")
index d425bac..72a1240 100644 (file)
@@ -378,3 +378,19 @@ libwebsockets-test-server-pthread does it, for the FD locking callbacks.
 There is no knowledge or dependency in lws itself about pthreads.  How the
 locking is implemented is entirely up to the user code.
 
+
+Libev / Libuv support
+---------------------
+
+You can select either or both
+
+-DLWS_WITH_LIBEV=1
+-DLWS_WITH_LIBUV=1
+
+at cmake configure-time.  The user application may use one of the
+context init options flags
+
+LWS_SERVER_OPTION_LIBEV
+LWS_SERVER_OPTION_LIBUV
+
+to indicate it will use either of the event libraries.
index 5f9e51d..c9bbfc6 100644 (file)
--- a/changelog
+++ b/changelog
@@ -222,6 +222,24 @@ had just been accepted by lws' own listen socket.
 
 9) X-Real-IP: header has been added as WSI_TOKEN_HTTP_X_REAL_IP
 
+10) Libuv support is added, there are new related user apis
+
+typedef void (lws_uv_signal_cb_t)(uv_loop_t *l, uv_signal_t *w, int revents);
+
+LWS_VISIBLE LWS_EXTERN int
+lws_uv_sigint_cfg(struct lws_context *context, int use_uv_sigint,
+                 lws_uv_signal_cb_t *cb);
+
+LWS_VISIBLE LWS_EXTERN int
+lws_uv_initloop(struct lws_context *context, uv_loop_t *loop, int tsi);
+
+LWS_VISIBLE void
+lws_uv_sigint_cb(uv_loop_t *loop, uv_signal_t *watcher, int revents);
+
+and CMAKE option
+
+LWS_WITH_LIBUV
+
 
 User api changes
 ----------------
@@ -262,6 +280,21 @@ the library.
 of value 73.  That's now corrected and WSI_TOKEN_PROXY moved to his own place at
 77.
 
+9) With the addition of libuv support, libev is not the only event loop
+library in town and his api names must be elaborated with _ev_
+
+  Callback typedef: lws_signal_cb ---> lws_ev_signal_cb_t
+  lws_sigint_cfg --> lws_ev_sigint_cfg
+  lws_initloop --> lws_ev_initloop
+  lws_sigint_cb --> lws_ev_sigint_cb
+
+10) Libev support is made compatible with multithreaded service,
+lws_ev_initloop (was lws_initloop) gets an extra argument for the
+thread service index (use 0 if you will just have 1 service thread).
+
+LWS_VISIBLE LWS_EXTERN int
+lws_uv_initloop(struct lws_context *context, uv_loop_t *loop, int tsi);
+
 
 v1.6.0-chrome48-firefox42
 =======================
index 76b2fbd..75d2032 100644 (file)
@@ -233,7 +233,6 @@ lws_client_connect_2(struct lws *wsi)
                         */
                        if (lws_change_pollfd(wsi, 0, LWS_POLLOUT))
                                goto failed;
-                       lws_libev_io(wsi, LWS_EV_START | LWS_EV_WRITE);
 
                        return wsi;
                }
@@ -461,6 +460,8 @@ lws_client_connect_via_info(struct lws_client_connect_info *i)
        }
        lwsl_client("lws_client_connect: direct conn\n");
 
+       wsi->context->count_wsi_allocated++;
+
        return lws_client_connect_2(wsi);
 
 bail1:
index 849da5a..fd9fb57 100644 (file)
@@ -144,7 +144,6 @@ lws_client_socket_service(struct lws_context *context, struct lws *wsi,
                 */
                if (lws_change_pollfd(wsi, LWS_POLLOUT, 0))
                        return -1;
-               lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_WRITE);
 
 #ifdef LWS_OPENSSL_SUPPORT
                /* we can retry this... just cook the SSL BIO the first time */
index 20650cb..5aed178 100644 (file)
@@ -199,8 +199,18 @@ lws_create_context(struct lws_context_creation_info *info)
         * before invoking lws_initloop:
         */
        context->use_ev_sigint = 1;
-       context->lws_ev_sigint_cb = &lws_sigint_cb;
+       context->lws_ev_sigint_cb = &lws_ev_sigint_cb;
 #endif /* LWS_USE_LIBEV */
+#ifdef LWS_USE_LIBUV
+       /* (Issue #264) In order to *avoid breaking backwards compatibility*, we
+        * enable libev mediated SIGINT handling with a default handler of
+        * lws_sigint_cb. The handler can be overridden or disabled
+        * by invoking lws_sigint_cfg after creating the context, but
+        * before invoking lws_initloop:
+        */
+       context->use_ev_sigint = 1;
+       context->lws_uv_sigint_cb = &lws_uv_sigint_cb;
+#endif
 
        lwsl_info(" mem: context:         %5u bytes (%d ctx + (%d thr x %d))\n",
                  sizeof(struct lws_context) +
@@ -324,6 +334,7 @@ LWS_VISIBLE void
 lws_context_destroy(struct lws_context *context)
 {
        const struct lws_protocols *protocol = NULL;
+       struct lws_context_per_thread *pt;
        struct lws wsi;
        int n, m;
 
@@ -343,59 +354,59 @@ lws_context_destroy(struct lws_context *context)
                lwsl_notice("Worst latency: %s\n", context->worst_latency_info);
 #endif
 
-       while (m--)
+       while (m--) {
+               pt = &context->pt[m];
+
                for (n = 0; (unsigned int)n < context->pt[m].fds_count; n++) {
-                       struct lws *wsi = wsi_from_fd(context, context->pt[m].fds[n].fd);
+                       struct lws *wsi = wsi_from_fd(context, pt->fds[n].fd);
                        if (!wsi)
                                continue;
-                       lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY
+
+                       lws_close_free_wsi(wsi,
+                               LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY
                                /* no protocol close */);
                        n--;
                }
-
+       }
        /*
         * give all extensions a chance to clean up any per-context
         * allocations they might have made
         */
 
        n = lws_ext_cb_all_exts(context, NULL,
-                       LWS_EXT_CB_SERVER_CONTEXT_DESTRUCT, NULL, 0);
+                               LWS_EXT_CB_SERVER_CONTEXT_DESTRUCT, NULL, 0);
 
        n = lws_ext_cb_all_exts(context, NULL,
-                       LWS_EXT_CB_CLIENT_CONTEXT_DESTRUCT, NULL, 0);
+                               LWS_EXT_CB_CLIENT_CONTEXT_DESTRUCT, NULL, 0);
 
        /*
         * inform all the protocols that they are done and will have no more
         * callbacks
         */
        protocol = context->protocols;
-       if (protocol) {
+       if (protocol)
                while (protocol->callback) {
-                       protocol->callback(&wsi,
-                                          LWS_CALLBACK_PROTOCOL_DESTROY,
+                       protocol->callback(&wsi, LWS_CALLBACK_PROTOCOL_DESTROY,
                                           NULL, NULL, 0);
                        protocol++;
                }
-       }
-#ifdef LWS_USE_LIBEV
-    uv_poll_stop(&context->w_accept.watcher);
-    //ev_io_stop(context->io_loop, &context->w_accept.watcher);
-    //if (context->use_ev_sigint)
-        //ev_signal_stop(context->io_loop, &context->w_sigint.watcher);
-#endif /* LWS_USE_LIBEV */
 
        for (n = 0; n < context->count_threads; n++) {
+               pt = &context->pt[n];
+
+               lws_libev_destroyloop(context, n);
+               lws_libuv_destroyloop(context, n);
+
                lws_free_set_NULL(context->pt[n].serv_buf);
-               if (context->pt[n].ah_pool)
-                       lws_free(context->pt[n].ah_pool);
-               if (context->pt[n].http_header_data)
-                       lws_free(context->pt[n].http_header_data);
+               if (pt->ah_pool)
+                       lws_free(pt->ah_pool);
+               if (pt->http_header_data)
+                       lws_free(pt->http_header_data);
        }
-
        lws_plat_context_early_destroy(context);
        lws_ssl_context_destroy(context);
        if (context->pt[0].fds)
-               lws_free(context->pt[0].fds);
+               lws_free_set_NULL(context->pt[0].fds);
 
        lws_plat_context_late_destroy(context);
 
index 990f586..f093850 100644 (file)
@@ -171,6 +171,9 @@ int lws_ext_cb_active(struct lws *wsi, int reason, void *arg, int len)
                                 wsi->active_extensions[n]->name, reason);
                        return -1;
                }
+               /* valgrind... */
+               if (reason == LWS_EXT_CB_DESTROY)
+                       wsi->act_ext_user[n] = NULL;
                if (m > handled)
                        handled = m;
        }
index a7fd1cb..40db6ed 100644 (file)
@@ -30,24 +30,24 @@ void lws_feature_status_libev(struct lws_context_creation_info *info)
 }
 
 static void
-lws_accept_cb(uv_poll_t *watcher, int status, int revents)
+lws_accept_cb(struct ev_loop *loop, struct ev_io *watcher, int revents)
 {
        struct lws_io_watcher *lws_io = container_of(watcher,
-                                               struct lws_io_watcher, watcher);
+                                       struct lws_io_watcher, ev_watcher);
        struct lws_context *context = lws_io->context;
        struct lws_pollfd eventfd;
 
-    if (status < 0/*revents & EV_ERROR*/)
-        return;
+       if (revents & EV_ERROR)
+               return;
 
-    eventfd.fd = watcher->io_watcher.fd;
+       eventfd.fd = watcher->fd;
        eventfd.events = 0;
-    eventfd.revents = 0;//EV_NONE;
-    if (revents & UV_READABLE) {
+       eventfd.revents = EV_NONE;
+       if (revents & EV_READ) {
                eventfd.events |= LWS_POLLIN;
                eventfd.revents |= LWS_POLLIN;
        }
-    if (revents & UV_WRITABLE) {
+       if (revents & EV_WRITE) {
                eventfd.events |= LWS_POLLOUT;
                eventfd.revents |= LWS_POLLOUT;
        }
@@ -55,54 +55,51 @@ lws_accept_cb(uv_poll_t *watcher, int status, int revents)
 }
 
 LWS_VISIBLE void
-lws_sigint_cb(uv_loop_t *loop, uv_signal_t *watcher, int revents)
+lws_ev_sigint_cb(struct ev_loop *loop, struct ev_signal *watcher, int revents)
 {
-    //ev_break(loop, EVBREAK_ALL);
+       ev_break(loop, EVBREAK_ALL);
 }
 
 LWS_VISIBLE int
-lws_sigint_cfg(struct lws_context *context, int use_ev_sigint,
-              lws_ev_signal_cb* cb)
+lws_ev_sigint_cfg(struct lws_context *context, int use_ev_sigint,
+                 lws_ev_signal_cb_t *cb)
 {
        context->use_ev_sigint = use_ev_sigint;
        if (cb)
                context->lws_ev_sigint_cb = cb;
        else
-               context->lws_ev_sigint_cb = &lws_sigint_cb;
+               context->lws_ev_sigint_cb = &lws_ev_sigint_cb;
 
        return 0;
 }
 
 LWS_VISIBLE int
-lws_initloop(struct lws_context *context, uv_loop_t *loop)
+lws_ev_initloop(struct lws_context *context, struct ev_loop *loop, int tsi)
 {
-    //uv_signal_t *w_sigint = &context->w_sigint.watcher;
-    uv_poll_t *w_accept = &context->w_accept.watcher;
-    //const char * backend_name;
+       struct ev_signal *w_sigint = &context->pt[tsi].w_sigint.ev_watcher;
+       struct ev_io *w_accept = &context->pt[tsi].w_accept.ev_watcher;
+       const char * backend_name;
        int status = 0;
-    //int backend;
-    int m = 0; /* !!! TODO add pt support */
+       int backend;
 
        if (!loop)
-        loop = uv_default_loop();
+               loop = ev_loop_new(0);
 
-       context->io_loop = loop;
+       context->pt[tsi].io_loop_ev = loop;
 
        /*
         * Initialize the accept w_accept with the listening socket
         * and register a callback for read operations
         */
-    uv_poll_init(context->io_loop, w_accept, context->pt[m].lserv_fd);
-    uv_poll_start(w_accept, UV_READABLE, lws_accept_cb);
-    //ev_io_init(w_accept, lws_accept_cb, context->pt[m].lserv_fd, UV_READABLE);
-    //ev_io_start(context->io_loop,w_accept);
+       ev_io_init(w_accept, lws_accept_cb, context->pt[tsi].lserv_fd, EV_READ);
+       ev_io_start(context->pt[tsi].io_loop_ev, w_accept);
 
        /* Register the signal watcher unless the user says not to */
        if (context->use_ev_sigint) {
-        //ev_signal_init(w_sigint, context->lws_ev_sigint_cb, SIGINT);
-        //ev_signal_start(context->io_loop,w_sigint);
+               ev_signal_init(w_sigint, context->lws_ev_sigint_cb, SIGINT);
+               ev_signal_start(context->pt[tsi].io_loop_ev, w_sigint);
        }
-    /*backend = ev_backend(loop);
+       backend = ev_backend(loop);
 
        switch (backend) {
        case EVBACKEND_SELECT:
@@ -128,82 +125,90 @@ lws_initloop(struct lws_context *context, uv_loop_t *loop)
                break;
        }
 
-    lwsl_notice(" libev backend: %s\n", backend_name);*/
+       lwsl_notice(" libev backend: %s\n", backend_name);
 
        return status;
 }
 
+void
+lws_libev_destroyloop(struct lws_context *context, int tsi)
+{
+       struct lws_context_per_thread *pt = &context->pt[tsi];
+
+       if (!(context->options & LWS_SERVER_OPTION_LIBEV))
+               return;
+
+       ev_io_stop(pt->io_loop_ev, &pt->w_accept.ev_watcher);
+       if (context->use_ev_sigint)
+               ev_signal_stop(pt->io_loop_ev,
+                      &pt->w_sigint.ev_watcher);
+       if (!pt->ev_loop_foreign)
+               ev_loop_destroy(pt->io_loop_ev);
+}
+
 LWS_VISIBLE void
 lws_libev_accept(struct lws *new_wsi, int accept_fd)
 {
        struct lws_context *context = lws_get_context(new_wsi);
-    uv_poll_t *r = &new_wsi->w_read.watcher;
-    //uv_poll_t *w = &new_wsi->w_write.watcher;
+       struct ev_io *r = &new_wsi->w_read.ev_watcher;
+       struct ev_io *w = &new_wsi->w_write.ev_watcher;
 
        if (!LWS_LIBEV_ENABLED(context))
                return;
 
        new_wsi->w_read.context = context;
        new_wsi->w_write.context = context;
-    uv_poll_init(context->io_loop, r, accept_fd);
-    //ev_io_init(r, lws_accept_cb, accept_fd, UV_READABLE);
-    //ev_io_init(w, lws_accept_cb, accept_fd, UV_WRITABLE);
+       ev_io_init(r, lws_accept_cb, accept_fd, EV_READ);
+       ev_io_init(w, lws_accept_cb, accept_fd, EV_WRITE);
 }
 
 LWS_VISIBLE void
 lws_libev_io(struct lws *wsi, int flags)
 {
        struct lws_context *context = lws_get_context(wsi);
-    int current_events = wsi->w_read.watcher.io_watcher.pevents & (UV_READABLE | UV_WRITABLE);
+       struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
 
        if (!LWS_LIBEV_ENABLED(context))
                return;
 
-       if (!context->io_loop)
+       if (!pt->io_loop_ev || context->being_destroyed)
                return;
 
        assert((flags & (LWS_EV_START | LWS_EV_STOP)) &&
               (flags & (LWS_EV_READ | LWS_EV_WRITE)));
 
        if (flags & LWS_EV_START) {
-        if (flags & LWS_EV_WRITE)
-            current_events |= UV_WRITABLE;
-            //ev_io_start(context->io_loop, &wsi->w_write.watcher);
-        if (flags & LWS_EV_READ)
-            current_events |= UV_READABLE;
-            //ev_io_start(context->io_loop, &wsi->w_read.watcher);
-
-        uv_poll_start(&wsi->w_read.watcher, current_events, lws_accept_cb);
+               if (flags & LWS_EV_WRITE)
+                       ev_io_start(pt->io_loop_ev, &wsi->w_write.ev_watcher);
+               if (flags & LWS_EV_READ)
+                       ev_io_start(pt->io_loop_ev, &wsi->w_read.ev_watcher);
        } else {
-        if (flags & LWS_EV_WRITE)
-            current_events &= ~UV_WRITABLE;
-            //ev_io_stop(context->io_loop, &wsi->w_write.watcher);
-        if (flags & LWS_EV_READ)
-            current_events &= ~UV_READABLE;
-            //ev_io_stop(context->io_loop, &wsi->w_read.watcher);
-
-        if (!(current_events & (UV_READABLE | UV_WRITABLE)))
-            uv_poll_stop(&wsi->w_read.watcher);
-        else
-            uv_poll_start(&wsi->w_read.watcher, current_events, lws_accept_cb);
+               if (flags & LWS_EV_WRITE)
+                       ev_io_stop(pt->io_loop_ev, &wsi->w_write.ev_watcher);
+               if (flags & LWS_EV_READ)
+                       ev_io_stop(pt->io_loop_ev, &wsi->w_read.ev_watcher);
        }
 }
 
 LWS_VISIBLE int
 lws_libev_init_fd_table(struct lws_context *context)
 {
+       int n;
+
        if (!LWS_LIBEV_ENABLED(context))
                return 0;
 
-       context->w_accept.context = context;
-       context->w_sigint.context = context;
+       for (n = 0; n < context->count_threads; n++) {
+               context->pt[n].w_accept.context = context;
+               context->pt[n].w_sigint.context = context;
+       }
 
        return 1;
 }
 
 LWS_VISIBLE void
-lws_libev_run(const struct lws_context *context)
+lws_libev_run(const struct lws_context *context, int tsi)
 {
-       if (context->io_loop && LWS_LIBEV_ENABLED(context))
-        uv_run(context->io_loop, 0);
+       if (context->pt[tsi].io_loop_ev && LWS_LIBEV_ENABLED(context))
+               ev_run(context->pt[tsi].io_loop_ev, 0);
 }
diff --git a/lib/libuv.c b/lib/libuv.c
new file mode 100644 (file)
index 0000000..e3d68c6
--- /dev/null
@@ -0,0 +1,301 @@
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010-2016 Andy Green <andy@warmcat.com>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation:
+ *  version 2.1 of the License.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *  MA  02110-1301  USA
+ */
+
+#include "private-libwebsockets.h"
+
+void
+lws_feature_status_libuv(struct lws_context_creation_info *info)
+{
+       if (info->options & LWS_SERVER_OPTION_LIBUV)
+               lwsl_notice("libuv support compiled in and enabled\n");
+       else
+               lwsl_notice("libuv support compiled in but disabled\n");
+}
+
+static void
+lws_accept_cb(uv_poll_t *watcher, int status, int revents)
+{
+       struct lws_io_watcher *lws_io = container_of(watcher,
+                                       struct lws_io_watcher, uv_watcher);
+       struct lws_context *context = lws_io->context;
+       struct lws_pollfd eventfd;
+
+       if (status < 0)
+               return;
+
+       eventfd.fd = watcher->io_watcher.fd;
+       eventfd.events = 0;
+       eventfd.revents = 0;//EV_NONE;
+       if (revents & UV_READABLE) {
+               eventfd.events |= LWS_POLLIN;
+               eventfd.revents |= LWS_POLLIN;
+       }
+       if (revents & UV_WRITABLE) {
+               eventfd.events |= LWS_POLLOUT;
+               eventfd.revents |= LWS_POLLOUT;
+       }
+       lws_service_fd(context, &eventfd);
+}
+
+LWS_VISIBLE void
+lws_uv_sigint_cb(uv_loop_t *loop, uv_signal_t *watcher, int revents)
+{
+    //ev_break(loop, EVBREAK_ALL);
+}
+
+LWS_VISIBLE int
+lws_uv_sigint_cfg(struct lws_context *context, int use_uv_sigint,
+                 lws_uv_signal_cb_t *cb)
+{
+       context->use_ev_sigint = use_uv_sigint;
+       if (cb)
+               context->lws_uv_sigint_cb = cb;
+       else
+               context->lws_uv_sigint_cb = &lws_uv_sigint_cb;
+
+       return 0;
+}
+
+static const int sigs[] = { SIGINT, SIGTERM, SIGSEGV, SIGFPE };
+
+LWS_VISIBLE int
+lws_uv_initloop(struct lws_context *context, uv_loop_t *loop, uv_signal_cb cb,
+               int tsi)
+{
+       struct lws_context_per_thread *pt = &context->pt[tsi];
+       struct lws *wsi = wsi_from_fd(context, pt->lserv_fd);
+       int status = 0, n;
+
+       if (!loop) {
+               loop = lws_malloc(sizeof(*loop));
+               uv_loop_init(loop);
+               pt->ev_loop_foreign = 0;
+       } else
+               pt->ev_loop_foreign = 1;
+
+       pt->io_loop_uv = loop;
+
+       assert(ARRAY_SIZE(sigs) <= ARRAY_SIZE(pt->signals));
+       for (n = 0; n < ARRAY_SIZE(sigs); n++) {
+               uv_signal_init(loop, &pt->signals[n]);
+               uv_signal_start(&pt->signals[n], cb, sigs[n]);
+       }
+
+       /*
+        * Initialize the accept wsi read watcher with the listening socket
+        * and register a callback for read operations
+        *
+        * We have to do it here because the uv loop(s) are not
+        * initialized until after context creation.
+        */
+       if (wsi) {
+               wsi->w_read.context = context;
+               uv_poll_init(pt->io_loop_uv, &wsi->w_read.uv_watcher, pt->lserv_fd);
+               uv_poll_start(&wsi->w_read.uv_watcher, UV_READABLE, lws_accept_cb);
+       }
+
+       return status;
+}
+
+void
+lws_libuv_destroyloop(struct lws_context *context, int tsi)
+{
+       struct lws_context_per_thread *pt = &context->pt[tsi];
+       int m;
+
+       if (!(context->options & LWS_SERVER_OPTION_LIBUV))
+               return;
+
+       if (context->use_ev_sigint)
+               uv_signal_stop(&pt->w_sigint.uv_watcher);
+       for (m = 0; m < ARRAY_SIZE(sigs); m++)
+               uv_signal_stop(&pt->signals[m]);
+       if (!pt->ev_loop_foreign) {
+               m = uv_loop_close(pt->io_loop_uv);
+               lwsl_debug("%s: uv_loop_close: %d\n", __func__, m);
+               lws_free(pt->io_loop_uv);
+       }
+}
+
+void
+lws_libuv_accept(struct lws *wsi, int accept_fd)
+{
+       struct lws_context *context = lws_get_context(wsi);
+       struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
+
+       if (!LWS_LIBUV_ENABLED(context))
+               return;
+
+       lwsl_debug("%s: new wsi %p\n", __func__, wsi);
+
+       wsi->w_read.context = context;
+
+       uv_poll_init(pt->io_loop_uv, &wsi->w_read.uv_watcher, accept_fd);
+}
+
+void
+lws_libuv_io(struct lws *wsi, int flags)
+{
+       struct lws_context *context = lws_get_context(wsi);
+       struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
+       int current_events = wsi->w_read.uv_watcher.io_watcher.pevents &
+                            (UV_READABLE | UV_WRITABLE);
+       struct lws_io_watcher *w = &wsi->w_read;
+
+       if (!LWS_LIBUV_ENABLED(context))
+               return;
+
+       lwsl_debug("%s: wsi: %p, flags:%d\n", __func__, wsi, flags);
+
+       if (!pt->io_loop_uv) {
+               lwsl_info("%s: no io loop yet\n", __func__);
+               return;
+       }
+
+       assert((flags & (LWS_EV_START | LWS_EV_STOP)) &&
+              (flags & (LWS_EV_READ | LWS_EV_WRITE)));
+
+       if (flags & LWS_EV_START) {
+               if (flags & LWS_EV_WRITE)
+                       current_events |= UV_WRITABLE;
+
+               if (flags & LWS_EV_READ)
+                       current_events |= UV_READABLE;
+
+               uv_poll_start(&w->uv_watcher, current_events, lws_accept_cb);
+       } else {
+               if (flags & LWS_EV_WRITE)
+                       current_events &= ~UV_WRITABLE;
+
+               if (flags & LWS_EV_READ)
+                       current_events &= ~UV_READABLE;
+
+               if (!(current_events & (UV_READABLE | UV_WRITABLE)))
+                       uv_poll_stop(&w->uv_watcher);
+               else
+                       uv_poll_start(&w->uv_watcher, current_events,
+                                     lws_accept_cb);
+       }
+}
+
+int
+lws_libuv_init_fd_table(struct lws_context *context)
+{
+       int n;
+
+       if (!LWS_LIBUV_ENABLED(context))
+               return 0;
+
+       for (n = 0; n < context->count_threads; n++) {
+               context->pt[n].w_sigint.context = context;
+       }
+
+       return 1;
+}
+
+LWS_VISIBLE void
+lws_libuv_run(const struct lws_context *context, int tsi)
+{
+       if (context->pt[tsi].io_loop_uv && LWS_LIBUV_ENABLED(context))
+               uv_run(context->pt[tsi].io_loop_uv, 0);
+}
+
+static void
+lws_libuv_kill(const struct lws_context *context)
+{
+       int n;
+
+       for (n = 0; n < context->count_threads; n++)
+               if (context->pt[n].io_loop_uv && LWS_LIBUV_ENABLED(context))
+                       uv_stop(context->pt[n].io_loop_uv);
+}
+
+/*
+ * This does not actually stop the event loop.  The reason is we have to pass
+ * libuv handle closures through its event loop.  So this tries to close all
+ * wsi, and set a flag; when all the wsi closures are finalized then we
+ * actually stop the libuv event loops.
+ */
+
+LWS_VISIBLE void
+lws_libuv_stop(struct lws_context *context)
+{
+       struct lws_context_per_thread *pt;
+       int n, m;
+
+       context->requested_kill = 1;
+
+       m = context->count_threads;
+       context->being_destroyed = 1;
+
+       while (m--) {
+               pt = &context->pt[m];
+
+               for (n = 0; (unsigned int)n < context->pt[m].fds_count; n++) {
+                       struct lws *wsi = wsi_from_fd(context, pt->fds[n].fd);
+                       if (!wsi)
+                               continue;
+
+                       lws_close_free_wsi(wsi,
+                               LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY
+                               /* no protocol close */);
+                       n--;
+               }
+       }
+
+       if (context->count_wsi_allocated == 0)
+               lws_libuv_kill(context);
+}
+
+LWS_VISIBLE uv_loop_t *
+lws_uv_getloop(struct lws_context *context, int tsi)
+{
+       if (context->pt[tsi].io_loop_uv && LWS_LIBUV_ENABLED(context))
+               return context->pt[tsi].io_loop_uv;
+
+       return NULL;
+}
+
+static void
+lws_libuv_closewsi(uv_handle_t* handle)
+{
+       struct lws *n = NULL, *wsi = (struct lws *)(((void *)handle) -
+                         (void *)(&n->w_read.uv_watcher));
+       struct lws_context *context = lws_get_context(wsi);
+
+       lws_close_free_wsi_final(wsi);
+
+       if (context->requested_kill && context->count_wsi_allocated == 0)
+               lws_libuv_kill(context);
+}
+
+void
+lws_libuv_closehandle(struct lws *wsi)
+{
+       struct lws_context *context = lws_get_context(wsi);
+
+       /* required to defer actual deletion until libuv has processed it */
+
+       uv_close((uv_handle_t*)&wsi->w_read.uv_watcher, lws_libuv_closewsi);
+
+       if (context->requested_kill && context->count_wsi_allocated == 0)
+               lws_libuv_kill(context);
+}
index 7cdc333..a5cc13c 100644 (file)
@@ -65,6 +65,10 @@ lws_free_wsi(struct lws *wsi)
            wsi->mode != LWSCM_WS_SERVING)
                lws_free_header_table(wsi);
 
+       wsi->context->count_wsi_allocated--;
+       lwsl_debug("%s: %p, remaining wsi %d\n", __func__, wsi,
+                       wsi->context->count_wsi_allocated);
+
        lws_free(wsi);
 }
 
@@ -294,7 +298,9 @@ just_kill_connection:
         * for the POLLIN to show a zero-size rx before coming back and doing
         * the actual close.
         */
-       if (wsi->state != LWSS_SHUTDOWN) {
+       if (wsi->state != LWSS_SHUTDOWN &&
+           reason != LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY &&
+           !wsi->socket_is_permanently_unusable) {
                lwsl_info("%s: shutting down connection: %p\n", __func__, wsi);
                n = shutdown(wsi->sock, SHUT_WR);
                if (n)
@@ -404,6 +410,21 @@ just_kill_connection:
 
        wsi->socket_is_permanently_unusable = 1;
 
+#ifdef LWS_USE_LIBUV
+       /* libuv has to do his own close handle processing asynchronously */
+       lws_libuv_closehandle(wsi);
+
+       return;
+#endif
+
+       lws_close_free_wsi_final(wsi);
+}
+
+void
+lws_close_free_wsi_final(struct lws *wsi)
+{
+       int n;
+
        if (!lws_ssl_close(wsi) && lws_socket_is_valid(wsi->sock)) {
 #if LWS_POSIX
                n = compatible_close(wsi->sock);
@@ -417,7 +438,7 @@ just_kill_connection:
        }
 
        /* outermost destroy notification for wsi (user_space still intact) */
-       context->protocols[0].callback(wsi, LWS_CALLBACK_WSI_DESTROY,
+       wsi->context->protocols[0].callback(wsi, LWS_CALLBACK_WSI_DESTROY,
                                       wsi->user_space, NULL, 0);
 
        lws_free_wsi(wsi);
index 8c5b0a2..86c56b9 100644 (file)
@@ -170,8 +170,11 @@ struct sockaddr_in;
 #endif
 
 #ifdef LWS_USE_LIBEV
-#include <uv.h>
+#include <ev.h>
 #endif /* LWS_USE_LIBEV */
+#ifdef LWS_USE_LIBUV
+#include <uv.h>
+#endif /* LWS_USE_LIBUV */
 
 #ifndef LWS_EXTERN
 #define LWS_EXTERN extern
@@ -281,6 +284,7 @@ enum lws_context_options {
        LWS_SERVER_OPTION_PEER_CERT_NOT_REQUIRED                = (1 << 7),
        LWS_SERVER_OPTION_VALIDATE_UTF8                         = (1 << 8),
        LWS_SERVER_OPTION_SSL_ECDH                              = (1 << 9),
+       LWS_SERVER_OPTION_LIBUV                                 = (1 << 10),
 
        /****** add new things just above ---^ ******/
 };
@@ -1457,19 +1461,42 @@ LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
 lws_http_transaction_completed(struct lws *wsi);
 
 #ifdef LWS_USE_LIBEV
-typedef void (lws_ev_signal_cb)(uv_loop_t *l, uv_signal_t *w, int revents);
+typedef void (lws_ev_signal_cb_t)(EV_P_ struct ev_signal *w, int revents);
 
 LWS_VISIBLE LWS_EXTERN int
-lws_sigint_cfg(struct lws_context *context, int use_ev_sigint,
-              lws_ev_signal_cb *cb);
+lws_ev_sigint_cfg(struct lws_context *context, int use_ev_sigint,
+                 lws_ev_signal_cb_t *cb);
 
 LWS_VISIBLE LWS_EXTERN int
-lws_initloop(struct lws_context *context, uv_loop_t *loop);
+lws_ev_initloop(struct lws_context *context, struct ev_loop *loop, int tsi);
 
 LWS_VISIBLE void
-lws_sigint_cb(uv_loop_t *loop, uv_signal_t *watcher, int revents);
+lws_ev_sigint_cb(struct ev_loop *loop, struct ev_signal *watcher, int revents);
 #endif /* LWS_USE_LIBEV */
 
+#ifdef LWS_USE_LIBUV
+typedef void (lws_uv_signal_cb_t)(uv_loop_t *l, uv_signal_t *w, int revents);
+
+LWS_VISIBLE LWS_EXTERN int
+lws_uv_sigint_cfg(struct lws_context *context, int use_uv_sigint,
+                 lws_uv_signal_cb_t *cb);
+
+LWS_VISIBLE LWS_EXTERN void
+lws_libuv_run(const struct lws_context *context, int tsi);
+
+LWS_VISIBLE void
+lws_libuv_stop(struct lws_context *context);
+
+LWS_VISIBLE LWS_EXTERN int
+lws_uv_initloop(struct lws_context *context, uv_loop_t *loop, uv_signal_cb cb, int tsi);
+
+LWS_VISIBLE LWS_EXTERN uv_loop_t *
+lws_uv_getloop(struct lws_context *context, int tsi);
+
+LWS_VISIBLE void
+lws_uv_sigint_cb(uv_loop_t *loop, uv_signal_t *watcher, int revents);
+#endif /* LWS_USE_LIBUV */
+
 LWS_VISIBLE LWS_EXTERN int
 lws_service_fd(struct lws_context *context, struct lws_pollfd *pollfd);
 
index 0963deb..eb09ce8 100644 (file)
@@ -134,7 +134,8 @@ lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
        if (!context)
                return 1;
 
-       lws_libev_run(context);
+       lws_libev_run(context, tsi);
+       lws_libuv_run(context, tsi);
 
        if (!context->service_tid_detected) {
                struct lws _lws;
@@ -455,6 +456,8 @@ lws_plat_insert_socket_into_fds(struct lws_context *context, struct lws *wsi)
        struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
 
        lws_libev_io(wsi, LWS_EV_START | LWS_EV_READ);
+       lws_libuv_io(wsi, LWS_EV_START | LWS_EV_READ);
+
        pt->fds[pt->fds_count++].revents = 0;
 }
 
@@ -579,7 +582,8 @@ lws_plat_init(struct lws_context *context,
                return 1;
        }
 
-       if (!lws_libev_init_fd_table(context)) {
+       if (!lws_libev_init_fd_table(context) &&
+           !lws_libuv_init_fd_table(context)) {
                /* otherwise libev handled it instead */
 
                while (n--) {
index 96406fa..fb86fbd 100644 (file)
@@ -49,6 +49,23 @@ _lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa)
                goto bail;
        }
 
+       if (_and & LWS_POLLIN) {
+               lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_READ);
+               lws_libuv_io(wsi, LWS_EV_STOP | LWS_EV_READ);
+       }
+       if (_or & LWS_POLLIN) {
+               lws_libev_io(wsi, LWS_EV_START | LWS_EV_READ);
+               lws_libuv_io(wsi, LWS_EV_START | LWS_EV_READ);
+       }
+       if (_and & LWS_POLLOUT) {
+               lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_WRITE);
+               lws_libuv_io(wsi, LWS_EV_STOP | LWS_EV_WRITE);
+       }
+       if (_or & LWS_POLLOUT) {
+               lws_libev_io(wsi, LWS_EV_START | LWS_EV_WRITE);
+               lws_libuv_io(wsi, LWS_EV_START | LWS_EV_WRITE);
+       }
+
        /*
         * if we changed something in this pollfd...
         *   ... and we're running in a different thread context
@@ -167,7 +184,8 @@ remove_wsi_socket_from_fds(struct lws *wsi)
                                           wsi->user_space, (void *)&pa, 1))
                return -1;
 
-       lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE);
+       lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE | LWS_EV_PREPARE_DELETION);
+       lws_libuv_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE | LWS_EV_PREPARE_DELETION);
 
        lws_pt_lock(pt);
 
@@ -199,9 +217,10 @@ remove_wsi_socket_from_fds(struct lws *wsi)
                                                   wsi->user_space, (void *) &pa, 0))
                        ret = -1;
 #ifndef LWS_NO_SERVER
-       /* if this made some room, accept connects on this thread */
-       if ((unsigned int)pt->fds_count < context->fd_limit_per_thread - 1)
-               _lws_change_pollfd(pt->wsi_listening, 0, LWS_POLLIN, &pa1);
+       if (!context->being_destroyed)
+               /* if this made some room, accept connects on this thread */
+               if ((unsigned int)pt->fds_count < context->fd_limit_per_thread - 1)
+                       _lws_change_pollfd(pt->wsi_listening, 0, LWS_POLLIN, &pa1);
 #endif
        lws_pt_unlock(pt);
 
@@ -314,8 +333,6 @@ network_sock:
        if (lws_change_pollfd(wsi, 0, LWS_POLLOUT))
                return -1;
 
-       lws_libev_io(wsi, LWS_EV_START | LWS_EV_WRITE);
-
        return 1;
 }
 
index d20da12..bdfecda 100644 (file)
 #include <arpa/inet.h>
 #include <poll.h>
 #ifdef LWS_USE_LIBEV
+#include <ev.h>
+#endif
+#ifdef LWS_USE_LIBUV
 #include <uv.h>
-#endif /* LWS_USE_LIBEV */
+#endif
 #include <sys/mman.h>
 
 #endif /* MBED */
@@ -437,17 +440,28 @@ enum {
 struct lws_protocols;
 struct lws;
 
-#ifdef LWS_USE_LIBEV
+#if defined(LWS_USE_LIBEV) || defined(LWS_USE_LIBUV)
+
 struct lws_io_watcher {
-    uv_poll_t watcher;
-       struct lws_context* context;
+#ifdef LWS_USE_LIBEV
+       ev_io ev_watcher;
+#endif
+#ifdef LWS_USE_LIBUV
+       uv_poll_t uv_watcher;
+#endif
+       struct lws_context *context;
 };
 
 struct lws_signal_watcher {
-    uv_signal_t watcher;
-       struct lws_context* context;
+#ifdef LWS_USE_LIBEV
+       ev_signal ev_watcher;
+#endif
+#ifdef LWS_USE_LIBUV
+       uv_signal_t uv_watcher;
+#endif
+       struct lws_context *context;
 };
-#endif /* LWS_USE_LIBEV */
+#endif
 
 #ifdef _WIN32
 #define LWS_FD_HASH(fd) ((fd ^ (fd >> 8) ^ (fd >> 16)) % FD_HASHTABLE_MODULUS)
@@ -521,6 +535,20 @@ struct lws_context_per_thread {
 #ifndef LWS_NO_SERVER
        struct lws *wsi_listening;
 #endif
+#if defined(LWS_USE_LIBEV)
+       struct ev_loop *io_loop_ev;
+#endif
+#if defined(LWS_USE_LIBUV)
+       uv_loop_t *io_loop_uv;
+       uv_signal_t signals[8];
+#endif
+#if defined(LWS_USE_LIBEV)
+       struct lws_io_watcher w_accept;
+#endif
+#if defined(LWS_USE_LIBEV) || defined(LWS_USE_LIBUV)
+       struct lws_signal_watcher w_sigint;
+       unsigned char ev_loop_foreign:1;
+#endif
        lws_sockfd_type lserv_fd;
 
        unsigned long count_conns;
@@ -559,12 +587,6 @@ struct lws_context {
 #else
        struct lws **lws_lookup;  /* fd to wsi */
 #endif
-#ifdef LWS_USE_LIBEV
-    uv_loop_t *io_loop;
-       struct lws_io_watcher w_accept;
-       struct lws_signal_watcher w_sigint;
-       lws_ev_signal_cb* lws_ev_sigint_cb;
-#endif /* LWS_USE_LIBEV */
        const char *iface;
        const struct lws_token_limits *token_limits;
        void *user_space;
@@ -578,7 +600,12 @@ struct lws_context {
 #ifndef LWS_NO_EXTENSIONS
        const struct lws_extension *extensions;
 #endif
-
+#if defined(LWS_USE_LIBEV)
+       lws_ev_signal_cb_t * lws_ev_sigint_cb;
+#endif
+#if defined(LWS_USE_LIBUV)
+       lws_uv_signal_cb_t * lws_uv_sigint_cb;
+#endif
        char http_proxy_address[128];
        char proxy_basic_auth_token[128];
        char canonical_hostname[128];
@@ -589,13 +616,14 @@ struct lws_context {
 
        int max_fds;
        int listen_port;
-#ifdef LWS_USE_LIBEV
+#if defined(LWS_USE_LIBEV) || defined(LWS_USE_LIBUV)
        int use_ev_sigint;
 #endif
        int started_with_parent;
 
        int fd_random;
        int lserv_mod;
+       int count_wsi_allocated;
        unsigned int http_proxy_port;
        unsigned int options;
        unsigned int fd_limit_per_thread;
@@ -634,18 +662,24 @@ struct lws_context {
        short count_threads;
 
        unsigned int being_destroyed:1;
+       unsigned int requested_kill:1;
 };
 
+LWS_EXTERN void
+lws_close_free_wsi_final(struct lws *wsi);
+LWS_EXTERN void
+lws_libuv_closehandle(struct lws *wsi);
+
 enum {
        LWS_EV_READ = (1 << 0),
        LWS_EV_WRITE = (1 << 1),
        LWS_EV_START = (1 << 2),
        LWS_EV_STOP = (1 << 3),
+
+       LWS_EV_PREPARE_DELETION = (1 << 31),
 };
 
-#ifdef LWS_USE_LIBEV
-#define LWS_LIBEV_ENABLED(context) (context->options & LWS_SERVER_OPTION_LIBEV)
-LWS_EXTERN void lws_feature_status_libev(struct lws_context_creation_info *info);
+#if defined(LWS_USE_LIBEV)
 LWS_EXTERN void
 lws_libev_accept(struct lws *new_wsi, lws_sockfd_type accept_fd);
 LWS_EXTERN void
@@ -653,21 +687,55 @@ lws_libev_io(struct lws *wsi, int flags);
 LWS_EXTERN int
 lws_libev_init_fd_table(struct lws_context *context);
 LWS_EXTERN void
-lws_libev_run(const struct lws_context *context);
+lws_libev_destroyloop(struct lws_context *context, int tsi);
+LWS_EXTERN void
+lws_libev_run(const struct lws_context *context, int tsi);
+#define LWS_LIBEV_ENABLED(context) (context->options & LWS_SERVER_OPTION_LIBEV)
+LWS_EXTERN void lws_feature_status_libev(struct lws_context_creation_info *info);
 #else
+#define lws_libev_accept(_a, _b) ((void) 0)
+#define lws_libev_io(_a, _b) ((void) 0)
+#define lws_libev_init_fd_table(_a) (0)
+#define lws_libev_run(_a, _b) ((void) 0)
+#define lws_libev_destroyloop(_a, _b) ((void) 0)
 #define LWS_LIBEV_ENABLED(context) (0)
-#ifdef LWS_POSIX
+#if LWS_POSIX
 #define lws_feature_status_libev(_a) \
                        lwsl_notice("libev support not compiled in\n")
 #else
 #define lws_feature_status_libev(_a)
 #endif
-#define lws_libev_accept(_a, _b) ((void) 0)
-#define lws_libev_io(_a, _b) ((void) 0)
-#define lws_libev_init_fd_table(_a) (0)
-#define lws_libev_run(_a) ((void) 0)
 #endif
 
+#if defined(LWS_USE_LIBUV)
+LWS_EXTERN void
+lws_libuv_accept(struct lws *new_wsi, lws_sockfd_type accept_fd);
+LWS_EXTERN void
+lws_libuv_io(struct lws *wsi, int flags);
+LWS_EXTERN int
+lws_libuv_init_fd_table(struct lws_context *context);
+LWS_EXTERN void
+lws_libuv_run(const struct lws_context *context, int tsi);
+LWS_EXTERN void
+lws_libuv_destroyloop(struct lws_context *context, int tsi);
+#define LWS_LIBUV_ENABLED(context) (context->options & LWS_SERVER_OPTION_LIBUV)
+LWS_EXTERN void lws_feature_status_libuv(struct lws_context_creation_info *info);
+#else
+#define lws_libuv_accept(_a, _b) ((void) 0)
+#define lws_libuv_io(_a, _b) ((void) 0)
+#define lws_libuv_init_fd_table(_a) (0)
+#define lws_libuv_run(_a, _b) ((void) 0)
+#define lws_libuv_destroyloop(_a, _b) ((void) 0)
+#define LWS_LIBUV_ENABLED(context) (0)
+#if LWS_POSIX
+#define lws_feature_status_libuv(_a) \
+                       lwsl_notice("libuv support not compiled in\n")
+#else
+#define lws_feature_status_libuv(_a)
+#endif
+#endif
+
+
 #ifdef LWS_USE_IPV6
 #define LWS_IPV6_ENABLED(context) \
        (!(context->options & LWS_SERVER_OPTION_DISABLE_IPV6))
@@ -933,10 +1001,12 @@ struct lws {
 
        /* lifetime members */
 
-#ifdef LWS_USE_LIBEV
+#if defined(LWS_USE_LIBEV) || defined(LWS_USE_LIBUV)
        struct lws_io_watcher w_read;
+#endif
+#if defined(LWS_USE_LIBEV)
        struct lws_io_watcher w_write;
-#endif /* LWS_USE_LIBEV */
+#endif
        time_t pending_timeout_limit;
 
        /* pointers */
index e1ca343..9b8d6b5 100644 (file)
@@ -143,6 +143,7 @@ lws_context_init_server(struct lws_context_creation_info *info,
        if (insert_wsi_socket_into_fds(context, wsi))
                goto bail;
 
+       context->count_wsi_allocated++;
        context->pt[m].lserv_fd = sockfd;
 
 #if LWS_POSIX
@@ -665,7 +666,7 @@ lws_create_new_server_wsi(struct lws_context *context)
        new_wsi->user_space = NULL;
        new_wsi->ietf_spec_revision = 0;
        new_wsi->sock = LWS_SOCK_INVALID;
-
+       context->count_wsi_allocated++;
 
        /*
         * outermost create notification for wsi
@@ -752,6 +753,8 @@ lws_adopt_socket(struct lws_context *context, lws_sockfd_type accept_fd)
                return NULL;
        }
 
+       lwsl_debug("%s: new wsi %p\n", __func__, new_wsi);
+
        new_wsi->sock = accept_fd;
 
        /* the transport is accepted... give him time to negotiate */
@@ -775,6 +778,7 @@ lws_adopt_socket(struct lws_context *context, lws_sockfd_type accept_fd)
        }
 
        lws_libev_accept(new_wsi, new_wsi->sock);
+       lws_libuv_accept(new_wsi, new_wsi->sock);
 
        if (!LWS_SSL_ENABLED(context)) {
                if (insert_wsi_socket_into_fds(context, new_wsi))
@@ -908,8 +912,6 @@ try_pollout:
                        goto fail;
                }
 
-               lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_WRITE);
-
                if (wsi->state != LWSS_HTTP_ISSUING_FILE) {
                        n = user_callback_handle_rxflow(wsi->protocol->callback,
                                        wsi, LWS_CALLBACK_HTTP_WRITEABLE,
index 5893a69..2e4570f 100644 (file)
@@ -231,15 +231,12 @@ lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd)
 user_service:
        /* one shot */
 
-       if (pollfd) {
+       if (pollfd)
                if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
                        lwsl_info("failed at set pollfd\n");
                        return 1;
                }
 
-               lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_WRITE);
-       }
-
 #ifdef LWS_USE_HTTP2
        /*
         * we are the 'network wsi' for potentially many muxed child wsi with
index 751f9c1..0f15d7d 100644 (file)
--- a/lib/ssl.c
+++ b/lib/ssl.c
@@ -664,8 +664,6 @@ lws_server_socket_service_ssl(struct lws *wsi, lws_sockfd_type accept_fd)
                if (lws_change_pollfd(wsi, LWS_POLLOUT, 0))
                        goto fail;
 
-               lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_WRITE);
-
                lws_latency_pre(context, wsi);
 
                n = recv(wsi->sock, (char *)pt->serv_buf, LWS_MAX_SOCKET_IO_BUF,
@@ -732,8 +730,6 @@ go_again:
                        if (lws_change_pollfd(wsi, 0, LWS_POLLIN))
                                goto fail;
 
-                       lws_libev_io(wsi, LWS_EV_START | LWS_EV_READ);
-
                        lwsl_info("SSL_ERROR_WANT_READ\n");
                        break;
                }
@@ -741,7 +737,6 @@ go_again:
                        if (lws_change_pollfd(wsi, 0, LWS_POLLOUT))
                                goto fail;
 
-                       lws_libev_io(wsi, LWS_EV_START | LWS_EV_WRITE);
                        break;
                }
                lwsl_debug("SSL_accept failed skt %u: %s\n",
index 9d4a0a6..6ce964b 100644 (file)
@@ -41,6 +41,9 @@
 /* Enable libev io loop */
 #cmakedefine LWS_USE_LIBEV
 
+/* Enable libuv io loop */
+#cmakedefine LWS_USE_LIBUV
+
 /* Build with support for ipv6 */
 #cmakedefine LWS_USE_IPV6
 
index 1a35f18..316198c 100644 (file)
@@ -350,17 +350,13 @@ int main(int argc, char **argv)
        /* override the active fops */
        lws_get_fops(context)->open = test_server_fops_open;
 
-       lws_initloop(context, loop);
+       lws_ev_initloop(context, loop, 0);
 
        _ev_timer_init(&timeout_watcher, ev_timeout_cb, 0.05, 0.05);
        ev_timer_start(loop, &timeout_watcher);
-
-       while (!force_exit)
-               ev_run(loop, 0);
+       ev_run(loop, 0);
 
        lws_context_destroy(context);
-       ev_loop_destroy(loop);
-
        lwsl_notice("libwebsockets-test-server exited cleanly\n");
 
 #ifndef _WIN32
index 1232d0d..4ac7956 100644 (file)
@@ -24,7 +24,6 @@
 int close_testing;
 int max_poll_elements;
 int debug_level = 7;
-volatile int force_exit = 0;
 struct lws_context *context;
 struct lws_plat_file_ops fops_plat;
 
@@ -107,44 +106,25 @@ static const struct lws_extension exts[] = {
        { NULL, NULL, NULL /* terminator */ }
 };
 
-/* this shows how to override the lws file operations.  You don't need
- * to do any of this unless you have a reason (eg, want to serve
- * compressed files without decompressing the whole archive)
- */
-static lws_filefd_type
-test_server_fops_open(struct lws *wsi, const char *filename,
-                     unsigned long *filelen, int flags)
-{
-       lws_filefd_type n;
-
-       /* call through to original platform implementation */
-       n = fops_plat.open(wsi, filename, filelen, flags);
-
-       lwsl_notice("%s: opening %s, ret %ld, len %lu\n", __func__, filename,
-                       (long)n, *filelen);
-
-       return n;
-}
-
 void signal_cb(uv_signal_t *watcher, int revents)
 {
-       lwsl_notice("Signal caught, exiting...\n");
-       force_exit = 1;
+       lwsl_err("Signal %d caught, exiting...\n", watcher->signum);
        switch (watcher->signum) {
        case SIGTERM:
        case SIGINT:
-               uv_stop(uv_default_loop()); /* Note: we assume default loop! */
                break;
        default:
                signal(SIGABRT, SIG_DFL);
                abort();
                break;
        }
+       lws_libuv_stop(context);
 }
 
 static void
-uv_timeout_cb (uv_timer_t *w)
+uv_timeout_cb(uv_timer_t *w)
 {
+       lwsl_info("%s\n", __func__);
        lws_callback_on_writable_all_protocol(context,
                                        &protocols[PROTOCOL_DUMB_INCREMENT]);
 }
@@ -167,13 +147,10 @@ static struct option options[] = {
 
 int main(int argc, char **argv)
 {
-       int sigs[] = { SIGINT, SIGKILL, SIGTERM, SIGSEGV, SIGFPE };
-       uv_signal_t signals[ARRAY_SIZE(sigs)];
-       uv_loop_t *loop = uv_default_loop();
        struct lws_context_creation_info info;
        char interface_name[128] = "";
-       const char *iface = NULL;
        uv_timer_t timeout_watcher;
+       const char *iface = NULL;
        char cert_path[1024];
        char key_path[1024];
        int use_ssl = 0;
@@ -257,16 +234,9 @@ int main(int argc, char **argv)
        }
 #endif
 
-       for (n = 0; n < ARRAY_SIZE(sigs); n++) {
-               uv_signal_init(loop, &signals[n]);
-               uv_signal_start(&signals[n], signal_cb, sigs[n]);
-       }
-
-#ifndef _WIN32
        /* we will only try to log things according to our debug_level */
        setlogmask(LOG_UPTO (LOG_DEBUG));
        openlog("lwsts", syslog_options, LOG_DAEMON);
-#endif
 
        /* tell the library what debug level to emit and to send it to syslog */
        lws_set_log_level(debug_level, lwsl_emit_syslog);
@@ -274,7 +244,7 @@ int main(int argc, char **argv)
        lwsl_notice("libwebsockets test server libuv - license LGPL2.1+SLE\n");
        lwsl_notice("(C) Copyright 2010-2016 Andy Green <andy@warmcat.com>\n");
 
-       printf("Using resource path \"%s\"\n", resource_path);
+       lwsl_info("Using resource path \"%s\"\n", resource_path);
 
        info.iface = iface;
        info.protocols = protocols;
@@ -289,13 +259,13 @@ int main(int argc, char **argv)
                        return -1;
                }
                sprintf(cert_path, "%s/libwebsockets-test-server.pem",
-                                                               resource_path);
+                       resource_path);
                if (strlen(resource_path) > sizeof(key_path) - 32) {
                        lwsl_err("resource path too long\n");
                        return -1;
                }
                sprintf(key_path, "%s/libwebsockets-test-server.key.pem",
-                                                               resource_path);
+                       resource_path);
 
                info.ssl_cert_filepath = cert_path;
                info.ssl_private_key_filepath = key_path;
@@ -311,32 +281,15 @@ int main(int argc, char **argv)
                return -1;
        }
 
-       /*
-        * this shows how to override the lws file operations.  You don't need
-        * to do any of this unless you have a reason (eg, want to serve
-        * compressed files without decompressing the whole archive)
-        */
-       /* stash original platform fops */
-       fops_plat = *(lws_get_fops(context));
-       /* override the active fops */
-       lws_get_fops(context)->open = test_server_fops_open;
-
-       lws_uv_initloop(context, loop, 0);
+       lws_uv_initloop(context, NULL, signal_cb, 0);
 
-       uv_timer_init(loop, &timeout_watcher);
-       uv_timer_start(&timeout_watcher, uv_timeout_cb, 0.05, 0.05);
+       uv_timer_init(lws_uv_getloop(context, 0), &timeout_watcher);
+       uv_timer_start(&timeout_watcher, uv_timeout_cb, 50, 50);
 
-       while (!force_exit)
-               uv_run(loop, 0);
+       lws_libuv_run(context, 0);
 
        lws_context_destroy(context);
-       uv_stop(loop);
-
        lwsl_notice("libwebsockets-test-server exited cleanly\n");
 
-#ifndef _WIN32
-       closelog();
-#endif
-
        return 0;
 }