diff --git a/.gitignore b/.gitignore index a030dbaa..d2b3e444 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,12 @@ #Ignore build files +CMakeCache.txt +CMakeFiles +build +cmake_install.cmake +lws-minimal* +Makefile +.cproject +.project config.h config.log config.status @@ -38,4 +46,4 @@ ar-lib libwebsockets.pc build/ *.swp -doc \ No newline at end of file +doc diff --git a/CMakeLists.txt b/CMakeLists.txt index 3c6cd9fd..8903a49e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,7 @@ if(WIN32) endif() set(LWS_ROLE_RAW 1) +set(LWS_WITH_POLL 1) # # Select features recommended for PC distro packaging @@ -947,9 +948,9 @@ if (LWS_WITH_HTTP_PROXY) lib/roles/http/server/rewrite.c) endif() -if (LWS_WITH_LIBEV) +if (LWS_WITH_POLL) list(APPEND SOURCES - lib/event-libs/libev/libev.c) + lib/event-libs/poll/poll.c) endif() if (LWS_WITH_LIBUV) @@ -962,6 +963,11 @@ if (LWS_WITH_LIBEVENT) lib/event-libs/libevent/libevent.c) endif() +if (LWS_WITH_LIBEV) + list(APPEND SOURCES + lib/event-libs/libev/libev.c) +endif() + if (LWS_WITH_LEJP) list(APPEND SOURCES lib/misc/lejp.c) diff --git a/lib/context.c b/lib/context.c index c7657153..33069988 100644 --- a/lib/context.c +++ b/lib/context.c @@ -817,7 +817,7 @@ lws_create_vhost(struct lws_context *context, goto bail1; } lws_context_lock(context); - n = _lws_context_init_server(info, vh); + n = _lws_vhost_init_server(info, vh); lws_context_unlock(context); if (n < 0) { lwsl_err("init server failed\n"); @@ -926,26 +926,34 @@ lws_create_event_pipes(struct lws_context *context) context->pt[n].pipe_wsi = wsi; - lws_libuv_accept(wsi, wsi->desc); - lws_libev_accept(wsi, wsi->desc); - lws_libevent_accept(wsi, wsi->desc); + if (context->event_loop_ops->accept) + context->event_loop_ops->accept(wsi); if (__insert_wsi_socket_into_fds(context, wsi)) return 1; - lws_change_pollfd(context->pt[n].pipe_wsi, 0, LWS_POLLIN); + //lws_change_pollfd(context->pt[n].pipe_wsi, 0, LWS_POLLIN); context->count_wsi_allocated++; } return 0; } -static void +void lws_destroy_event_pipe(struct lws *wsi) { - lws_plat_pipe_close(wsi); + lwsl_info("%s\n", __func__); __remove_wsi_socket_from_fds(wsi); - lws_libevent_destroy(wsi); + + if (wsi->context->event_loop_ops->wsi_logical_close) { + wsi->context->event_loop_ops->wsi_logical_close(wsi); + lws_plat_pipe_close(wsi); + return; + } + + if (wsi->context->event_loop_ops->destroy_wsi) + wsi->context->event_loop_ops->destroy_wsi(wsi); + lws_plat_pipe_close(wsi); wsi->context->count_wsi_allocated--; lws_free(wsi); } @@ -1056,6 +1064,7 @@ lws_create_context(const struct lws_context_creation_info *info) info->external_baggage_free_on_destroy; context->time_up = time(NULL); + context->pcontext_finalize = info->pcontext; context->simultaneous_ssl_restriction = info->simultaneous_ssl_restriction; @@ -1089,6 +1098,40 @@ lws_create_context(const struct lws_context_creation_info *info) context->options = info->options; + /* + * set the context event loops ops struct + * + * after this, all event_loop actions use the generic ops + */ + +#if defined(LWS_WITH_POLL) + context->event_loop_ops = &event_loop_ops_poll; +#endif + +#if defined(LWS_WITH_LIBUV) + if (LWS_LIBUV_ENABLED(context)) { + context->event_loop_ops = &event_loop_ops_uv; + } +#endif +#if defined(LWS_WITH_LIBEV) + if (LWS_LIBEV_ENABLED(context)) { + context->event_loop_ops = &event_loop_ops_ev; + } +#endif +#if defined(LWS_WITH_LIBEVENT) + if (LWS_LIBEVENT_ENABLED(context)) { + context->event_loop_ops = &event_loop_ops_event; + } +#endif + + if (!context->event_loop_ops) { + lwsl_err("no event loop possible\n"); + + goto bail; + } + + lwsl_info("Using event loop: %s\n", context->event_loop_ops->name); + #if defined(LWS_WITH_TLS) if (info->alpn) context->alpn_default = info->alpn; @@ -1135,6 +1178,12 @@ lws_create_context(const struct lws_context_creation_info *info) else context->max_http_header_pool = context->max_fds; + if (info->fd_limit_per_thread) + context->fd_limit_per_thread = info->fd_limit_per_thread; + else + context->fd_limit_per_thread = context->max_fds / + context->count_threads; + /* * Allocate the per-thread storage for scratchpad buffers, * and header data pool @@ -1147,10 +1196,9 @@ lws_create_context(const struct lws_context_creation_info *info) return NULL; } -#ifdef LWS_WITH_LIBUV context->pt[n].context = context; -#endif context->pt[n].tid = n; + #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) context->pt[n].http.ah_list = NULL; context->pt[n].http.ah_pool_length = 0; @@ -1158,12 +1206,6 @@ lws_create_context(const struct lws_context_creation_info *info) lws_pt_mutex_init(&context->pt[n]); } - if (info->fd_limit_per_thread) - context->fd_limit_per_thread = info->fd_limit_per_thread; - else - context->fd_limit_per_thread = context->max_fds / - context->count_threads; - lwsl_info(" Threads: %d each %d fds\n", context->count_threads, context->fd_limit_per_thread); @@ -1172,24 +1214,6 @@ lws_create_context(const struct lws_context_creation_info *info) return NULL; } -#ifdef LWS_WITH_LIBEV - if (LWS_LIBEV_ENABLED(context)) { - context->use_event_loop_sigint = 1; - context->ev.sigint_cb = &lws_ev_sigint_cb; - } -#endif /* LWS_WITH_LIBEV */ -#ifdef LWS_WITH_LIBUV - if (LWS_LIBUV_ENABLED(context)) { - context->use_event_loop_sigint = 1; - context->uv.sigint_cb = &lws_uv_sigint_cb; - } -#endif -#ifdef LWS_WITH_LIBEVENT - if (LWS_LIBEVENT_ENABLED(context)) { - context->use_event_loop_sigint = 1; - context->event.sigint_cb = &lws_event_sigint_cb; - } -#endif /* LWS_WITH_LIBEVENT */ #if defined(LWS_WITH_PEER_LIMITS) /* scale the peer hash table according to the max fds for the process, @@ -1245,6 +1269,25 @@ lws_create_context(const struct lws_context_creation_info *info) if (lws_plat_init(context, info)) goto bail; + if (context->event_loop_ops->init_context) + if (context->event_loop_ops->init_context(context, info)) + goto bail; + + + if (context->event_loop_ops->init_pt) + for (n = 0; n < context->count_threads; n++) { + void *lp = NULL; + + if (info->foreign_loops) + lp = info->foreign_loops[n]; + + if (context->event_loop_ops->init_pt(context, lp, n)) + goto bail; + } + + if (lws_create_event_pipes(context)) + goto bail; + lws_context_init_ssl_library(info); context->user_space = info->user; @@ -1283,16 +1326,6 @@ lws_create_context(const struct lws_context_creation_info *info) context->count_caps = info->count_caps; #endif - /* - * The event libs handle doing this when their event loop starts, - * if we are using the default poll() service, do it here - */ - - if (!LWS_LIBEV_ENABLED(context) && - !LWS_LIBUV_ENABLED(context) && - !LWS_LIBEVENT_ENABLED(context) && lws_create_event_pipes(context)) - goto bail; - /* * drop any root privs for this process * to listen on port < 1023 we would have needed root, but now we are @@ -1365,10 +1398,6 @@ lws_context_is_deprecated(struct lws_context *context) return context->deprecated; } -LWS_VISIBLE void -lws_context_destroy2(struct lws_context *context); - - void lws_vhost_destroy1(struct lws_vhost *vh) { @@ -1540,16 +1569,10 @@ lws_vhost_destroy2(struct lws_vhost *vh) lws_free(vh->protocol_vh_privs); lws_ssl_SSL_CTX_destroy(vh); lws_free(vh->same_vh_protocol_list); -#ifdef LWS_WITH_PLUGINS - if (LWS_LIBUV_ENABLED(context)) { - if (context->plugin_list) - lws_free((void *)vh->protocols); - } else -#endif - { - if (context->options & LWS_SERVER_OPTION_EXPLICIT_VHOSTS) - lws_free((void *)vh->protocols); - } + + if (context->plugin_list || + (context->options & LWS_SERVER_OPTION_EXPLICIT_VHOSTS)) + lws_free((void *)vh->protocols); LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar) if (ar->destroy_vhost) @@ -1628,6 +1651,106 @@ lws_vhost_destroy(struct lws_vhost *vh) vh->context->deferred_free_list = df; } +/* + * When using an event loop, the context destruction is in three separate + * parts. This is to cover both internal and foreign event loops cleanly. + * + * - lws_context_destroy() simply starts a soft close of all wsi and + * related allocations. The event loop continues. + * + * As the closes complete in the event loop, reference counting is used + * to determine when everything is closed. It then calls + * lws_context_destroy2(). + * + * - lws_context_destroy2() cleans up the rest of the higher-level logical + * lws pieces like vhosts. If the loop was foreign, it then proceeds to + * lws_context_destroy3(). If it the loop is internal, it stops the + * internal loops and waits for lws_context_destroy() to be called again + * outside the event loop (since we cannot destroy the loop from + * within the loop). That will cause lws_context_destroy3() to run + * directly. + * + * - lws_context_destroy3() destroys any internal event loops and then + * destroys the context itself, setting what was info.pcontext to NULL. + */ + +static void +lws_context_destroy3(struct lws_context *context) +{ + struct lws_context **pcontext_finalize = context->pcontext_finalize; + + lws_free(context); + lwsl_info("%s: ctx %p freed\n", __func__, context); + + if (pcontext_finalize) + *pcontext_finalize = NULL; +} + +void +lws_context_destroy2(struct lws_context *context) +{ + struct lws_vhost *vh = NULL, *vh1; +#if defined(LWS_WITH_PEER_LIMITS) + uint32_t n; +#endif + + + lwsl_info("%s: ctx %p\n", __func__, context); + + /* + * free all the per-vhost allocations + */ + + vh = context->vhost_list; + while (vh) { + vh1 = vh->vhost_next; + lws_vhost_destroy2(vh); + vh = vh1; + } + + /* remove ourselves from the pending destruction list */ + + while (context->vhost_pending_destruction_list) + /* removes itself from list */ + lws_vhost_destroy2(context->vhost_pending_destruction_list); + + + lws_stats_log_dump(context); + + lws_ssl_context_destroy(context); + lws_plat_context_late_destroy(context); + +#if defined(LWS_WITH_PEER_LIMITS) + for (n = 0; n < context->pl_hash_elements; n++) { + lws_start_foreach_llp(struct lws_peer **, peer, + context->pl_hash_table[n]) { + struct lws_peer *df = *peer; + *peer = df->next; + lws_free(df); + continue; + } lws_end_foreach_llp(peer, next); + } + lws_free(context->pl_hash_table); +#endif + + if (context->external_baggage_free_on_destroy) + free(context->external_baggage_free_on_destroy); + + lws_check_deferred_free(context, 1); + +#if LWS_MAX_SMP > 1 + pthread_mutex_destroy(&context->lock); +#endif + + if (context->event_loop_ops->destroy_context2) + if (context->event_loop_ops->destroy_context2(context)) { + context->finalize_destroy_after_internal_loops_stopped = 1; + return; + } + + lws_context_destroy3(context); +} + LWS_VISIBLE void lws_context_destroy(struct lws_context *context) { @@ -1642,8 +1765,18 @@ lws_context_destroy(struct lws_context *context) lwsl_notice("%s: ctx %p\n", __func__, context); return; } + + if (context->finalize_destroy_after_internal_loops_stopped) { + if (context->event_loop_ops->destroy_context2) + context->event_loop_ops->destroy_context2(context); + + lws_context_destroy3(context); + + return; + } + if (context->being_destroyed1) { - lwsl_notice("%s: ctx %p: already being destroyed\n", + lwsl_info("%s: ctx %p: already being destroyed\n", __func__, context); return; } @@ -1653,6 +1786,7 @@ lws_context_destroy(struct lws_context *context) m = context->count_threads; context->being_destroyed = 1; context->being_destroyed1 = 1; + context->requested_kill = 1; memset(&wsi, 0, sizeof(wsi)); wsi.context = context; @@ -1708,9 +1842,8 @@ lws_context_destroy(struct lws_context *context) for (n = 0; n < context->count_threads; n++) { pt = &context->pt[n]; - lws_libev_destroyloop(context, n); - lws_libuv_destroyloop(context, n); - lws_libevent_destroyloop(context, n); + if (context->event_loop_ops->destroy_pt) + context->event_loop_ops->destroy_pt(context, n); lws_free_set_NULL(context->pt[n].serv_buf); @@ -1721,84 +1854,14 @@ lws_context_destroy(struct lws_context *context) } lws_plat_context_early_destroy(context); -#if defined(LWS_WITH_LIBUV) - if (LWS_LIBUV_ENABLED(context)) - for (n = 0; n < context->count_threads; n++) { - pt = &context->pt[n]; - if (!pt->event_loop_foreign) { -#if UV_VERSION_MAJOR > 0 - uv_loop_close(pt->uv.io_loop); -#endif - lws_free_set_NULL(pt->uv.io_loop); - } - } -#endif - if (context->pt[0].fds) lws_free_set_NULL(context->pt[0].fds); - if (!LWS_LIBUV_ENABLED(context)) - lws_context_destroy2(context); -} + if (context->event_loop_ops->destroy_context1) { + context->event_loop_ops->destroy_context1(context); -/* - * call the second one after the event loop has been shut down cleanly - */ - -LWS_VISIBLE void -lws_context_destroy2(struct lws_context *context) -{ - struct lws_vhost *vh = NULL, *vh1; -#if defined(LWS_WITH_PEER_LIMITS) - uint32_t n; -#endif - - lwsl_info("%s: ctx %p\n", __func__, context); - - /* - * free all the per-vhost allocations - */ - - vh = context->vhost_list; - while (vh) { - vh1 = vh->vhost_next; - lws_vhost_destroy2(vh); - vh = vh1; + return; } - /* remove ourselves from the pending destruction list */ - - while (context->vhost_pending_destruction_list) - /* removes itself from list */ - lws_vhost_destroy2(context->vhost_pending_destruction_list); - - - lws_stats_log_dump(context); - - lws_ssl_context_destroy(context); - lws_plat_context_late_destroy(context); - -#if defined(LWS_WITH_PEER_LIMITS) - for (n = 0; n < context->pl_hash_elements; n++) { - lws_start_foreach_llp(struct lws_peer **, peer, - context->pl_hash_table[n]) { - struct lws_peer *df = *peer; - *peer = df->next; - lws_free(df); - continue; - } lws_end_foreach_llp(peer, next); - } - lws_free(context->pl_hash_table); -#endif - - if (context->external_baggage_free_on_destroy) - free(context->external_baggage_free_on_destroy); - - lws_check_deferred_free(context, 1); - -#if LWS_MAX_SMP > 1 - pthread_mutex_destroy(&context->lock); -#endif - - lws_free(context); + lws_context_destroy2(context); } diff --git a/lib/event-libs/libev/libev.c b/lib/event-libs/libev/libev.c index bc18683d..2ec6d6f5 100644 --- a/lib/event-libs/libev/libev.c +++ b/lib/event-libs/libev/libev.c @@ -61,28 +61,23 @@ lws_accept_cb(struct ev_loop *loop, struct ev_io *watcher, int revents) LWS_VISIBLE void lws_ev_sigint_cb(struct ev_loop *loop, struct ev_signal *watcher, int revents) { + struct lws_context *context = watcher->data; + + if (context->eventlib_signal_cb) { + context->eventlib_signal_cb((void *)watcher, watcher->signum); + + return; + } ev_break(loop, EVBREAK_ALL); } -LWS_VISIBLE int -lws_ev_sigint_cfg(struct lws_context *context, int use_event_loop_sigint, - lws_ev_signal_cb_t *cb) -{ - context->use_event_loop_sigint = use_event_loop_sigint; - if (cb) - context->ev.sigint_cb = cb; - else - context->ev.sigint_cb = &lws_ev_sigint_cb; - - return 0; -} - -LWS_VISIBLE int -lws_ev_initloop(struct lws_context *context, struct ev_loop *loop, int tsi) +static int +elops_init_pt_ev(struct lws_context *context, void *_loop, int tsi) { struct ev_signal *w_sigint = &context->pt[tsi].w_sigint.ev.watcher; struct lws_vhost *vh = context->vhost_list; const char *backend_name; + struct ev_loop *loop = (struct ev_loop *)_loop; int status = 0; int backend; @@ -93,9 +88,6 @@ lws_ev_initloop(struct lws_context *context, struct ev_loop *loop, int tsi) context->pt[tsi].ev.io_loop = loop; - if (lws_create_event_pipes(context)) - return -1; - /* * Initialize the accept w_accept with all the listening sockets * and register a callback for read operations @@ -113,9 +105,10 @@ lws_ev_initloop(struct lws_context *context, struct ev_loop *loop, int tsi) vh = vh->vhost_next; } - /* Register the signal watcher unless the user says not to */ - if (context->use_event_loop_sigint) { - ev_signal_init(w_sigint, context->ev.sigint_cb, SIGINT); + /* Register the signal watcher unless it's a foreign loop */ + if (!context->pt[tsi].event_loop_foreign) { + ev_signal_init(w_sigint, lws_ev_sigint_cb, SIGINT); + w_sigint->data = context; ev_signal_start(loop, w_sigint); } @@ -150,8 +143,8 @@ lws_ev_initloop(struct lws_context *context, struct ev_loop *loop, int tsi) return status; } -void -lws_libev_destroyloop(struct lws_context *context, int tsi) +static void +elops_destroy_pt_ev(struct lws_context *context, int tsi) { struct lws_context_per_thread *pt = &context->pt[tsi]; struct lws_vhost *vh = context->vhost_list; @@ -167,44 +160,50 @@ lws_libev_destroyloop(struct lws_context *context, int tsi) ev_io_stop(pt->ev.io_loop, &vh->w_accept.ev.watcher); vh = vh->vhost_next; } - if (context->use_event_loop_sigint) - ev_signal_stop(pt->ev.io_loop, - &pt->w_sigint.ev.watcher); + if (!pt->event_loop_foreign) + ev_signal_stop(pt->ev.io_loop, &pt->w_sigint.ev.watcher); if (!pt->event_loop_foreign) ev_loop_destroy(pt->ev.io_loop); } -LWS_VISIBLE void -lws_libev_accept(struct lws *new_wsi, lws_sock_file_fd_type desc) +static int +elops_init_context_ev(struct lws_context *context, + const struct lws_context_creation_info *info) { - struct lws_context *context = lws_get_context(new_wsi); - struct ev_io *r = &new_wsi->w_read.ev.watcher; - struct ev_io *w = &new_wsi->w_write.ev.watcher; + int n; + + context->eventlib_signal_cb = info->signal_cb; + + for (n = 0; n < context->count_threads; n++) + context->pt[n].w_sigint.context = context; + + return 0; +} + +static void +elops_accept_ev(struct lws *wsi) +{ + struct lws_context *context = lws_get_context(wsi); + struct ev_io *r = &wsi->w_read.ev.watcher; + struct ev_io *w = &wsi->w_write.ev.watcher; int fd; - if (!LWS_LIBEV_ENABLED(context)) - return; - - if (new_wsi->role_ops == &role_ops_raw_file) - fd = desc.filefd; + if (wsi->role_ops->file_handle) + fd = wsi->desc.filefd; else - fd = desc.sockfd; + fd = wsi->desc.sockfd; - new_wsi->w_read.context = context; - new_wsi->w_write.context = context; + wsi->w_read.context = context; + wsi->w_write.context = context; ev_io_init(r, lws_accept_cb, fd, EV_READ); ev_io_init(w, lws_accept_cb, fd, EV_WRITE); } -LWS_VISIBLE void -lws_libev_io(struct lws *wsi, int flags) +static void +elops_io_ev(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]; - if (!LWS_LIBEV_ENABLED(context)) - return; - if (!pt->ev.io_loop) return; @@ -224,23 +223,28 @@ lws_libev_io(struct lws *wsi, int flags) } } -LWS_VISIBLE int -lws_libev_init_fd_table(struct lws_context *context) -{ - int n; - - if (!LWS_LIBEV_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_libev_run(const struct lws_context *context, int tsi) +static void +elops_run_pt_ev(struct lws_context *context, int tsi) { if (context->pt[tsi].ev.io_loop && LWS_LIBEV_ENABLED(context)) ev_run(context->pt[tsi].ev.io_loop, 0); } + +struct lws_event_loop_ops event_loop_ops_ev = { + /* name */ "libev", + /* init_context */ elops_init_context_ev, + /* destroy_context1 */ NULL, + /* destroy_context2 */ NULL, + /* init_vhost_listen_wsi */ NULL, + /* init_pt */ elops_init_pt_ev, + /* wsi_logical_close */ NULL, + /* check_client_connect_ok */ NULL, + /* close_handle_manually */ NULL, + /* accept */ elops_accept_ev, + /* io */ elops_io_ev, + /* run_pt */ elops_run_pt_ev, + /* destroy_pt */ elops_destroy_pt_ev, + /* destroy wsi */ NULL, + + /* periodic_events_available */ 0, +}; diff --git a/lib/event-libs/libev/private.h b/lib/event-libs/libev/private.h index eeec24b9..12f1d9f6 100644 --- a/lib/event-libs/libev/private.h +++ b/lib/event-libs/libev/private.h @@ -18,7 +18,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301 USA * - * This is included from private-libwebsockets.h if LWS_ROLE_WS + * This is included from private-libwebsockets.h if LWS_WITH_LIBEV */ #include @@ -36,18 +36,15 @@ struct lws_signal_watcher_libev { }; struct lws_context_eventlibs_libev { - lws_ev_signal_cb_t *sigint_cb; + int placeholder; }; -LWS_EXTERN void -lws_libev_accept(struct lws *new_wsi, lws_sock_file_fd_type desc); -LWS_EXTERN void -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_destroyloop(struct lws_context *context, int tsi); +#define LWS_LIBEV_ENABLED(context) lws_check_opt(context->options, \ + LWS_SERVER_OPTION_LIBEV) + +extern struct lws_event_loop_ops event_loop_ops_ev; + LWS_EXTERN void lws_libev_run(const struct lws_context *context, int tsi); -#define LWS_LIBEV_ENABLED(context) lws_check_opt(context->options, LWS_SERVER_OPTION_LIBEV) -LWS_EXTERN void lws_feature_status_libev(const struct lws_context_creation_info *info); +LWS_EXTERN void +lws_feature_status_libev(const struct lws_context_creation_info *info); diff --git a/lib/event-libs/libevent/libevent.c b/lib/event-libs/libevent/libevent.c index 34069ac0..53c4bf95 100644 --- a/lib/event-libs/libevent/libevent.c +++ b/lib/event-libs/libevent/libevent.c @@ -68,28 +68,22 @@ lws_event_sigint_cb(evutil_socket_t sock_fd, short revents, void *ctx) { struct lws_context_per_thread *pt = ctx; + if (pt->context->eventlib_signal_cb) { + pt->context->eventlib_signal_cb( + (void *)(lws_intptr_t)sock_fd, revents); + + return; + } if (!pt->event_loop_foreign) event_base_loopbreak(pt->event.io_loop); } -LWS_VISIBLE int -lws_event_sigint_cfg(struct lws_context *context, int use_event_sigint, -lws_event_signal_cb_t *cb) -{ - context->use_event_loop_sigint = use_event_sigint; - if (cb) - context->event.sigint_cb = cb; - else - context->event.sigint_cb = &lws_event_sigint_cb; - return 0; -} - -LWS_VISIBLE int -lws_event_initloop(struct lws_context *context, struct event_base *loop, -int tsi) +static int +elops_init_pt_event(struct lws_context *context, void *_loop, int tsi) { struct lws_vhost *vh = context->vhost_list; + struct event_base *loop = (struct event_base *)_loop; if (!loop) context->pt[tsi].event.io_loop = event_base_new(); @@ -98,9 +92,6 @@ int tsi) context->pt[tsi].event.io_loop = loop; } - if (lws_create_event_pipes(context)) - return 1; - /* * Initialize all events with the listening sockets * and register a callback for read operations @@ -118,95 +109,61 @@ int tsi) vh = vh->vhost_next; } - /* Register the signal watcher unless the user says not to */ - if (!context->use_event_loop_sigint) + /* Register the signal watcher unless it's a foreign loop */ + if (context->pt[tsi].event_loop_foreign) return 0; context->pt[tsi].w_sigint.event.watcher = evsignal_new(loop, SIGINT, - context->event.sigint_cb, &context->pt[tsi]); + lws_event_sigint_cb, &context->pt[tsi]); event_add(context->pt[tsi].w_sigint.event.watcher, NULL); return 0; } -void -lws_libevent_destroyloop(struct lws_context *context, int tsi) +static int +elops_init_context_event(struct lws_context *context, + const struct lws_context_creation_info *info) { - struct lws_context_per_thread *pt = &context->pt[tsi]; - struct lws_vhost *vh = context->vhost_list; + int n; - if (!lws_check_opt(context->options, LWS_SERVER_OPTION_LIBEVENT)) - return; + context->eventlib_signal_cb = info->signal_cb; - if (!pt->event.io_loop) - return; + for (n = 0; n < context->count_threads; n++) + context->pt[n].w_sigint.context = context; - /* - * Free all events with the listening sockets - */ - while (vh) { - if (vh->lserv_wsi) { - event_free(vh->lserv_wsi->w_read.event.watcher); - vh->lserv_wsi->w_read.event.watcher = NULL; - } - vh = vh->vhost_next; - } - - if (context->use_event_loop_sigint) - event_free(pt->w_sigint.event.watcher); - if (!pt->event_loop_foreign) - event_base_free(pt->event.io_loop); + return 0; } -LWS_VISIBLE void -lws_libevent_accept(struct lws *new_wsi, lws_sock_file_fd_type desc) +static void +elops_accept_event(struct lws *wsi) { - struct lws_context *context = lws_get_context(new_wsi); + struct lws_context *context = lws_get_context(wsi); struct lws_context_per_thread *pt; int fd; - if (!LWS_LIBEVENT_ENABLED(context)) - return; - - new_wsi->w_read.context = context; - new_wsi->w_write.context = context; + wsi->w_read.context = context; + wsi->w_write.context = context; // Initialize the event - pt = &context->pt[(int)new_wsi->tsi]; + pt = &context->pt[(int)wsi->tsi]; - if (new_wsi->role_ops == &role_ops_raw_file) - fd = desc.filefd; + if (wsi->role_ops->file_handle) + fd = wsi->desc.filefd; else - fd = desc.sockfd; + fd = wsi->desc.sockfd; - new_wsi->w_read.event.watcher = event_new(pt->event.io_loop, fd, - (EV_READ | EV_PERSIST), lws_event_cb, &new_wsi->w_read); - new_wsi->w_write.event.watcher = event_new(pt->event.io_loop, fd, - (EV_WRITE | EV_PERSIST), lws_event_cb, &new_wsi->w_write); + wsi->w_read.event.watcher = event_new(pt->event.io_loop, fd, + (EV_READ | EV_PERSIST), lws_event_cb, &wsi->w_read); + wsi->w_write.event.watcher = event_new(pt->event.io_loop, fd, + (EV_WRITE | EV_PERSIST), lws_event_cb, &wsi->w_write); } -LWS_VISIBLE void -lws_libevent_destroy(struct lws *wsi) -{ - if (!wsi) - return; - - if(wsi->w_read.event.watcher) - event_free(wsi->w_read.event.watcher); - - if(wsi->w_write.event.watcher) - event_free(wsi->w_write.event.watcher); -} - -LWS_VISIBLE void -lws_libevent_io(struct lws *wsi, int flags) +static void +elops_io_event(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]; - if (!LWS_LIBEVENT_ENABLED(context)) - return; - if (!pt->event.io_loop || context->being_destroyed) return; @@ -227,25 +184,73 @@ lws_libevent_io(struct lws *wsi, int flags) } } -LWS_VISIBLE int -lws_libevent_init_fd_table(struct lws_context *context) -{ - int n; - - if (!LWS_LIBEVENT_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_libevent_run(const struct lws_context *context, int tsi) +static void +elops_run_pt_event(struct lws_context *context, int tsi) { /* Run / Dispatch the event_base loop */ if (context->pt[tsi].event.io_loop && LWS_LIBEVENT_ENABLED(context)) event_base_dispatch(context->pt[tsi].event.io_loop); } + +static void +elops_destroy_pt_event(struct lws_context *context, int tsi) +{ + struct lws_context_per_thread *pt = &context->pt[tsi]; + struct lws_vhost *vh = context->vhost_list; + + if (!lws_check_opt(context->options, LWS_SERVER_OPTION_LIBEVENT)) + return; + + if (!pt->event.io_loop) + return; + + /* + * Free all events with the listening sockets + */ + while (vh) { + if (vh->lserv_wsi) { + event_free(vh->lserv_wsi->w_read.event.watcher); + vh->lserv_wsi->w_read.event.watcher = NULL; + } + vh = vh->vhost_next; + } + + if (!pt->event_loop_foreign) + event_free(pt->w_sigint.event.watcher); + if (!pt->event_loop_foreign) + event_base_free(pt->event.io_loop); +} + +static void +elops_destroy_wsi_event(struct lws *wsi) +{ + if (!wsi) + return; + + if(wsi->w_read.event.watcher) + event_free(wsi->w_read.event.watcher); + + if(wsi->w_write.event.watcher) + event_free(wsi->w_write.event.watcher); +} + + +struct lws_event_loop_ops event_loop_ops_event = { + /* name */ "libevent", + /* init_context */ elops_init_context_event, + /* destroy_context1 */ NULL, + /* destroy_context2 */ NULL, + /* init_vhost_listen_wsi */ NULL, + /* init_pt */ elops_init_pt_event, + /* wsi_logical_close */ NULL, + /* check_client_connect_ok */ NULL, + /* close_handle_manually */ NULL, + /* accept */ elops_accept_event, + /* io */ elops_io_event, + /* run_pt */ elops_run_pt_event, + /* destroy_pt */ elops_destroy_pt_event, + /* destroy wsi */ elops_destroy_wsi_event, + + /* periodic_events_available */ 0, +}; diff --git a/lib/event-libs/libevent/private.h b/lib/event-libs/libevent/private.h index 00cdcbb5..4715bc2a 100644 --- a/lib/event-libs/libevent/private.h +++ b/lib/event-libs/libevent/private.h @@ -18,7 +18,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301 USA * - * This is included from private-libwebsockets.h if LWS_ROLE_WS + * This is included from private-libwebsockets.h if LWS_WITH_LIBEVENT */ #include @@ -36,25 +36,14 @@ struct lws_signal_watcher_libevent { }; struct lws_context_eventlibs_libevent { -#if defined(LWS_HIDE_LIBEVENT) - void * sigint_cb; -#else - lws_event_signal_cb_t *sigint_cb; -#endif + int placeholder; }; -LWS_EXTERN void -lws_libevent_accept(struct lws *new_wsi, lws_sock_file_fd_type desc); -LWS_VISIBLE void -lws_libevent_destroy(struct lws *wsi); -LWS_EXTERN void -lws_libevent_io(struct lws *wsi, int flags); -LWS_EXTERN int -lws_libevent_init_fd_table(struct lws_context *context); -LWS_EXTERN void -lws_libevent_destroyloop(struct lws_context *context, int tsi); -LWS_EXTERN void -lws_libevent_run(const struct lws_context *context, int tsi); -#define LWS_LIBEVENT_ENABLED(context) lws_check_opt(context->options, LWS_SERVER_OPTION_LIBEVENT) -LWS_EXTERN void lws_feature_status_libevent(const struct lws_context_creation_info *info); +#define LWS_LIBEVENT_ENABLED(context) lws_check_opt(context->options, \ + LWS_SERVER_OPTION_LIBEVENT) + +extern struct lws_event_loop_ops event_loop_ops_event; + +LWS_EXTERN void +lws_feature_status_libevent(const struct lws_context_creation_info *info); diff --git a/lib/event-libs/libuv/libuv.c b/lib/event-libs/libuv/libuv.c index 8b89869d..b8990502 100644 --- a/lib/event-libs/libuv/libuv.c +++ b/lib/event-libs/libuv/libuv.c @@ -134,24 +134,73 @@ lws_io_cb(uv_poll_t *watcher, int status, int revents) uv_idle_start(&pt->uv.idle, lws_uv_idle); } -LWS_VISIBLE void -lws_uv_sigint_cb(uv_signal_t *watcher, int signum) +/* + * 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 LWS_EXTERN void +lws_libuv_stop(struct lws_context *context) { - lwsl_err("internal signal handler caught signal %d\n", signum); - lws_libuv_stop(watcher->data); + struct lws_context_per_thread *pt; + int n, m; + + lwsl_err("%s\n", __func__); + + if (context->requested_kill) { + lwsl_err("%s: ignoring\n", __func__); + return; + } + + context->requested_kill = 1; + + m = context->count_threads; + context->being_destroyed = 1; + + /* + * Phase 1: start the close of every dynamic uv handle + */ + + while (m--) { + pt = &context->pt[m]; + + if (pt->pipe_wsi) { + uv_poll_stop(&pt->pipe_wsi->w_read.uv.watcher); + lws_destroy_event_pipe(pt->pipe_wsi); + pt->pipe_wsi = NULL; + } + + 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, __func__ + /* no protocol close */); + n--; + } + } + + lwsl_info("%s: started closing all wsi\n", __func__); + + /* we cannot have completed... there are at least the cancel pipes */ } -LWS_VISIBLE int -lws_uv_sigint_cfg(struct lws_context *context, int use_uv_sigint, - uv_signal_cb cb) +static void +lws_uv_signal_handler(uv_signal_t *watcher, int signum) { - context->use_event_loop_sigint = use_uv_sigint; - if (cb) - context->uv.sigint_cb = cb; - else - context->uv.sigint_cb = &lws_uv_sigint_cb; + struct lws_context *context = watcher->data; - return 0; + if (context->eventlib_signal_cb) { + context->eventlib_signal_cb((void *)watcher, signum); + + return; + } + + lwsl_err("internal signal handler caught signal %d\n", signum); + lws_libuv_stop(watcher->data); } static void @@ -174,127 +223,6 @@ lws_uv_timeout_cb(uv_timer_t *timer static const int sigs[] = { SIGINT, SIGTERM, SIGSEGV, SIGFPE, SIGHUP }; -int -lws_uv_initvhost(struct lws_vhost* vh, struct lws* wsi) -{ - struct lws_context_per_thread *pt; - int n; - - if (!LWS_LIBUV_ENABLED(vh->context)) - return 0; - if (!wsi) - wsi = vh->lserv_wsi; - if (!wsi) - return 0; - if (wsi->w_read.context) - return 0; - - pt = &vh->context->pt[(int)wsi->tsi]; - if (!pt->uv.io_loop) - return 0; - - wsi->w_read.context = vh->context; - n = uv_poll_init_socket(pt->uv.io_loop, - &wsi->w_read.uv.watcher, wsi->desc.sockfd); - if (n) { - lwsl_err("uv_poll_init failed %d, sockfd=%p\n", - n, (void *)(lws_intptr_t)wsi->desc.sockfd); - - return -1; - } - lws_libuv_io(wsi, LWS_EV_START | LWS_EV_READ); - - return 0; -} - -/* - * This needs to be called after vhosts have been defined. - * - * If later, after server start, another vhost is added, this must be - * called again to bind the vhost - */ - -LWS_VISIBLE int -lws_uv_initloop(struct lws_context *context, uv_loop_t *loop, int tsi) -{ - struct lws_context_per_thread *pt = &context->pt[tsi]; - struct lws_vhost *vh = context->vhost_list; - int status = 0, n, ns, first = 1; - - if (!pt->uv.io_loop) { - if (!loop) { - loop = lws_malloc(sizeof(*loop), "libuv loop"); - if (!loop) { - lwsl_err("OOM\n"); - return -1; - } - #if UV_VERSION_MAJOR > 0 - uv_loop_init(loop); - #else - lwsl_err("This libuv is too old to work...\n"); - return 1; - #endif - pt->event_loop_foreign = 0; - } else { - lwsl_notice(" Using foreign event loop...\n"); - pt->event_loop_foreign = 1; - } - - pt->uv.io_loop = loop; - uv_idle_init(loop, &pt->uv.idle); - LWS_UV_REFCOUNT_STATIC_HANDLE_NEW(&pt->uv.idle, context); - - - ns = ARRAY_SIZE(sigs); - if (lws_check_opt(context->options, - LWS_SERVER_OPTION_UV_NO_SIGSEGV_SIGFPE_SPIN)) - ns = 2; - - if (pt->context->use_event_loop_sigint) { - assert(ns <= (int)ARRAY_SIZE(pt->uv.signals)); - for (n = 0; n < ns; n++) { - uv_signal_init(loop, &pt->uv.signals[n]); - LWS_UV_REFCOUNT_STATIC_HANDLE_NEW(&pt->uv.signals[n], - context); - pt->uv.signals[n].data = pt->context; - uv_signal_start(&pt->uv.signals[n], - context->uv.sigint_cb, sigs[n]); - } - } - } else - first = 0; - - if (lws_create_event_pipes(context)) - goto bail; - - /* - * Initialize the accept wsi read watcher with all the listening sockets - * 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. - */ - while (vh) { - if (lws_uv_initvhost(vh, vh->lserv_wsi) == -1) - return -1; - vh = vh->vhost_next; - } - - if (!first) - return status; - - uv_timer_init(pt->uv.io_loop, &pt->uv.timeout_watcher); - LWS_UV_REFCOUNT_STATIC_HANDLE_NEW(&pt->uv.timeout_watcher, context); - uv_timer_start(&pt->uv.timeout_watcher, lws_uv_timeout_cb, 10, 1000); - uv_timer_init(pt->uv.io_loop, &pt->uv.hrtimer); - LWS_UV_REFCOUNT_STATIC_HANDLE_NEW(&pt->uv.hrtimer, context); - - return status; - -bail: - return -1; -} - /* * Closing Phase 2: Close callback for a static UV asset */ @@ -326,17 +254,16 @@ lws_uv_close_cb_sa(uv_handle_t *handle) for (n = 0; n < context->count_threads; n++) { struct lws_context_per_thread *pt = &context->pt[n]; - if (!pt->uv.io_loop || !LWS_LIBUV_ENABLED(context)) - continue; - - uv_stop(pt->uv.io_loop); - - /* - * we can't delete non-foreign loop here, because - * the uv_stop() hasn't got us out of the uv_run() - * yet. So we do it in context destroy. - */ + if (pt->uv.io_loop && !pt->event_loop_foreign) + uv_stop(pt->uv.io_loop); } + + if (!context->pt[0].event_loop_foreign) { + lwsl_info("%s: calling lws_context_destroy2\n", __func__); + lws_context_destroy2(context); + } + + lwsl_info("%s: all done\n", __func__); } /* @@ -378,136 +305,6 @@ lws_close_all_handles_in_loop(uv_loop_t *loop) uv_walk(loop, lws_uv_walk_cb, NULL); } -void -lws_libuv_destroyloop(struct lws_context *context, int tsi) -{ - struct lws_context_per_thread *pt = &context->pt[tsi]; - int m, /* budget = 100, */ ns; - - lwsl_info("%s: %d\n", __func__, tsi); - - if (!lws_check_opt(context->options, LWS_SERVER_OPTION_LIBUV)) - return; - - if (!pt->uv.io_loop) - return; - - if (pt->event_loop_destroy_processing_done) - return; - - pt->event_loop_destroy_processing_done = 1; - - if (context->use_event_loop_sigint) { - uv_signal_stop(&pt->w_sigint.uv.watcher); - - ns = ARRAY_SIZE(sigs); - if (lws_check_opt(context->options, - LWS_SERVER_OPTION_UV_NO_SIGSEGV_SIGFPE_SPIN)) - ns = 2; - - for (m = 0; m < ns; m++) { - uv_signal_stop(&pt->uv.signals[m]); - uv_close((uv_handle_t *)&pt->uv.signals[m], - lws_uv_close_cb_sa); - } - } - - uv_timer_stop(&pt->uv.timeout_watcher); - uv_close((uv_handle_t *)&pt->uv.timeout_watcher, lws_uv_close_cb_sa); - uv_timer_stop(&pt->uv.hrtimer); - uv_close((uv_handle_t *)&pt->uv.hrtimer, lws_uv_close_cb_sa); - - uv_idle_stop(&pt->uv.idle); - uv_close((uv_handle_t *)&pt->uv.idle, lws_uv_close_cb_sa); -} - -void -lws_libuv_accept(struct lws *wsi, lws_sock_file_fd_type desc) -{ - 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; - - wsi->w_read.context = context; - if (wsi->role_ops == &role_ops_raw_file || wsi->event_pipe) - uv_poll_init(pt->uv.io_loop, &wsi->w_read.uv.watcher, - (int)(long long)desc.filefd); - else - uv_poll_init_socket(pt->uv.io_loop, &wsi->w_read.uv.watcher, - desc.sockfd); -} - -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]; - struct lws_io_watcher *w = &wsi->w_read; - int current_events = w->actual_events & (UV_READABLE | UV_WRITABLE); - - if (!LWS_LIBUV_ENABLED(context)) - return; - - /* w->context is set after the loop is initialized */ - - if (!pt->uv.io_loop || !w->context) { - lwsl_info("%s: no io loop yet\n", __func__); - return; - } - - if (!((flags & (LWS_EV_START | LWS_EV_STOP)) && - (flags & (LWS_EV_READ | LWS_EV_WRITE)))) { - lwsl_err("%s: assert: flags %d", __func__, flags); - assert(0); - } - - 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_io_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_io_cb); - } - - w->actual_events = current_events; -} - -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].uv.io_loop && LWS_LIBUV_ENABLED(context)) - uv_run(context->pt[tsi].uv.io_loop, 0); -} LWS_VISIBLE void lws_libuv_stop_without_kill(const struct lws_context *context, int tsi) @@ -527,123 +324,6 @@ lws_uv_getloop(struct lws_context *context, int tsi) return NULL; } -static void -lws_libuv_closewsi(uv_handle_t* handle) -{ - struct lws *n = NULL, *wsi = (struct lws *)(((char *)handle) - - (char *)(&n->w_read.uv.watcher)); - struct lws_context *context = lws_get_context(wsi); - struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; - int lspd = 0, m; - - /* - * We get called back here for every wsi that closes - */ - - if (wsi->role_ops == &role_ops_listen && wsi->context->deprecated) { - lspd = 1; - context->deprecation_pending_listen_close_count--; - if (!context->deprecation_pending_listen_close_count) - lspd = 2; - } - - lws_pt_lock(pt, __func__); - __lws_close_free_wsi_final(wsi); - lws_pt_unlock(pt); - - if (lspd == 2 && context->deprecation_cb) { - lwsl_notice("calling deprecation callback\n"); - context->deprecation_cb(); - } - - lwsl_info("%s: sa left %d: dyn left: %d\n", __func__, - context->count_event_loop_static_asset_handles, - context->count_wsi_allocated); - - /* - * eventually, we closed all the wsi... - */ - - if (context->requested_kill && !context->count_wsi_allocated) { - struct lws_vhost *vh = context->vhost_list; - - /* - * Start Closing Phase 2: close of static handles - */ - - lwsl_info("%s: all lws dynamic handles down, closing static\n", - __func__); - - for (m = 0; m < context->count_threads; m++) - lws_libuv_destroyloop(context, m); - - /* protocols may have initialized libuv objects */ - - while (vh) { - lws_vhost_destroy1(vh); - vh = vh->vhost_next; - } - } -} - -/* - * 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; - - if (context->requested_kill) - return; - - context->requested_kill = 1; - - m = context->count_threads; - context->being_destroyed = 1; - - /* - * Phase 1: start the close of every dynamic uv handle - */ - - 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, __func__ - /* no protocol close */); - n--; - } - } - - lwsl_info("%s: started closing all wsi\n", __func__); - - /* we cannot have completed... there are at least the cancel pipes */ -} - -void -lws_libuv_closehandle(struct lws *wsi) -{ - if (wsi->told_event_loop_closed) { - assert(0); - return; - } - - wsi->told_event_loop_closed = 1; - - /* required to defer actual deletion until libuv has processed it */ - uv_close((uv_handle_t*)&wsi->w_read.uv.watcher, lws_libuv_closewsi); -} - static void lws_libuv_closewsi_m(uv_handle_t* handle) { @@ -652,16 +332,6 @@ lws_libuv_closewsi_m(uv_handle_t* handle) compatible_close(sockfd); } -void -lws_libuv_closehandle_manually(struct lws *wsi) -{ - uv_handle_t *h = (void *)&wsi->w_read.uv.watcher; - - h->data = (void *)(lws_intptr_t)wsi->desc.sockfd; - /* required to defer actual deletion until libuv has processed it */ - uv_close((uv_handle_t*)&wsi->w_read.uv.watcher, lws_libuv_closewsi_m); -} - int lws_libuv_check_watcher_active(struct lws *wsi) { @@ -833,3 +503,441 @@ lws_plat_plugins_destroy(struct lws_context *context) #endif +static int +elops_init_context_uv(struct lws_context *context, + const struct lws_context_creation_info *info) +{ + int n; + + context->eventlib_signal_cb = info->signal_cb; + + for (n = 0; n < context->count_threads; n++) + context->pt[n].w_sigint.context = context; + + return 0; +} + +static int +elops_destroy_context1_uv(struct lws_context *context) +{ + struct lws_context_per_thread *pt; + int n, m; + + for (n = 0; n < context->count_threads; n++) { + int budget = 10000; + pt = &context->pt[n]; + + /* only for internal loops... */ + + if (!pt->event_loop_foreign) { + + while (budget-- && (m = uv_run(pt->uv.io_loop, + UV_RUN_NOWAIT))) + ; + if (m) + lwsl_err("%s: tsi %d: failed to close everything\n", __func__, n); + + } + } + + /* call destroy2 if internal loop */ + return !context->pt[0].event_loop_foreign; +} + +static int +elops_destroy_context2_uv(struct lws_context *context) +{ + struct lws_context_per_thread *pt; + int n, internal = 0; + + for (n = 0; n < context->count_threads; n++) { + pt = &context->pt[n]; + + /* only for internal loops... */ + + if (!pt->event_loop_foreign && pt->uv.io_loop) { + internal = 1; + if (!context->finalize_destroy_after_internal_loops_stopped) + uv_stop(pt->uv.io_loop); + else { +#if UV_VERSION_MAJOR > 0 + uv_loop_close(pt->uv.io_loop); +#endif + lws_free_set_NULL(pt->uv.io_loop); + } + } + } + + return internal; +} + +static int +elops_wsi_logical_close_uv(struct lws *wsi) +{ + if (wsi->parent_carries_io || !lws_sockfd_valid(wsi->desc.sockfd)) + return 0; + + if (wsi->listener || wsi->event_pipe) { + lwsl_debug("%s: %p: %d %d stop listener / pipe poll\n", + __func__, wsi, wsi->listener, wsi->event_pipe); + uv_poll_stop(&wsi->w_read.uv.watcher); + } + lwsl_debug("%s: lws_libuv_closehandle: wsi %p\n", __func__, wsi); + /* + * libuv has to do his own close handle processing asynchronously + */ + lws_libuv_closehandle(wsi); + + return 1; /* do not complete the wsi close, uv close cb will do it */ +} + +static int +elops_check_client_connect_ok_uv(struct lws *wsi) +{ + if (lws_libuv_check_watcher_active(wsi)) { + lwsl_warn("Waiting for libuv watcher to close\n"); + return 1; + } + + return 0; +} + +static void +elops_close_handle_manually_uv(struct lws *wsi) +{ + uv_handle_t *h = (void *)&wsi->w_read.uv.watcher; + + lwsl_debug("%s: lws_libuv_closehandle: wsi %p\n", __func__, wsi); + h->data = (void *)(lws_intptr_t)wsi->desc.sockfd; + /* required to defer actual deletion until libuv has processed it */ + uv_close((uv_handle_t*)&wsi->w_read.uv.watcher, lws_libuv_closewsi_m); +} + +static void +elops_accept_uv(struct lws *wsi) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + + wsi->w_read.context = wsi->context; + if (wsi->role_ops->file_handle) + uv_poll_init(pt->uv.io_loop, &wsi->w_read.uv.watcher, + (int)(long long)wsi->desc.filefd); + else + uv_poll_init_socket(pt->uv.io_loop, &wsi->w_read.uv.watcher, + wsi->desc.sockfd); +} + +static void +elops_io_uv(struct lws *wsi, int flags) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + struct lws_io_watcher *w = &wsi->w_read; + int current_events = w->actual_events & (UV_READABLE | UV_WRITABLE); + + lwsl_debug("%s: %p: %d\n", __func__, wsi, flags); + + /* w->context is set after the loop is initialized */ + + if (!pt->uv.io_loop || !w->context) { + lwsl_info("%s: no io loop yet\n", __func__); + return; + } + + if (!((flags & (LWS_EV_START | LWS_EV_STOP)) && + (flags & (LWS_EV_READ | LWS_EV_WRITE)))) { + lwsl_err("%s: assert: flags %d", __func__, flags); + assert(0); + } + + 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_io_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_io_cb); + } + + w->actual_events = current_events; +} + +static int +elops_init_vhost_listen_wsi_uv(struct lws *wsi) +{ + struct lws_context_per_thread *pt; + struct lws_vhost *vh = wsi->vhost; + int n; + + if (!wsi) + wsi = vh->lserv_wsi; + if (!wsi) + return 0; + if (wsi->w_read.context) + return 0; + + pt = &vh->context->pt[(int)wsi->tsi]; + if (!pt->uv.io_loop) + return 0; + + wsi->w_read.context = vh->context; + n = uv_poll_init_socket(pt->uv.io_loop, + &wsi->w_read.uv.watcher, wsi->desc.sockfd); + if (n) { + lwsl_err("uv_poll_init failed %d, sockfd=%p\n", + n, (void *)(lws_intptr_t)wsi->desc.sockfd); + + return -1; + } + elops_io_uv(wsi, LWS_EV_START | LWS_EV_READ); + + return 0; +} + +static void +elops_run_pt_uv(struct lws_context *context, int tsi) +{ + if (context->pt[tsi].uv.io_loop && LWS_LIBUV_ENABLED(context)) + uv_run(context->pt[tsi].uv.io_loop, 0); +} + +static void +elops_destroy_pt_uv(struct lws_context *context, int tsi) +{ + struct lws_context_per_thread *pt = &context->pt[tsi]; + int m, ns; + + lwsl_info("%s: %d\n", __func__, tsi); + + if (!lws_check_opt(context->options, LWS_SERVER_OPTION_LIBUV)) + return; + + if (!pt->uv.io_loop) + return; + + if (pt->event_loop_destroy_processing_done) + return; + + pt->event_loop_destroy_processing_done = 1; + + if (!pt->event_loop_foreign) { + uv_signal_stop(&pt->w_sigint.uv.watcher); + + ns = ARRAY_SIZE(sigs); + if (lws_check_opt(context->options, + LWS_SERVER_OPTION_UV_NO_SIGSEGV_SIGFPE_SPIN)) + ns = 2; + + for (m = 0; m < ns; m++) { + uv_signal_stop(&pt->uv.signals[m]); + uv_close((uv_handle_t *)&pt->uv.signals[m], + lws_uv_close_cb_sa); + } + } else + lwsl_debug("%s: not closing pt signals\n", __func__); + + uv_timer_stop(&pt->uv.timeout_watcher); + uv_close((uv_handle_t *)&pt->uv.timeout_watcher, lws_uv_close_cb_sa); + uv_timer_stop(&pt->uv.hrtimer); + uv_close((uv_handle_t *)&pt->uv.hrtimer, lws_uv_close_cb_sa); + + uv_idle_stop(&pt->uv.idle); + uv_close((uv_handle_t *)&pt->uv.idle, lws_uv_close_cb_sa); +} + +/* + * This needs to be called after vhosts have been defined. + * + * If later, after server start, another vhost is added, this must be + * called again to bind the vhost + */ + +LWS_VISIBLE int +elops_init_pt_uv(struct lws_context *context, void *_loop, int tsi) +{ + struct lws_context_per_thread *pt = &context->pt[tsi]; + struct lws_vhost *vh = context->vhost_list; + int status = 0, n, ns, first = 1; + uv_loop_t *loop = (uv_loop_t *)_loop; + + if (!pt->uv.io_loop) { + if (!loop) { + loop = lws_malloc(sizeof(*loop), "libuv loop"); + if (!loop) { + lwsl_err("OOM\n"); + return -1; + } + #if UV_VERSION_MAJOR > 0 + uv_loop_init(loop); + #else + lwsl_err("This libuv is too old to work...\n"); + return 1; + #endif + pt->event_loop_foreign = 0; + } else { + lwsl_notice(" Using foreign event loop...\n"); + pt->event_loop_foreign = 1; + } + + pt->uv.io_loop = loop; + uv_idle_init(loop, &pt->uv.idle); + LWS_UV_REFCOUNT_STATIC_HANDLE_NEW(&pt->uv.idle, context); + + + ns = ARRAY_SIZE(sigs); + if (lws_check_opt(context->options, + LWS_SERVER_OPTION_UV_NO_SIGSEGV_SIGFPE_SPIN)) + ns = 2; + + if (!pt->event_loop_foreign) { + assert(ns <= (int)ARRAY_SIZE(pt->uv.signals)); + for (n = 0; n < ns; n++) { + uv_signal_init(loop, &pt->uv.signals[n]); + LWS_UV_REFCOUNT_STATIC_HANDLE_NEW(&pt->uv.signals[n], + context); + pt->uv.signals[n].data = pt->context; + uv_signal_start(&pt->uv.signals[n], + lws_uv_signal_handler, sigs[n]); + } + } + } else + first = 0; + + /* + * Initialize the accept wsi read watcher with all the listening sockets + * 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. + */ + while (vh) { + if (elops_init_vhost_listen_wsi_uv(vh->lserv_wsi) == -1) + return -1; + vh = vh->vhost_next; + } + + if (!first) + return status; + + uv_timer_init(pt->uv.io_loop, &pt->uv.timeout_watcher); + LWS_UV_REFCOUNT_STATIC_HANDLE_NEW(&pt->uv.timeout_watcher, context); + uv_timer_start(&pt->uv.timeout_watcher, lws_uv_timeout_cb, 10, 1000); + + uv_timer_init(pt->uv.io_loop, &pt->uv.hrtimer); + LWS_UV_REFCOUNT_STATIC_HANDLE_NEW(&pt->uv.hrtimer, context); + + return status; +} + +static void +lws_libuv_closewsi(uv_handle_t* handle) +{ + struct lws *n = NULL, *wsi = (struct lws *)(((char *)handle) - + (char *)(&n->w_read.uv.watcher)); + struct lws_context *context = lws_get_context(wsi); + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + int lspd = 0, m; + + lwsl_info("%s: %p\n", __func__, wsi); + + /* + * We get called back here for every wsi that closes + */ + + if (wsi->role_ops == &role_ops_listen && wsi->context->deprecated) { + lspd = 1; + context->deprecation_pending_listen_close_count--; + if (!context->deprecation_pending_listen_close_count) + lspd = 2; + } + + lws_pt_lock(pt, __func__); + __lws_close_free_wsi_final(wsi); + lws_pt_unlock(pt); + + if (lspd == 2 && context->deprecation_cb) { + lwsl_notice("calling deprecation callback\n"); + context->deprecation_cb(); + } + + lwsl_info("%s: sa left %d: dyn left: %d (rk %d)\n", __func__, + context->count_event_loop_static_asset_handles, + context->count_wsi_allocated, context->requested_kill); + + /* + * eventually, we closed all the wsi... + */ + + if (context->requested_kill && !context->count_wsi_allocated) { + struct lws_vhost *vh = context->vhost_list; + + /* + * Start Closing Phase 2: close of static handles + */ + + lwsl_info("%s: all lws dynamic handles down, closing static\n", + __func__); + + for (m = 0; m < context->count_threads; m++) + elops_destroy_pt_uv(context, m); + + /* protocols may have initialized libuv objects */ + + while (vh) { + lws_vhost_destroy1(vh); + vh = vh->vhost_next; + } + + if (context->pt[0].event_loop_foreign) { + lwsl_info("%s: calling lws_context_destroy2\n", __func__); + lws_context_destroy2(context); + } + } +} + +void +lws_libuv_closehandle(struct lws *wsi) +{ + if (wsi->told_event_loop_closed) { + assert(0); + return; + } + + lwsl_debug("%s: %p\n", __func__, wsi); + + wsi->told_event_loop_closed = 1; + + /* required to defer actual deletion until libuv has processed it */ + uv_close((uv_handle_t*)&wsi->w_read.uv.watcher, lws_libuv_closewsi); +} + +struct lws_event_loop_ops event_loop_ops_uv = { + /* name */ "libuv", + /* init_context */ elops_init_context_uv, + /* destroy_context1 */ elops_destroy_context1_uv, + /* destroy_context2 */ elops_destroy_context2_uv, + /* init_vhost_listen_wsi */ elops_init_vhost_listen_wsi_uv, + /* init_pt */ elops_init_pt_uv, + /* wsi_logical_close */ elops_wsi_logical_close_uv, + /* check_client_connect_ok */ elops_check_client_connect_ok_uv, + /* close_handle_manually */ elops_close_handle_manually_uv, + /* accept */ elops_accept_uv, + /* io */ elops_io_uv, + /* run_pt */ elops_run_pt_uv, + /* destroy_pt */ elops_destroy_pt_uv, + /* destroy wsi */ NULL, + + /* periodic_events_available */ 0, +}; diff --git a/lib/event-libs/libuv/private.h b/lib/event-libs/libuv/private.h index e2dc5375..5ea25183 100644 --- a/lib/event-libs/libuv/private.h +++ b/lib/event-libs/libuv/private.h @@ -18,7 +18,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301 USA * - * This is included from private-libwebsockets.h if LWS_ROLE_WS + * This is included from private-libwebsockets.h if LWS_WITH_LIBUV */ #include @@ -49,7 +49,6 @@ struct lws_pt_eventlibs_libuv { }; struct lws_context_eventlibs_libuv { - uv_signal_cb sigint_cb; uv_loop_t pu_loop; }; @@ -61,17 +60,11 @@ struct lws_signal_watcher_libuv { uv_signal_t watcher; }; +#define LWS_LIBUV_ENABLED(context) lws_check_opt(context->options, \ + LWS_SERVER_OPTION_LIBUV) + +extern struct lws_event_loop_ops event_loop_ops_uv; + LWS_EXTERN void -lws_libuv_accept(struct lws *new_wsi, lws_sock_file_fd_type desc); -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); -LWS_EXTERN int -lws_uv_initvhost(struct lws_vhost* vh, struct lws*); -#define LWS_LIBUV_ENABLED(context) lws_check_opt(context->options, LWS_SERVER_OPTION_LIBUV) -LWS_EXTERN void lws_feature_status_libuv(const struct lws_context_creation_info *info); +lws_feature_status_libuv(const struct lws_context_creation_info *info); + diff --git a/lib/event-libs/poll/poll.c b/lib/event-libs/poll/poll.c new file mode 100644 index 00000000..ad6b29b0 --- /dev/null +++ b/lib/event-libs/poll/poll.c @@ -0,0 +1,43 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2018 Andy Green + * + * 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 + * + * This is included from private-libwebsockets.h if LWS_ROLE_WS + */ + +#include + +struct lws_event_loop_ops event_loop_ops_poll = { + /* name */ "poll", + /* init_context */ NULL, + /* destroy_context1 */ NULL, + /* destroy_context2 */ NULL, + /* init_vhost_listen_wsi */ NULL, + /* init_pt */ NULL, + /* wsi_logical_close */ NULL, + /* check_client_connect_ok */ NULL, + /* close_handle_manually */ NULL, + /* accept */ NULL, + /* io */ NULL, + /* run */ NULL, + /* destroy_pt */ NULL, + /* destroy wsi */ NULL, + + /* periodic_events_available */ 1, +}; \ No newline at end of file diff --git a/lib/event-libs/poll/private.h b/lib/event-libs/poll/private.h new file mode 100644 index 00000000..2cd65b8d --- /dev/null +++ b/lib/event-libs/poll/private.h @@ -0,0 +1,23 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2018 Andy Green + * + * 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 + * + */ + +extern struct lws_event_loop_ops event_loop_ops_poll; \ No newline at end of file diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c index 47d47c67..4cbd4d0d 100644 --- a/lib/libwebsockets.c +++ b/lib/libwebsockets.c @@ -152,7 +152,8 @@ __lws_free_wsi(struct lws *wsi) #endif __lws_remove_from_timeout_list(wsi); - lws_libevent_destroy(wsi); + if (wsi->context->event_loop_ops->destroy_wsi) + wsi->context->event_loop_ops->destroy_wsi(wsi); wsi->context->count_wsi_allocated--; lwsl_debug("%s: %p, remaining wsi %d\n", __func__, wsi, @@ -796,11 +797,11 @@ just_kill_connection: if (!wsi->socket_is_permanently_unusable && lws_sockfd_valid(wsi->desc.sockfd) && lwsi_state(wsi) != LRS_SHUTDOWN && - !LWS_LIBUV_ENABLED(context)) { + context->event_loop_ops->periodic_events_available) { __lws_change_pollfd(wsi, LWS_POLLOUT, LWS_POLLIN); lwsi_set_state(wsi, LRS_SHUTDOWN); __lws_set_timeout(wsi, PENDING_TIMEOUT_SHUTDOWN_FLUSH, - context->timeout_secs); + context->timeout_secs); return; } @@ -860,24 +861,9 @@ just_kill_connection: async_close: wsi->socket_is_permanently_unusable = 1; -#ifdef LWS_WITH_LIBUV - if (!wsi->parent_carries_io && lws_sockfd_valid(wsi->desc.sockfd)) - if (LWS_LIBUV_ENABLED(context)) { - if (wsi->listener) { - lwsl_debug("%s: stop listener poll\n", __func__); - uv_poll_stop(&wsi->w_read.uv.watcher); - } - lwsl_debug("%s: lws_libuv_closehandle: wsi %p\n", - __func__, wsi); - /* - * libuv has to do his own close handle processing - * asynchronously - */ - lws_libuv_closehandle(wsi); - + if (wsi->context->event_loop_ops->wsi_logical_close) + if (wsi->context->event_loop_ops->wsi_logical_close(wsi)) return; - } -#endif __lws_close_free_wsi_final(wsi); } diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h index 405ec913..848b3f81 100644 --- a/lib/libwebsockets.h +++ b/lib/libwebsockets.h @@ -855,37 +855,6 @@ struct lws_context; /* needed even with extensions disabled for create context */ struct lws_extension; -/*! \defgroup lwsmeta lws-meta - * - * ##lws-meta protocol - * - * The protocol wraps other muxed connections inside one tcp connection. - * - * Commands are assigned from 0x41 up (so they are valid unicode) - */ -///@{ - -enum lws_meta_commands { - LWS_META_CMD_OPEN_SUBCHANNEL = 'A', - /**< Client requests to open new subchannel - */ - LWS_META_CMD_OPEN_RESULT, - /**< Result of client request to open new subchannel */ - LWS_META_CMD_CLOSE_NOTIFY, - /**< Notification of subchannel closure */ - LWS_META_CMD_CLOSE_RQ, - /**< client requests to close a subchannel */ - LWS_META_CMD_WRITE, - /**< connection writes something to specific channel index */ - - /****** add new things just above ---^ ******/ -}; - -/* channel numbers are transported offset by 0x20 so they are valid unicode */ - -#define LWS_META_TRANSPORT_OFFSET 0x20 - -///@} /*! \defgroup usercb User Callback * @@ -2990,6 +2959,26 @@ struct lws_context_creation_info { * VHOST: If non-NULL, per-vhost list of advertised alpn, comma- * separated */ + void **foreign_loops; + /**< CONTEXT: This is ignored if the context is not being started with + * an event loop, ie, .options has a flag like + * LWS_SERVER_OPTION_LIBUV. + * + * NULL indicates lws should start its own even loop for + * each service thread, and deal with closing the loops + * when the context is destroyed. + * + * Non-NULL means it points to an array of external + * ("foreign") event loops that are to be used in turn for + * each service thread. In the default case of 1 service + * thread, it can just point to one foreign event loop. + */ + void (*signal_cb)(void *event_lib_handle, int signum); + /**< CONTEXT: NULL: default signal handling. Otherwise this receives + * the signal handler callback. event_lib_handle is the + * native event library signal handle, eg uv_signal_t * + * for libuv. + */ /* Add new things just above here ---^ * This is part of the ABI, don't needlessly break compatibility @@ -2998,6 +2987,12 @@ struct lws_context_creation_info { * members added above will see 0 (default) even if the app * was not built against the newer headers. */ + struct lws_context **pcontext; + /**< CONTEXT: if non-NULL, at the end of context destroy processing, + * the pointer pointed to by pcontext is written with NULL. You can + * use this to let foreign event loops know that lws context destruction + * is fully completed. + */ void *_unused[4]; /**< dummy */ }; @@ -3039,6 +3034,7 @@ struct lws_context_creation_info { LWS_VISIBLE LWS_EXTERN struct lws_context * lws_create_context(const struct lws_context_creation_info *info); + /** * lws_context_destroy() - Destroy the websocket context * \param context: Websocket context @@ -3050,9 +3046,6 @@ lws_create_context(const struct lws_context_creation_info *info); LWS_VISIBLE LWS_EXTERN void lws_context_destroy(struct lws_context *context); -LWS_VISIBLE LWS_EXTERN void -lws_context_destroy2(struct lws_context *context); - typedef int (*lws_reload_func)(void); /** @@ -4591,30 +4584,6 @@ LWS_VISIBLE LWS_EXTERN int lws_plat_recommended_rsa_bits(void); ///@} -/*! \defgroup ev libev helpers - * - * ##libev helpers - * - * APIs specific to libev event loop itegration - */ -///@{ - -#if defined(LWS_WITH_LIBEV) -typedef void (lws_ev_signal_cb_t)(EV_P_ struct ev_signal *w, int revents); - -LWS_VISIBLE LWS_EXTERN int -lws_ev_sigint_cfg(struct lws_context *context, int use_event_loop_sigint, - lws_ev_signal_cb_t *cb); - -LWS_VISIBLE LWS_EXTERN int -lws_ev_initloop(struct lws_context *context, struct ev_loop *loop, int tsi); - -LWS_VISIBLE LWS_EXTERN void -lws_ev_sigint_cb(struct ev_loop *loop, struct ev_signal *watcher, int revents); -#endif /* LWS_WITH_LIBEV */ - -///@} - /*! \defgroup uv libuv helpers * * ##libuv helpers @@ -4623,33 +4592,8 @@ lws_ev_sigint_cb(struct ev_loop *loop, struct ev_signal *watcher, int revents); */ ///@{ #ifdef LWS_WITH_LIBUV -LWS_VISIBLE LWS_EXTERN int -lws_uv_sigint_cfg(struct lws_context *context, int use_uv_sigint, - uv_signal_cb cb); - -LWS_VISIBLE LWS_EXTERN void -lws_libuv_run(const struct lws_context *context, int tsi); - -LWS_VISIBLE LWS_EXTERN void -lws_libuv_stop(struct lws_context *context); - -LWS_VISIBLE LWS_EXTERN void -lws_libuv_stop_without_kill(const struct lws_context *context, int tsi); - -LWS_VISIBLE LWS_EXTERN int -lws_uv_initloop(struct lws_context *context, uv_loop_t *loop, int tsi); - -LWS_VISIBLE LWS_EXTERN uv_loop_t * -lws_uv_getloop(struct lws_context *context, int tsi); - -LWS_VISIBLE LWS_EXTERN void -lws_uv_sigint_cb(uv_signal_t *watcher, int signum); - -LWS_VISIBLE LWS_EXTERN void -lws_close_all_handles_in_loop(uv_loop_t *loop); - /* - * Any direct libuv allocations in protocol handlers must participate in the + * Any direct libuv allocations in lws protocol handlers must participate in the * lws reference counting scheme. Two apis are provided: * * - lws_libuv_static_refcount_add(handle, context) to mark the handle with @@ -4658,12 +4602,16 @@ lws_close_all_handles_in_loop(uv_loop_t *loop); * - lws_libuv_static_refcount_del() which should be used as the close callback * for your own libuv objects declared in the protocol scope. * - * See the dumb increment plugin for an example of how to use them. - * * Using the apis allows lws to detach itself from a libuv loop completely * cleanly and at the moment all of its libuv objects have completed close. */ +LWS_VISIBLE uv_loop_t * +lws_uv_getloop(struct lws_context *context, int tsi); + +LWS_VISIBLE LWS_EXTERN void +lws_libuv_stop(struct lws_context *context); + LWS_VISIBLE LWS_EXTERN void lws_libuv_static_refcount_add(uv_handle_t *, struct lws_context *context); @@ -4678,32 +4626,6 @@ lws_libuv_static_refcount_del(uv_handle_t *); #endif ///@} -/*! \defgroup event libevent helpers - * - * ##libevent helpers - * - * APIs specific to libevent event loop itegration - */ -///@{ - -#if defined(LWS_WITH_LIBEVENT) && !defined(LWS_HIDE_LIBEVENT) -typedef void (lws_event_signal_cb_t) (evutil_socket_t sock_fd, short revents, - void *ctx); - -LWS_VISIBLE LWS_EXTERN int -lws_event_sigint_cfg(struct lws_context *context, int use_event_sigint, - lws_event_signal_cb_t cb); - -LWS_VISIBLE LWS_EXTERN int -lws_event_initloop(struct lws_context *context, struct event_base *loop, - int tsi); - -LWS_VISIBLE LWS_EXTERN void -lws_event_sigint_cb(evutil_socket_t sock_fd, short revents, - void *ctx); -#endif /* LWS_WITH_LIBEVENT */ - -///@} /*! \defgroup timeout Connection timeouts diff --git a/lib/plat/lws-plat-unix.c b/lib/plat/lws-plat-unix.c index fe80c8e3..80b43b1b 100644 --- a/lib/plat/lws-plat-unix.c +++ b/lib/plat/lws-plat-unix.c @@ -176,9 +176,8 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi) if (timeout_ms < 0) goto faked_service; - lws_libev_run(context, tsi); - lws_libuv_run(context, tsi); - lws_libevent_run(context, tsi); + if (context->event_loop_ops->run_pt) + context->event_loop_ops->run_pt(context, tsi); if (!context->service_tid_detected) { struct lws _lws; @@ -738,9 +737,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); - lws_libevent_io(wsi, LWS_EV_START | LWS_EV_READ); + if (context->event_loop_ops->io) + context->event_loop_ops->io(wsi, LWS_EV_START | LWS_EV_READ); pt->fds[pt->fds_count++].revents = 0; } @@ -751,9 +749,9 @@ lws_plat_delete_socket_from_fds(struct lws_context *context, { struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; - lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE); - lws_libuv_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE); - lws_libevent_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE); + if (context->event_loop_ops->io) + context->event_loop_ops->io(wsi, + LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE); pt->fds_count--; } @@ -915,10 +913,6 @@ lws_plat_init(struct lws_context *context, return 1; } - (void)lws_libev_init_fd_table(context); - (void)lws_libuv_init_fd_table(context); - (void)lws_libevent_init_fd_table(context); - #ifdef LWS_WITH_PLUGINS if (info->plugin_dirs) lws_plat_plugins_init(context, info->plugin_dirs); diff --git a/lib/pollfd.c b/lib/pollfd.c index 901167b7..6a06d51f 100644 --- a/lib/pollfd.c +++ b/lib/pollfd.c @@ -144,25 +144,22 @@ _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); - lws_libevent_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); - lws_libevent_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); - lws_libevent_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); - lws_libevent_io(wsi, LWS_EV_START | LWS_EV_WRITE); + if (context->event_loop_ops->io) { + if (_and & LWS_POLLIN) + context->event_loop_ops->io(wsi, + LWS_EV_STOP | LWS_EV_READ); + + if (_or & LWS_POLLIN) + context->event_loop_ops->io(wsi, + LWS_EV_START | LWS_EV_READ); + + if (_and & LWS_POLLOUT) + context->event_loop_ops->io(wsi, + LWS_EV_STOP | LWS_EV_WRITE); + + if (_or & LWS_POLLOUT) + context->event_loop_ops->io(wsi, + LWS_EV_START | LWS_EV_WRITE); } /* @@ -319,10 +316,10 @@ __remove_wsi_socket_from_fds(struct lws *wsi) /* the guy who is to be deleted's slot index in pt->fds */ m = wsi->position_in_fds_table; - 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); + if (context->event_loop_ops->io) + context->event_loop_ops->io(wsi, + LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE | + LWS_EV_PREPARE_DELETION); lwsl_debug("%s: wsi=%p, sock=%d, fds pos=%d, end guy pos=%d, endfd=%d\n", __func__, wsi, wsi->desc.sockfd, wsi->position_in_fds_table, diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h index 827bc388..5a72047b 100644 --- a/lib/private-libwebsockets.h +++ b/lib/private-libwebsockets.h @@ -199,16 +199,6 @@ #include "libwebsockets.h" -#if defined(LWS_WITH_LIBEV) -#include "event-libs/libev/private.h" -#endif -#ifdef LWS_WITH_LIBUV - #include "event-libs/libuv/private.h" -#endif -#if defined(LWS_WITH_LIBEVENT) && !defined(LWS_HIDE_LIBEVENT) -#include "event-libs/libevent/private.h" -#endif - #if defined(LWS_WITH_TLS) #include "tls/private.h" #endif @@ -382,6 +372,12 @@ enum lws_ssl_capable_status { #define lws_memory_barrier() #endif +/* + * + * ------ role ------ + * + */ + typedef uint32_t lws_wsi_state_t; /* @@ -579,6 +575,8 @@ struct lws_role_ops { * (just client applies if no concept of client or server) */ uint16_t close_cb[2]; + + unsigned int file_handle:1; /* role operates on files not sockets */ }; /* null-terminated array of pointers to roles lws built with */ @@ -641,6 +639,60 @@ enum { LWS_UPG_RET_BAIL }; +/* + * + * ------ event_loop ops ------ + * + */ + +struct lws_event_loop_ops { + const char *name; + /* event loop-specific context init during context creation */ + int (*init_context)(struct lws_context *context, + const struct lws_context_creation_info *info); + /* called during lws_destroy_context */ + int (*destroy_context1)(struct lws_context *context); + /* called during lws_destroy_context2 */ + int (*destroy_context2)(struct lws_context *context); + /* init vhost listening wsi */ + int (*init_vhost_listen_wsi)(struct lws *wsi); + /* init the event loop for a pt */ + int (*init_pt)(struct lws_context *context, void *_loop, int tsi); + /* called at end of first phase of close_free_wsi() */ + int (*wsi_logical_close)(struct lws *wsi); + /* return nonzero if client connect not allowed */ + int (*check_client_connect_ok)(struct lws *wsi); + /* close handle manually */ + void (*close_handle_manually)(struct lws *wsi); + /* event loop accept processing */ + void (*accept)(struct lws *wsi); + /* control wsi active events */ + void (*io)(struct lws *wsi, int flags); + /* run the event loop for a pt */ + void (*run_pt)(struct lws_context *context, int tsi); + /* called before pt is destroyed */ + void (*destroy_pt)(struct lws_context *context, int tsi); + /* called just before wsi is freed */ + void (*destroy_wsi)(struct lws *wsi); + + unsigned int periodic_events_available:1; +}; + +#if defined(LWS_WITH_POLL) +#include "event-libs/poll/private.h" +#endif +#if defined(LWS_WITH_LIBEV) +#include "event-libs/libev/private.h" +#endif +#if defined(LWS_WITH_LIBUV) +#include "event-libs/libuv/private.h" +#endif +#if defined(LWS_WITH_LIBEVENT) && !defined(LWS_HIDE_LIBEVENT) +#include "event-libs/libevent/private.h" +#endif + + + /* enums of socks version */ enum socks_version { SOCKS_VERSION_4 = 4, @@ -1031,6 +1083,7 @@ struct lws_context { time_t time_fixup; const struct lws_plat_file_ops *fops; struct lws_plat_file_ops fops_platform; + struct lws_context **pcontext_finalize; #if defined(LWS_WITH_HTTP2) struct http2_settings set; #endif @@ -1067,6 +1120,7 @@ struct lws_context { const struct lws_protocol_vhost_options *reject_service_keywords; const char *alpn_default; lws_reload_func deprecation_cb; + void (*eventlib_signal_cb)(void *event_lib_handle, int signum); #if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP) cap_value_t caps[4]; @@ -1082,6 +1136,9 @@ struct lws_context { #if defined(LWS_WITH_LIBEVENT) struct lws_context_eventlibs_libevent event; #endif + struct lws_event_loop_ops *event_loop_ops; + + char canonical_hostname[128]; #ifdef LWS_LATENCY unsigned long worst_latency; @@ -1099,10 +1156,7 @@ struct lws_context { #endif int max_fds; -#if defined(LWS_WITH_LIBEV) || defined(LWS_WITH_LIBUV) || defined(LWS_WITH_LIBEVENT) - int use_event_loop_sigint; int count_event_loop_static_asset_handles; -#endif int started_with_parent; int uid, gid; @@ -1129,8 +1183,9 @@ struct lws_context { unsigned int requested_kill:1; unsigned int protocol_init_done:1; unsigned int ssl_gate_accepts:1; - unsigned int doing_protocol_init; - unsigned int done_protocol_destroy_cb; + unsigned int doing_protocol_init:1; + unsigned int done_protocol_destroy_cb:1; + unsigned int finalize_destroy_after_internal_loops_stopped:1; /* * set to the Thread ID that's doing the service loop just before entry * to poll indicates service thread likely idling in poll() @@ -1161,8 +1216,6 @@ LWS_EXTERN void __lws_close_free_wsi_final(struct lws *wsi); LWS_EXTERN void lws_libuv_closehandle(struct lws *wsi); -LWS_EXTERN void -lws_libuv_closehandle_manually(struct lws *wsi); LWS_EXTERN int lws_libuv_check_watcher_active(struct lws *wsi); @@ -1194,11 +1247,6 @@ enum { }; #if !defined(LWS_WITH_LIBEV) -#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) #if !defined(LWS_WITH_ESP32) #define lws_feature_status_libev(_a) \ @@ -1209,11 +1257,6 @@ enum { #endif #if !defined(LWS_WITH_LIBUV) -#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 !defined(LWS_WITH_ESP32) #define lws_feature_status_libuv(_a) \ @@ -1224,11 +1267,6 @@ enum { #endif #if !defined(LWS_WITH_LIBEVENT) -#define lws_libevent_accept(_a, _b) ((void) 0) -#define lws_libevent_destroy(_a) ((void) 0) -#define lws_libevent_io(_a, _b) ((void) 0) -#define lws_libevent_init_fd_table(_a) (0) -#define lws_libevent_run(_a, _b) ((void) 0) #define lws_libevent_destroyloop(_a, _b) ((void) 0) #define LWS_LIBEVENT_ENABLED(context) (0) #if !defined(LWS_WITH_ESP32) @@ -1675,7 +1713,7 @@ LWS_EXTERN int lws_change_pollfd(struct lws *wsi, int _and, int _or); #ifndef LWS_NO_SERVER - int _lws_context_init_server(const struct lws_context_creation_info *info, + int _lws_vhost_init_server(const struct lws_context_creation_info *info, struct lws_vhost *vhost); LWS_EXTERN struct lws_vhost * lws_select_vhost(struct lws_context *context, int port, const char *servername); @@ -1685,7 +1723,7 @@ lws_change_pollfd(struct lws *wsi, int _and, int _or); lws_server_get_canonical_hostname(struct lws_context *context, const struct lws_context_creation_info *info); #else - #define _lws_context_init_server(_a, _b) (0) + #define _lws_vhost_init_server(_a, _b) (0) #define lws_parse_ws(_a, _b, _c) (0) #define lws_server_get_canonical_hostname(_a, _b) #endif @@ -2045,6 +2083,10 @@ lws_tls_server_conn_alpn(struct lws *wsi); int lws_ws_client_rx_sm_block(struct lws *wsi, unsigned char **buf, size_t len); +void +lws_destroy_event_pipe(struct lws *wsi); +void +lws_context_destroy2(struct lws_context *context); #ifdef __cplusplus }; diff --git a/lib/roles/cgi/cgi-server.c b/lib/roles/cgi/cgi-server.c index 2de5a875..4fc8305b 100644 --- a/lib/roles/cgi/cgi-server.c +++ b/lib/roles/cgi/cgi-server.c @@ -163,7 +163,9 @@ lws_cgi(struct lws *wsi, const char * const *exec_array, int script_uri_path_len } for (n = 0; n < 3; n++) { - lws_libuv_accept(cgi->stdwsi[n], cgi->stdwsi[n]->desc); + if (wsi->context->event_loop_ops->accept) + wsi->context->event_loop_ops->accept(cgi->stdwsi[n]); + if (__insert_wsi_socket_into_fds(wsi->context, cgi->stdwsi[n])) goto bail3; cgi->stdwsi[n]->parent = wsi; diff --git a/lib/roles/cgi/ops-cgi.c b/lib/roles/cgi/ops-cgi.c index 546d2948..bc42c10e 100644 --- a/lib/roles/cgi/ops-cgi.c +++ b/lib/roles/cgi/ops-cgi.c @@ -98,4 +98,5 @@ struct lws_role_ops role_ops_cgi = { /* destroy_role */ NULL, /* writeable cb clnt, srv */ { 0, 0 }, /* close cb clnt, srv */ { 0, 0 }, + /* file_handle */ 0, }; diff --git a/lib/roles/h1/ops-h1.c b/lib/roles/h1/ops-h1.c index a5611f8e..0cde99d5 100644 --- a/lib/roles/h1/ops-h1.c +++ b/lib/roles/h1/ops-h1.c @@ -635,4 +635,5 @@ struct lws_role_ops role_ops_h1 = { LWS_CALLBACK_HTTP_WRITEABLE }, /* close cb clnt, srv */ { LWS_CALLBACK_CLOSED_CLIENT_HTTP, LWS_CALLBACK_CLOSED_HTTP }, + /* file_handle */ 0, }; diff --git a/lib/roles/h2/ops-h2.c b/lib/roles/h2/ops-h2.c index 93c5b7fe..f10f7c93 100644 --- a/lib/roles/h2/ops-h2.c +++ b/lib/roles/h2/ops-h2.c @@ -1052,4 +1052,5 @@ struct lws_role_ops role_ops_h2 = { LWS_CALLBACK_HTTP_WRITEABLE }, /* close cb clnt, srv */ { LWS_CALLBACK_CLOSED_CLIENT_HTTP, LWS_CALLBACK_CLOSED_HTTP }, + /* file_handle */ 0, }; diff --git a/lib/roles/http/client/client-handshake.c b/lib/roles/http/client/client-handshake.c index 7edf3cce..6455a1c0 100644 --- a/lib/roles/http/client/client-handshake.c +++ b/lib/roles/http/client/client-handshake.c @@ -338,14 +338,11 @@ create_new_conn: if (!lws_socket_is_valid(wsi->desc.sockfd)) { -#if defined(LWS_WITH_LIBUV) - if (LWS_LIBUV_ENABLED(wsi->context)) - if (lws_libuv_check_watcher_active(wsi)) { - lwsl_warn("Waiting for libuv watcher to close\n"); - cce = "waiting for libuv watcher to close"; - goto oom4; - } -#endif + if (wsi->context->event_loop_ops->check_client_connect_ok && + wsi->context->event_loop_ops->check_client_connect_ok(wsi)) { + cce = "waiting for event loop watcher to close"; + goto oom4; + } #ifdef LWS_WITH_IPV6 if (wsi->ipv6) @@ -369,9 +366,8 @@ create_new_conn: lwsi_set_state(wsi, LRS_WAITING_CONNECT); - lws_libev_accept(wsi, wsi->desc); - lws_libuv_accept(wsi, wsi->desc); - lws_libevent_accept(wsi, wsi->desc); + if (wsi->context->event_loop_ops->accept) + wsi->context->event_loop_ops->accept(wsi); if (__insert_wsi_socket_into_fds(wsi->context, wsi)) { compatible_close(wsi->desc.sockfd); @@ -650,21 +646,10 @@ lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port, lws_ssl_close(wsi); #endif -#ifdef LWS_WITH_LIBUV - if (LWS_LIBUV_ENABLED(wsi->context)) { - lwsl_debug("%s: lws_libuv_closehandle: wsi %p\n", __func__, wsi); - /* - * libuv has to do his own close handle processing asynchronously - * but once it starts we can do everything else synchronously, - * including trash wsi->desc.sockfd since it took a copy. - * - * When it completes it will call compatible_close() - */ - lws_libuv_closehandle_manually(wsi); - } else -#else - compatible_close(wsi->desc.sockfd); -#endif + if (wsi->context->event_loop_ops->close_handle_manually) + wsi->context->event_loop_ops->close_handle_manually(wsi); + else + compatible_close(wsi->desc.sockfd); __remove_wsi_socket_from_fds(wsi); diff --git a/lib/roles/http/server/server.c b/lib/roles/http/server/server.c index 689ae29e..2f2224f5 100644 --- a/lib/roles/http/server/server.c +++ b/lib/roles/http/server/server.c @@ -37,7 +37,7 @@ const char * const method_names[] = { */ int -_lws_context_init_server(const struct lws_context_creation_info *info, +_lws_vhost_init_server(const struct lws_context_creation_info *info, struct lws_vhost *vhost) { int n, opt = 1, limit = 1; @@ -240,10 +240,8 @@ done_list: wsi->vhost = vhost; wsi->listener = 1; -#ifdef LWS_WITH_LIBUV - if (LWS_LIBUV_ENABLED(vhost->context)) - lws_uv_initvhost(vhost, wsi); -#endif + if (wsi->context->event_loop_ops->init_vhost_listen_wsi) + wsi->context->event_loop_ops->init_vhost_listen_wsi(wsi); if (__insert_wsi_socket_into_fds(vhost->context, wsi)) { lwsl_notice("inserting wsi socket into fds failed\n"); @@ -1353,7 +1351,8 @@ deal_body: if (m < 0) return -1; - lws_buflist_aware_consume(wsi, &ebuf, m, 1); + if (lws_buflist_aware_consume(wsi, &ebuf, m, 1)) + return -1; } } } @@ -1988,9 +1987,8 @@ lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type, lwsl_debug("new wsi wsistate 0x%x\n", new_wsi->wsistate); - lws_libev_accept(new_wsi, new_wsi->desc); - lws_libuv_accept(new_wsi, new_wsi->desc); - lws_libevent_accept(new_wsi, new_wsi->desc); + if (context->event_loop_ops->accept) + context->event_loop_ops->accept(new_wsi); if (!ssl) { lws_pt_lock(pt, __func__); diff --git a/lib/roles/listen/ops-listen.c b/lib/roles/listen/ops-listen.c index cea1494f..690476de 100644 --- a/lib/roles/listen/ops-listen.c +++ b/lib/roles/listen/ops-listen.c @@ -172,4 +172,5 @@ struct lws_role_ops role_ops_listen = { /* destroy_role */ NULL, /* writeable cb clnt, srv */ { 0, 0 }, /* close cb clnt, srv */ { 0, 0 }, + /* file_handle */ 0, }; diff --git a/lib/roles/pipe/ops-pipe.c b/lib/roles/pipe/ops-pipe.c index 13673444..4fd49027 100644 --- a/lib/roles/pipe/ops-pipe.c +++ b/lib/roles/pipe/ops-pipe.c @@ -77,4 +77,5 @@ struct lws_role_ops role_ops_pipe = { /* destroy_role */ NULL, /* writeable cb clnt, srv */ { 0, 0 }, /* close cb clnt, srv */ { 0, 0 }, + /* file_handle */ 1, }; diff --git a/lib/roles/raw/ops-raw.c b/lib/roles/raw/ops-raw.c index 24006fed..f59bdab6 100644 --- a/lib/roles/raw/ops-raw.c +++ b/lib/roles/raw/ops-raw.c @@ -186,6 +186,7 @@ struct lws_role_ops role_ops_raw_skt = { /* destroy_role */ NULL, /* writeable cb clnt, srv */ { LWS_CALLBACK_RAW_WRITEABLE, 0 }, /* close cb clnt, srv */ { LWS_CALLBACK_RAW_CLOSE, 0 }, + /* file_handle */ 0, }; @@ -213,4 +214,5 @@ struct lws_role_ops role_ops_raw_file = { /* destroy_role */ NULL, /* writeable cb clnt, srv */ { LWS_CALLBACK_RAW_WRITEABLE_FILE, 0 }, /* close cb clnt, srv */ { LWS_CALLBACK_RAW_CLOSE_FILE, 0 }, + /* file_handle */ 1, }; diff --git a/lib/service.c b/lib/service.c index 13ce9d12..d9b82ab2 100644 --- a/lib/service.c +++ b/lib/service.c @@ -750,7 +750,7 @@ lws_service_periodic_checks(struct lws_context *context, context->no_listener_vhost_list) { struct lws_vhost *v = *pv; lwsl_debug("deferred iface: checking if on vh %s\n", (*pv)->name); - if (_lws_context_init_server(NULL, *pv) == 0) { + if (_lws_vhost_init_server(NULL, *pv) == 0) { /* became happy */ lwsl_notice("vh %s: became connected\n", v->name); *pv = v->no_listener_vhost_list; @@ -899,12 +899,24 @@ lws_service_fd(struct lws_context *context, struct lws_pollfd *pollfd) LWS_VISIBLE int lws_service(struct lws_context *context, int timeout_ms) { + if (context->event_loop_ops->run_pt) { + /* we are configured for an event loop */ + context->event_loop_ops->run_pt(context, 0); + + return 1; + } return lws_plat_service(context, timeout_ms); } LWS_VISIBLE int lws_service_tsi(struct lws_context *context, int timeout_ms, int tsi) { + if (context->event_loop_ops->run_pt) { + /* we are configured for an event loop */ + context->event_loop_ops->run_pt(context, tsi); + + return 1; + } + return _lws_plat_service_tsi(context, timeout_ms, tsi); } - diff --git a/lwsws/main.c b/lwsws/main.c index dcc7b7fa..1a89f852 100644 --- a/lwsws/main.c +++ b/lwsws/main.c @@ -1,7 +1,7 @@ /* * libwebsockets web server application * - * Copyright (C) 2010-2017 Andy Green + * Copyright (C) 2010-2018 Andy Green * * This file is made available under the Creative Commons CC0 1.0 * Universal Public Domain Dedication. @@ -36,6 +36,7 @@ #else #include #include "gettimeofday.h" +#include int fork(void) { @@ -52,7 +53,7 @@ static struct lws_context *context; static char config_dir[128]; static int opts = 0, do_reload = 1; static uv_loop_t loop; -static uv_signal_t signal_outer; +static uv_signal_t signal_outer[2]; static int pids[32]; void lwsl_emit_stderr(int level, const char *line); @@ -101,7 +102,9 @@ void signal_cb(uv_signal_t *watcher, int signum) break; } lwsl_err("Signal %d caught\n", watcher->signum); - lws_libuv_stop(context); + uv_signal_stop(watcher); + uv_signal_stop(&signal_outer[1]); + lws_context_destroy(context); } static int @@ -110,6 +113,7 @@ context_creation(void) int cs_len = LWSWS_CONFIG_STRING_SIZE - 1; struct lws_context_creation_info info; char *cs, *config_strings; + void *foreign_loops[1]; cs = config_strings = malloc(LWSWS_CONFIG_STRING_SIZE); if (!config_strings) { @@ -120,7 +124,6 @@ context_creation(void) memset(&info, 0, sizeof(info)); info.external_baggage_free_on_destroy = config_strings; - info.max_http_header_pool = 1024; info.pt_serv_buf_size = 8192; info.options = opts | LWS_SERVER_OPTION_VALIDATE_UTF8 | LWS_SERVER_OPTION_EXPLICIT_VHOSTS | @@ -135,15 +138,16 @@ context_creation(void) if (lwsws_get_config_globals(&info, config_dir, &cs, &cs_len)) goto init_failed; + foreign_loops[0] = &loop; + info.foreign_loops = foreign_loops; + info.pcontext = &context; + context = lws_create_context(&info); if (context == NULL) { lwsl_err("libwebsocket init failed\n"); goto init_failed; } - lws_uv_sigint_cfg(context, 1, signal_cb); - lws_uv_initloop(context, &loop, 0); - /* * then create the vhosts... protocols are entirely coming from * plugins, so we leave it NULL @@ -151,8 +155,7 @@ context_creation(void) info.extensions = exts; - if (lwsws_get_config_vhosts(context, &info, config_dir, - &cs, &cs_len)) + if (lwsws_get_config_vhosts(context, &info, config_dir, &cs, &cs_len)) return 1; return 0; @@ -190,6 +193,8 @@ reload_handler(int signum) case SIGINT: case SIGTERM: case SIGKILL: + fprintf(stderr, "master process waiting 2s...\n"); + sleep(2); /* give children a chance to deal with the signal */ fprintf(stderr, "killing service processes\n"); for (m = 0; m < (int)ARRAY_SIZE(pids); m++) if (pids[m]) @@ -203,7 +208,7 @@ reload_handler(int signum) int main(int argc, char **argv) { - int n = 0, debug_level = 7; + int n = 0, budget = 100, debug_level = 7; #ifndef _WIN32 int m; int status, syslog_options = LOG_PID | LOG_PERROR; @@ -283,7 +288,7 @@ int main(int argc, char **argv) lws_set_log_level(debug_level, lwsl_emit_syslog); lwsl_notice("lwsws libwebsockets web server - license CC0 + LGPL2.1\n"); - lwsl_notice("(C) Copyright 2010-2016 Andy Green \n"); + lwsl_notice("(C) Copyright 2010-2018 Andy Green \n"); #if (UV_VERSION_MAJOR > 0) // Travis... uv_loop_init(&loop); @@ -291,30 +296,31 @@ int main(int argc, char **argv) fprintf(stderr, "Your libuv is too old!\n"); return 0; #endif - uv_signal_init(&loop, &signal_outer); - uv_signal_start(&signal_outer, signal_cb, SIGINT); - uv_signal_start(&signal_outer, signal_cb, SIGHUP); + uv_signal_init(&loop, &signal_outer[0]); + uv_signal_start(&signal_outer[0], signal_cb, SIGINT); + uv_signal_init(&loop, &signal_outer[1]); + uv_signal_start(&signal_outer[1], signal_cb, SIGHUP); if (context_creation()) { lwsl_err("Context creation failed\n"); return 1; } - lws_libuv_run(context, 0); + lws_service(context, 0); + + lwsl_err("%s: closing\n", __func__); + + for (n = 0; n < 2; n++) { + uv_signal_stop(&signal_outer[n]); + uv_close((uv_handle_t *)&signal_outer[n], NULL); + } - uv_signal_stop(&signal_outer); lws_context_destroy(context); -#if (UV_VERSION_MAJOR > 0) // Travis... - lws_close_all_handles_in_loop(&loop); - n = 0; - while (n++ < 4096 && uv_loop_close(&loop)) - uv_run(&loop, UV_RUN_NOWAIT); -#endif + while ((n = uv_loop_close(&loop)) && --budget) + uv_run(&loop, UV_RUN_ONCE); - lws_context_destroy2(context); - - fprintf(stderr, "lwsws exited cleanly\n"); + fprintf(stderr, "lwsws exited cleanly: %d\n", n); #ifndef _WIN32 closelog(); diff --git a/minimal-examples/http-server/minimal-http-server-libuv-foreign/README.md b/minimal-examples/http-server/minimal-http-server-libuv-foreign/README.md index 3b823d66..41cc9d93 100644 --- a/minimal-examples/http-server/minimal-http-server-libuv-foreign/README.md +++ b/minimal-examples/http-server/minimal-http-server-libuv-foreign/README.md @@ -3,7 +3,19 @@ This demonstrates having lws take part in a libuv loop owned by something else, with its own objects running in the loop. -Lws can join the loop, and clean up perfectly after itself. +Lws can join the loop, and clean up perfectly after itself without +leaving anything behind or making trouble in the larger loop, which +does not need to stop during lws creation or destruction. + +First the foreign loop is created with a 1s timer, and runs alone for 5s. + +Then the lws context is created inside the timer callback and runs for 10s... +during this period you can visit http://localhost:7681 for normal lws +service using the foreign loop. + +After the 10s are up, the lws context is destroyed inside the foreign loop +timer. The foreign loop runs alone again for a further 5s and then +exits itself. ## build diff --git a/minimal-examples/http-server/minimal-http-server-libuv-foreign/localhost-100y.cert b/minimal-examples/http-server/minimal-http-server-libuv-foreign/localhost-100y.cert new file mode 100644 index 00000000..6f372db4 --- /dev/null +++ b/minimal-examples/http-server/minimal-http-server-libuv-foreign/localhost-100y.cert @@ -0,0 +1,34 @@ +-----BEGIN CERTIFICATE----- +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 +e2Hl/7M0c/lbZ4FvXgARlex2rkgS0Ka06HE= +-----END CERTIFICATE----- diff --git a/minimal-examples/http-server/minimal-http-server-libuv-foreign/localhost-100y.key b/minimal-examples/http-server/minimal-http-server-libuv-foreign/localhost-100y.key new file mode 100644 index 00000000..148f8598 --- /dev/null +++ b/minimal-examples/http-server/minimal-http-server-libuv-foreign/localhost-100y.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +fBrpEY1IATtPq1taBZZogRqI3rOkkPk= +-----END PRIVATE KEY----- diff --git a/minimal-examples/http-server/minimal-http-server-libuv-foreign/minimal-http-server-libuv-foreign.c b/minimal-examples/http-server/minimal-http-server-libuv-foreign/minimal-http-server-libuv-foreign.c index b27763b5..7108133e 100644 --- a/minimal-examples/http-server/minimal-http-server-libuv-foreign/minimal-http-server-libuv-foreign.c +++ b/minimal-examples/http-server/minimal-http-server-libuv-foreign/minimal-http-server-libuv-foreign.c @@ -20,6 +20,17 @@ #include static struct lws_context *context; +static uv_loop_t loop; +static int lifetime = 5, reported; +struct lws_context_creation_info info; + +enum { + TEST_STATE_CREATE_LWS_CONTEXT, + TEST_STATE_DESTROY_LWS_CONTEXT, + TEST_STATE_EXIT +}; + +static int sequence = TEST_STATE_CREATE_LWS_CONTEXT; static const struct lws_http_mount mount = { /* .mount_next */ NULL, /* linked-list "next" */ @@ -54,32 +65,66 @@ void signal_cb(uv_signal_t *watcher, int signum) abort(); break; } - lws_libuv_stop(context); + + if (context) + lws_context_destroy(context); } /* this logs once a second to show that the foreign loop assets are working */ -static void timer_cb(uv_timer_t *t) +static void +timer_cb(uv_timer_t *t) { + void *foreign_loops[1]; + + foreign_loops[0] = &loop; + info.foreign_loops = foreign_loops; + lwsl_user("Foreign 1Hz timer\n"); + + if (sequence == TEST_STATE_EXIT && !context && !reported) { + /* + * at this point the lws_context_destroy() we did earlier + * has completed and the entire context is wholly destroyed + */ + lwsl_user("lws_destroy_context() completed, continuing for 5s\n"); + reported = 1; + } + + if (--lifetime) + return; + + switch (sequence++) { + case TEST_STATE_CREATE_LWS_CONTEXT: + context = lws_create_context(&info); + if (!context) { + lwsl_err("lws init failed\n"); + return; + } + lwsl_user("LWS Context created and active for 10s\n"); + lifetime = 11; + break; + case TEST_STATE_DESTROY_LWS_CONTEXT: + /* cleanup the lws part */ + lwsl_user("Destroying lws context and continuing loop for 5s\n"); + lws_context_destroy(context); + lifetime = 6; + break; + + case TEST_STATE_EXIT: + lwsl_user("Deciding to exit foreign loop too\n"); + uv_stop(&loop); + break; + default: + break; + } } -static void lws_uv_close_cb(uv_handle_t *handle) -{ -} - -static void lws_uv_walk_cb(uv_handle_t *handle, void *arg) -{ - lwsl_info("%s: closing foreign loop asset: %p (type %d)\n", - __func__, handle, handle->type); - uv_close(handle, lws_uv_close_cb); -} int main(int argc, const char **argv) { - struct lws_context_creation_info info; uv_timer_t timer_outer; - uv_loop_t loop; + uv_signal_t sighandler; const char *p; int logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE /* for LLL_ verbosity above NOTICE to be built into lws, @@ -96,61 +141,58 @@ int main(int argc, const char **argv) lwsl_user("LWS minimal http server libuv + foreign loop |" " visit http://localhost:7681\n"); + /* + * We prepare the info here, but don't use it until later in the + * timer callback, to demonstrate the independence of the foreign loop + * and lws. + */ + memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ info.port = 7681; info.mounts = &mount; info.error_document_404 = "/404.html"; info.options = LWS_SERVER_OPTION_LIBUV; + info.pcontext = &context; + if (lws_cmdline_option(argc, argv, "-s")) { + info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; + info.ssl_cert_filepath = "localhost-100y.cert"; + info.ssl_private_key_filepath = "localhost-100y.key"; + } + info.pcontext = &context; + + lwsl_user(" This app creates a uv loop with a timer + signalhandler, and\n"); + lwsl_user(" performs a test in three phases:\n"); + lwsl_user("\n"); + lwsl_user(" 1) 5s: Runs the loop with just the timer\n"); + lwsl_user(" 2) 10s: create an lws context serving on localhost:7681\n"); + lwsl_user(" using the same uv loop. Destroy it after 10s.\n"); + lwsl_user(" 3) 5s: Run the loop again with just the timer\n"); + lwsl_user("\n"); + lwsl_user(" Finally close only the timer and signalhandler and\n"); + lwsl_user(" exit the loop cleanly\n"); + + /* we create and start our "foreign loop" */ uv_loop_init(&loop); + uv_signal_init(&loop, &sighandler); + uv_signal_start(&sighandler, signal_cb, SIGINT); uv_timer_init(&loop, &timer_outer); uv_timer_start(&timer_outer, timer_cb, 0, 1000); - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - lws_uv_sigint_cfg(context, 1, signal_cb); - - if (lws_uv_initloop(context, &loop, 0)) { - lwsl_err("lws_uv_initloop failed\n"); - - goto bail; - } - - lws_libuv_run(context, 0); - -bail: - lwsl_user("%s: starting exit cleanup...\n", __func__); - - /* cleanup the lws part */ - - lws_context_destroy(context); - lws_context_destroy2(context); - - /* cleanup the foreign loop part */ - - lwsl_user("%s: lws context destroyed: cleaning the foreign loop\n", - __func__); - - /* - * Instead of walking to close all the foreign assets, it's also - * fine to close them individually instead as below - */ - // uv_timer_stop(&timer_outer); - // uv_close((uv_handle_t*)&timer_outer, NULL); - - /* close every foreign loop asset unconditionally */ - uv_walk(&loop, lws_uv_walk_cb, NULL); - - /* let it run until everything completed close */ uv_run(&loop, UV_RUN_DEFAULT); - /* nothing left in the foreign loop, destroy it */ + /* in the case we hit ^C while lws still exists */ + lws_context_destroy(context); + /* cleanup the foreign loop assets */ + + uv_timer_stop(&timer_outer); + uv_close((uv_handle_t*)&timer_outer, NULL); + uv_signal_stop(&sighandler); + uv_close((uv_handle_t *)&sighandler, NULL); + + uv_run(&loop, UV_RUN_DEFAULT); uv_loop_close(&loop); lwsl_user("%s: exiting...\n", __func__); diff --git a/minimal-examples/http-server/minimal-http-server-libuv/CMakeLists.txt b/minimal-examples/http-server/minimal-http-server-libuv/CMakeLists.txt index 1ced4952..d95e7533 100644 --- a/minimal-examples/http-server/minimal-http-server-libuv/CMakeLists.txt +++ b/minimal-examples/http-server/minimal-http-server-libuv/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 2.8) include(CheckCSourceCompiles) set(SAMP lws-minimal-http-server-libuv) -set(SRCS minimal-http-server.c) +set(SRCS minimal-http-server-libuv.c) # If we are being built as part of lws, confirm current build config supports # reqconfig, else skip building ourselves. @@ -76,4 +76,4 @@ if (requirements) else() target_link_libraries(${SAMP} websockets) endif() -endif() \ No newline at end of file +endif() diff --git a/minimal-examples/http-server/minimal-http-server-libuv/minimal-http-server.c b/minimal-examples/http-server/minimal-http-server-libuv/minimal-http-server-libuv.c similarity index 89% rename from minimal-examples/http-server/minimal-http-server-libuv/minimal-http-server.c rename to minimal-examples/http-server/minimal-http-server-libuv/minimal-http-server-libuv.c index e6e66185..9d73be13 100644 --- a/minimal-examples/http-server/minimal-http-server-libuv/minimal-http-server.c +++ b/minimal-examples/http-server/minimal-http-server-libuv/minimal-http-server-libuv.c @@ -6,7 +6,8 @@ * This file is made available under the Creative Commons CC0 1.0 * Universal Public Domain Dedication. * - * This demonstrates the most minimal http server you can make with lws. + * This demonstrates the most minimal http server you can make with lws using + * the libuv event loop. * * To keep it simple, it serves stuff from the subdirectory * "./mount-origin" of the directory it was started in. @@ -39,8 +40,10 @@ static const struct lws_http_mount mount = { /* .basic_auth_login_file */ NULL, }; -void signal_cb(uv_signal_t *watcher, int signum) +void signal_cb(void *handle, int signum) { + uv_signal_t *watcher = (uv_signal_t *)handle; + lwsl_notice("Signal %d caught, exiting...\n", watcher->signum); switch (watcher->signum) { @@ -52,7 +55,7 @@ void signal_cb(uv_signal_t *watcher, int signum) abort(); break; } - lws_libuv_stop(context); + lws_context_destroy(context); } int main(int argc, const char **argv) @@ -83,6 +86,7 @@ int main(int argc, const char **argv) info.ssl_private_key_filepath = "localhost-100y.key"; } info.options |= LWS_SERVER_OPTION_LIBUV; + info.signal_cb = signal_cb; context = lws_create_context(&info); if (!context) { @@ -90,19 +94,9 @@ int main(int argc, const char **argv) return 1; } - lws_uv_sigint_cfg(context, 1, signal_cb); + lws_service(context, 0); - if (lws_uv_initloop(context, NULL, 0)) { - lwsl_err("lws_uv_initloop failed\n"); - - goto bail; - } - - lws_libuv_run(context, 0); - -bail: lws_context_destroy(context); - lws_context_destroy2(context); return 0; } diff --git a/plugins/protocol_lws_server_status.c b/plugins/protocol_lws_server_status.c index 9619eb66..4ee1bf4f 100644 --- a/plugins/protocol_lws_server_status.c +++ b/plugins/protocol_lws_server_status.c @@ -52,10 +52,10 @@ struct per_session_data__server_status { }; struct per_vhost_data__lws_server_status { - uv_timer_t timeout_watcher; struct lws_context *context; int hide_vhosts; int tow_flag; + int period_us; struct lws_ss_dumps d; struct lws_ss_filepath *fp; }; @@ -63,15 +63,8 @@ struct per_vhost_data__lws_server_status { static const struct lws_protocols protocols[1]; static void -uv_timeout_cb_server_status(uv_timer_t *w -#if UV_VERSION_MAJOR == 0 - , int status -#endif -) +update(struct per_vhost_data__lws_server_status *v) { - struct per_vhost_data__lws_server_status *v = lws_container_of(w, - struct per_vhost_data__lws_server_status, - timeout_watcher); struct lws_ss_filepath *fp; char *p = v->d.buf + LWS_PRE, contents[256], pure[256]; int n, l, first = 1, fd; @@ -135,12 +128,13 @@ callback_lws_server_status(struct lws *wsi, enum lws_callback_reasons reason, lws_protocol_vh_priv_get(lws_get_vhost(wsi), lws_get_protocol(wsi)); struct lws_ss_filepath *fp, *fp1, **fp_old; - int m, period = 1000; + int m; switch (reason) { case LWS_CALLBACK_ESTABLISHED: lwsl_info("%s: LWS_CALLBACK_ESTABLISHED\n", __func__); + lws_set_timer_usecs(wsi, v->period_us); lws_callback_on_writable(wsi); break; @@ -161,7 +155,9 @@ callback_lws_server_status(struct lws *wsi, enum lws_callback_reasons reason, if (!strcmp(pvo->name, "hide-vhosts")) v->hide_vhosts = atoi(pvo->value); if (!strcmp(pvo->name, "update-ms")) - period = atoi(pvo->value); + v->period_us = atoi(pvo->value) * 1000; + else + v->period_us = 5 * 1000 * 1000; if (!strcmp(pvo->name, "filepath")) { fp = malloc(sizeof(*fp)); fp->next = NULL; @@ -174,17 +170,12 @@ callback_lws_server_status(struct lws *wsi, enum lws_callback_reasons reason, pvo = pvo->next; } v->context = lws_get_context(wsi); - uv_timer_init(lws_uv_getloop(v->context, 0), - &v->timeout_watcher); - uv_timer_start(&v->timeout_watcher, - uv_timeout_cb_server_status, 2000, period); + break; case LWS_CALLBACK_PROTOCOL_DESTROY: /* per vhost */ if (!v) break; - uv_timer_stop(&v->timeout_watcher); - uv_close((uv_handle_t *)&v->timeout_watcher, NULL); fp = v->fp; while (fp) { fp1= fp->next; @@ -200,6 +191,12 @@ callback_lws_server_status(struct lws *wsi, enum lws_callback_reasons reason, return -1; break; + case LWS_CALLBACK_TIMER: + lws_set_timer_usecs(wsi, v->period_us); + update(v); + lws_callback_on_writable(wsi); + break; + default: break; } diff --git a/test-apps/test-server-libev.c b/test-apps/test-server-libev.c index debbf0f1..42eca940 100644 --- a/test-apps/test-server-libev.c +++ b/test-apps/test-server-libev.c @@ -198,6 +198,7 @@ int main(int argc, char **argv) struct lws_context_creation_info info; char interface_name[128] = ""; const char *iface = NULL; + void *foreign_loops[1]; char cert_path[1024]; char key_path[1024]; int use_ssl = 0; @@ -319,6 +320,9 @@ int main(int argc, char **argv) info.uid = -1; info.options = opts | LWS_SERVER_OPTION_LIBEV; + foreign_loops[0] = &loop; + info.foreign_loops = foreign_loops; + context = lws_create_context(&info); if (context == NULL) { lwsl_err("libwebsocket init failed\n"); @@ -335,8 +339,7 @@ int main(int argc, char **argv) /* override the active fops */ lws_get_fops(context)->open = test_server_fops_open; - lws_ev_initloop(context, loop, 0); - ev_run(loop, 0); + lws_service(context, 0); lws_context_destroy(context); diff --git a/test-apps/test-server-libevent.c b/test-apps/test-server-libevent.c index 2766c9f7..bc47f999 100644 --- a/test-apps/test-server-libevent.c +++ b/test-apps/test-server-libevent.c @@ -192,6 +192,7 @@ int main(int argc, char **argv) struct event_base *event_base_loop = event_base_new(); struct lws_context_creation_info info; char interface_name[128] = ""; + void *foreign_loops[1]; const char *iface = NULL; char cert_path[1024]; char key_path[1024]; @@ -273,7 +274,8 @@ int main(int argc, char **argv) #endif for (n = 0; n < (int)ARRAY_SIZE(sigs); n++) { - signals[n] = evsignal_new(event_base_loop, sigs[n], signal_cb, event_base_loop); + signals[n] = evsignal_new(event_base_loop, sigs[n], signal_cb, + event_base_loop); evsignal_add(signals[n], NULL); } @@ -315,9 +317,11 @@ int main(int argc, char **argv) } info.gid = -1; info.uid = -1; - info.max_http_header_pool = 1; info.options = opts | LWS_SERVER_OPTION_LIBEVENT; + foreign_loops[0] = event_base_loop; + info.foreign_loops = foreign_loops; + context = lws_create_context(&info); if (context == NULL) { lwsl_err("libwebsocket init failed\n"); @@ -334,13 +338,11 @@ int main(int argc, char **argv) /* override the active fops */ lws_get_fops(context)->open = test_server_fops_open; - // Don't use the default Signal Event Watcher & Handler - lws_event_sigint_cfg(context, 0, NULL); - // Initialize the LWS with libevent loop - lws_event_initloop(context, event_base_loop, 0); - event_base_dispatch(event_base_loop); + for (n = 0; n < (int)ARRAY_SIZE(sigs); n++) + event_free(signals[n]); + lws_context_destroy(context); lwsl_notice("libwebsockets-test-server exited cleanly\n"); diff --git a/test-apps/test-server-libuv.c b/test-apps/test-server-libuv.c index 6d28add4..f20cf8bc 100644 --- a/test-apps/test-server-libuv.c +++ b/test-apps/test-server-libuv.c @@ -82,8 +82,10 @@ static const struct lws_extension exts[] = { { NULL, NULL, NULL /* terminator */ } }; -void signal_cb(uv_signal_t *watcher, int signum) +void signal_cb(void *handle, int signum) { + uv_signal_t *watcher = (uv_signal_t *)handle; + lwsl_err("Signal %d caught, exiting...\n", watcher->signum); switch (watcher->signum) { case SIGTERM: @@ -94,7 +96,7 @@ void signal_cb(uv_signal_t *watcher, int signum) abort(); break; } - lws_libuv_stop(context); + lws_context_destroy(context); } /* @@ -228,6 +230,11 @@ static void timer_close_cb(uv_handle_t *h) h, h->loop->active_handles); } +static void walk_cb(uv_handle_t *h, void *arg) +{ + lwsl_err("%s: handle %p: type %d\n", __func__, h, h->type); +} + void outer_signal_cb(uv_signal_t *s, int signum) { lwsl_notice("Foreign loop got signal %d\n", signum); @@ -255,6 +262,7 @@ int main(int argc, char **argv) const char *iface = NULL; char cert_path[1024]; char key_path[1024]; + void *foreign_loops[1]; int use_ssl = 0; int opts = 0; int n = 0; @@ -351,6 +359,7 @@ int main(int argc, char **argv) info.protocols = protocols; info.extensions = exts; info.mounts = &mount; + info.pcontext = &context; info.ssl_cert_filepath = NULL; info.ssl_private_key_filepath = NULL; @@ -375,7 +384,6 @@ int main(int argc, char **argv) } info.gid = -1; info.uid = -1; - info.max_http_header_pool = 16; info.timeout_secs = 5; info.options = opts | LWS_SERVER_OPTION_LIBUV; @@ -403,7 +411,11 @@ int main(int argc, char **argv) uv_run(&loop, UV_RUN_DEFAULT); /* timer will stop loop and we will get here */ - } + + foreign_loops[0] = &loop; + info.foreign_loops = foreign_loops; + } else + info.signal_cb = signal_cb; #endif context = lws_create_context(&info); @@ -412,25 +424,6 @@ int main(int argc, char **argv) return -1; } - lws_uv_sigint_cfg(context, 1, signal_cb); - -#if UV_VERSION_MAJOR > 0 - if (foreign_libuv_loop) { - /* we have our own uv loop outside of lws */ - lws_uv_initloop(context, &loop, 0); - } else -#endif - { - /* - * lws will create his own libuv loop in the context - */ - if (lws_uv_initloop(context, NULL, 0)) { - lwsl_err("lws_uv_initloop failed\n"); - - goto bail; - } - } - #if UV_VERSION_MAJOR > 0 if (foreign_libuv_loop) { /* @@ -462,7 +455,6 @@ int main(int argc, char **argv) /* detach lws */ lws_context_destroy(context); - lws_context_destroy2(context); lwsl_notice("Please wait while the outer libuv test continues for 10s\n"); @@ -478,10 +470,13 @@ int main(int argc, char **argv) * outside of lws */ uv_timer_stop(&timer_outer); - uv_timer_stop(&timer_test_cancel); uv_close((uv_handle_t*)&timer_outer, timer_close_cb); + uv_timer_stop(&timer_test_cancel); + uv_close((uv_handle_t*)&timer_test_cancel, timer_close_cb); + uv_timer_stop(&timer_inner); uv_close((uv_handle_t*)&timer_inner, timer_close_cb); uv_signal_stop(&signal_outer); + uv_close((uv_handle_t*)&signal_outer, NULL); e = 100; while (e--) @@ -490,17 +485,21 @@ int main(int argc, char **argv) /* PHASE 2: close the UV loop itself */ e = uv_loop_close(&loop); - lwsl_notice("uv loop close rc %s\n", - e ? uv_strerror(e) : "ok"); + if (e) { + lwsl_notice("uv loop close rc %s\n", e ? uv_strerror(e) : "ok"); + + uv_walk(&loop, walk_cb, NULL); + } } else #endif { - lws_libuv_run(context, 0); + lws_service(context, 0); -bail: + /* + * we can't destroy the internal loops while they are running + */ lws_context_destroy(context); - lws_context_destroy2(context); } lwsl_notice("libwebsockets-test-server exited cleanly\n"); diff --git a/test-apps/test-server-v2.0.c b/test-apps/test-server-v2.0.c index 94cddce5..8873d71b 100644 --- a/test-apps/test-server-v2.0.c +++ b/test-apps/test-server-v2.0.c @@ -265,8 +265,10 @@ static const struct lws_protocol_vhost_options pvo = { "" }; -static void signal_cb(uv_signal_t *watcher, int signum) +static void signal_cb(void *handle, int signum) { + uv_signal_t *watcher = (uv_signal_t *)handle; + lwsl_err("Signal %d caught, exiting...\n", watcher->signum); switch (watcher->signum) { case SIGTERM: @@ -277,7 +279,7 @@ static void signal_cb(uv_signal_t *watcher, int signum) abort(); break; } - lws_libuv_stop(context); + lws_context_destroy(context); } static const struct option options[] = { @@ -318,6 +320,7 @@ int main(int argc, char **argv) char key_path[1024] = ""; char ca_path[1024] = ""; int uid = -1, gid = -1; + void *foreign_loops[1]; int use_ssl = 0; uv_loop_t loop; int opts = 0; @@ -507,6 +510,10 @@ int main(int argc, char **argv) * our vhost */ info.pvo = &pvo; + info.signal_cb = signal_cb; + + foreign_loops[0] = &loop; + info.foreign_loops = foreign_loops; /* * Since we used LWS_SERVER_OPTION_EXPLICIT_VHOSTS, this only creates @@ -534,19 +541,10 @@ int main(int argc, char **argv) info.port++; #endif - /* libuv event loop */ - lws_uv_sigint_cfg(context, 1, signal_cb); - if (lws_uv_initloop(context, &loop, 0)) { - lwsl_err("lws_uv_initloop failed\n"); - goto bail; - } + lws_service(context, 0); - lws_libuv_run(context, 0); - -bail: /* when we decided to exit the event loop */ lws_context_destroy(context); - lws_context_destroy2(context); #if defined(TEST_DYNAMIC_VHOST)