diff --git a/include/libwebsockets/lws-context-vhost.h b/include/libwebsockets/lws-context-vhost.h index 2a7bdbe59..9240674a4 100644 --- a/include/libwebsockets/lws-context-vhost.h +++ b/include/libwebsockets/lws-context-vhost.h @@ -1123,6 +1123,24 @@ lws_context_user(struct lws_context *context); LWS_VISIBLE LWS_EXTERN const char * lws_vh_tag(struct lws_vhost *vh); +/** + * lws_context_is_being_destroyed() - find out if context is being destroyed + * + * \param context: the struct lws_context pointer + * + * Returns nonzero if the context has had lws_context_destroy() called on it... + * when using event library loops the destroy process can be asynchronous. In + * the special case of libuv foreign loops, the failure to create the context + * may have to do work on the foreign loop to reverse the partial creation, + * meaning a failed context create cannot unpick what it did and return NULL. + * + * In that condition, a valid context that is already started the destroy + * process is returned, and this test api will return nonzero as a way to + * find out the create is in the middle of failing. + */ +LWS_VISIBLE LWS_EXTERN int +lws_context_is_being_destroyed(struct lws_context *context); + /*! \defgroup vhost-mounts Vhost mounts and options * \ingroup context-and-vhost-creation * diff --git a/lib/core-net/vhost.c b/lib/core-net/vhost.c index ab8c0ca4a..5ba736377 100644 --- a/lib/core-net/vhost.c +++ b/lib/core-net/vhost.c @@ -1053,7 +1053,6 @@ lws_vhost_destroy1(struct lws_vhost *vh) break; } } lws_end_foreach_ll(v, vhost_next); - #endif lws_vhost_unlock(vh); /* } vh -------------- */ diff --git a/lib/core/context.c b/lib/core/context.c index b7e379fd3..921af1e05 100644 --- a/lib/core/context.c +++ b/lib/core/context.c @@ -1871,10 +1871,6 @@ next: #if defined(LWS_WITH_NETWORK) - context->evlib_finalize_destroy_after_int_loops_stop = 1; - if (context->event_loop_ops->destroy_context2) - context->event_loop_ops->destroy_context2(context); - for (n = 0; n < context->count_threads; n++) { struct lws_context_per_thread *pt = &context->pt[n]; (void)pt; @@ -1915,11 +1911,20 @@ next: goto bail; } + if (!context->pt[0].event_loop_foreign) { + lwsl_notice("%s: waiting for internal loop exit\n", __func__); + + goto bail; + } #endif case LWSCD_FINALIZATION: + context->evlib_finalize_destroy_after_int_loops_stop = 1; + #if defined(LWS_WITH_NETWORK) + if (context->event_loop_ops->destroy_context2) + context->event_loop_ops->destroy_context2(context); /* * finalize destroy of pt and things hanging off it @@ -2027,6 +2032,12 @@ bail: lws_context_unlock(context); } +int +lws_context_is_being_destroyed(struct lws_context *context) +{ + return !!context->being_destroyed; +} + #if defined(LWS_WITH_SYS_STATE) struct lws_context * lws_system_context_from_system_mgr(lws_state_manager_t *mgr) diff --git a/lib/event-libs/libuv/libuv.c b/lib/event-libs/libuv/libuv.c index 9fdd76751..b9fae034a 100644 --- a/lib/event-libs/libuv/libuv.c +++ b/lib/event-libs/libuv/libuv.c @@ -227,13 +227,21 @@ lws_uv_finalize_pt(struct lws_context_per_thread *pt) return 1; } } else - lwsl_debug("%s: still %d undestroyed\n", __func__, pt->context->undestroyed_threads); + lwsl_debug("%s: still %d undestroyed\n", __func__, + pt->context->undestroyed_threads); lws_context_unlock(pt->context); return 0; } +// static void lws_uv_walk_cb(uv_handle_t *handle, void *arg) +// { +// if (!uv_is_closing(handle)) +// lwsl_err("%s: handle %p still alive on loop\n", __func__, handle); +// } + + static const int sigs[] = { SIGINT, SIGTERM, SIGSEGV, SIGFPE, SIGHUP }; /* @@ -261,6 +269,13 @@ lws_uv_close_cb_sa(uv_handle_t *handle) ptpriv->extant_handles) return; + /* + * So we believe nothing of ours left on the loop. Let's sanity + * check it to count what's still on the loop + */ + + // uv_walk(pt_to_priv_uv(pt)->io_loop, lws_uv_walk_cb, NULL); + /* * That's it... all wsi were down, and now every * static asset lws had a UV handle for is down. @@ -270,9 +285,6 @@ lws_uv_close_cb_sa(uv_handle_t *handle) lwsl_info("%s: thr %d: seen final static handle gone\n", __func__, tsi); - if (ptpriv->io_loop && !pt->event_loop_foreign) - uv_stop(pt_to_priv_uv(pt)->io_loop); - if (!pt->event_loop_foreign) { lwsl_info("%s: calling lws_context_destroy2\n", __func__); lws_context_destroy(context); @@ -308,24 +320,6 @@ lws_libuv_static_refcount_del(uv_handle_t *h) lws_uv_close_cb_sa(h); } - -static void lws_uv_close_cb(uv_handle_t *handle) -{ -} - -static void lws_uv_walk_cb(uv_handle_t *handle, void *arg) -{ - if (!uv_is_closing(handle)) - uv_close(handle, lws_uv_close_cb); -} - -void -lws_close_all_handles_in_loop(uv_loop_t *loop) -{ - uv_walk(loop, lws_uv_walk_cb, NULL); -} - - void lws_libuv_stop_without_kill(const struct lws_context *context, int tsi) { @@ -333,8 +327,6 @@ lws_libuv_stop_without_kill(const struct lws_context *context, int tsi) uv_stop(pt_to_priv_uv(&context->pt[tsi])->io_loop); } - - uv_loop_t * lws_uv_getloop(struct lws_context *context, int tsi) { @@ -516,6 +508,12 @@ elops_accept_uv(struct lws *wsi) ptpriv->extant_handles++; + lwsl_debug("%s: thr %d: %s sa left %d: dyn left: %d\n", __func__, + (int)(pt - &pt->context->pt[0]), + lws_wsi_tag(wsi), + pt->count_event_loop_static_asset_handles, + ptpriv->extant_handles); + return 0; } @@ -575,6 +573,7 @@ static int elops_init_vhost_listen_wsi_uv(struct lws *wsi) { struct lws_context_per_thread *pt; + struct lws_pt_eventlibs_libuv *ptpriv; struct lws_io_watcher_libuv *w_read; int n; @@ -587,7 +586,8 @@ elops_init_vhost_listen_wsi_uv(struct lws *wsi) return 0; pt = &wsi->a.context->pt[(int)wsi->tsi]; - if (!pt_to_priv_uv(pt)->io_loop) + ptpriv = pt_to_priv_uv(pt); + if (!ptpriv->io_loop) return 0; w_read->context = wsi->a.context; @@ -605,6 +605,14 @@ elops_init_vhost_listen_wsi_uv(struct lws *wsi) return -1; } + ptpriv->extant_handles++; + + lwsl_debug("%s: thr %d: %s sa left %d: dyn left: %d\n", __func__, + (int)(pt - &pt->context->pt[0]), + lws_wsi_tag(wsi), + pt->count_event_loop_static_asset_handles, + ptpriv->extant_handles); + ((uv_handle_t *)w_read->pwatcher)->data = (void *)wsi; elops_io_uv(wsi, LWS_EV_START | LWS_EV_READ); @@ -623,16 +631,22 @@ static void elops_destroy_pt_uv(struct lws_context *context, int tsi) { struct lws_context_per_thread *pt = &context->pt[tsi]; + struct lws_pt_eventlibs_libuv *ptpriv = pt_to_priv_uv(pt); int m, ns; if (!lws_check_opt(context->options, LWS_SERVER_OPTION_LIBUV)) return; - if (!pt_to_priv_uv(pt)->io_loop) + if (!ptpriv->io_loop) return; - if (pt->event_loop_destroy_processing_done) + if (pt->event_loop_destroy_processing_done) { + if (!pt->event_loop_foreign) { + lwsl_warn("%s: stopping event loop\n", __func__); + uv_stop(pt_to_priv_uv(pt)->io_loop); + } return; + } pt->event_loop_destroy_processing_done = 1; lwsl_debug("%s: %d\n", __func__, tsi); @@ -774,9 +788,17 @@ lws_libuv_closewsi(uv_handle_t* handle) #endif lws_pt_lock(pt, __func__); + + lwsl_notice("%s: thr %d: %s sa left %d: dyn left: %d (rk %d)\n", __func__, + (int)(pt - &pt->context->pt[0]), + lws_wsi_tag(wsi), + pt->count_event_loop_static_asset_handles, + ptpriv->extant_handles - 1, + context->requested_stop_internal_loops); + __lws_close_free_wsi_final(wsi); + assert(ptpriv->extant_handles); ptpriv->extant_handles--; - assert(ptpriv >= 0); lws_pt_unlock(pt); /* it's our job to close the handle finally */ @@ -789,12 +811,6 @@ lws_libuv_closewsi(uv_handle_t* handle) } #endif - lwsl_notice("%s: thr %d: sa left %d: dyn left: %d (rk %d)\n", __func__, - (int)(pt - &pt->context->pt[0]), - pt->count_event_loop_static_asset_handles, - ptpriv->extant_handles, - context->requested_stop_internal_loops); - /* * eventually, we closed all the wsi... */ diff --git a/lib/plat/unix/unix-fds.c b/lib/plat/unix/unix-fds.c index e15b57c3b..ec457664d 100644 --- a/lib/plat/unix/unix-fds.c +++ b/lib/plat/unix/unix-fds.c @@ -90,7 +90,7 @@ sanity_assert_no_sockfd_traces(const struct lws_context *context, #else struct lws **p, **done; - if (sfd == LWS_SOCK_INVALID) + if (sfd == LWS_SOCK_INVALID || !context->lws_lookup) return 0; if (!context->max_fds_unrelated_to_ulimit &&