diff --git a/READMEs/README.lws_system.md b/READMEs/README.lws_system.md index 05afa4701..e1a91eea4 100644 --- a/READMEs/README.lws_system.md +++ b/READMEs/README.lws_system.md @@ -207,6 +207,7 @@ The generic states defined are: |`LWS_SYSTATE_AUTH2`|Optional second access token for different services| |`LWS_SYSTATE_OPERATIONAL`|The system is ready for user code to work normally| |`LWS_SYSTATE_POLICY_INVALID`|All connections are being dropped because policy information is changing. It will transition back to `LWS_SYSTATE_INITIALIZED` and onward to `OPERATIONAL` again afterwards with the new policy| +|`LWS_SYSTATE_CONTEXT_DESTROYING`|Context is going down and smd with it| ### Inserting a notifier diff --git a/include/libwebsockets/lws-system.h b/include/libwebsockets/lws-system.h index 70d03efd5..07900e02b 100644 --- a/include/libwebsockets/lws-system.h +++ b/include/libwebsockets/lws-system.h @@ -139,6 +139,7 @@ typedef enum { /* keep system_state_names[] in sync in context.c */ * drop everything done with old * policy, switch to new then enter * LWS_SYSTATE_POLICY_VALID */ + LWS_SYSTATE_CONTEXT_DESTROYING, /* Context is being destroyed */ } lws_system_states_t; /* Captive Portal Detect -related */ diff --git a/lib/core-net/vhost.c b/lib/core-net/vhost.c index 515008366..298bf01e8 100644 --- a/lib/core-net/vhost.c +++ b/lib/core-net/vhost.c @@ -1286,6 +1286,7 @@ void lws_vhost_destroy1(struct lws_vhost *vh) { struct lws_context *context = vh->context; + int n; lwsl_vhost_info(vh, "\n"); @@ -1294,6 +1295,14 @@ lws_vhost_destroy1(struct lws_vhost *vh) if (vh->being_destroyed) goto out; + /* + * let's lock all the pts, to enforce pt->vh order... pt is refcounted + * so it's OK if we acquire it later inside this + */ + + for (n = 0; n < context->count_threads; n++) + lws_pt_lock((&context->pt[n]), __func__); + lws_vhost_lock(vh); /* -------------- vh { */ #if defined(LWS_WITH_TLS_SESSIONS) && defined(LWS_WITH_TLS) @@ -1390,6 +1399,9 @@ lws_vhost_destroy1(struct lws_vhost *vh) lws_vhost_unlock(vh); /* } vh -------------- */ + for (n = 0; n < context->count_threads; n++) + lws_pt_unlock((&context->pt[n])); + out: lws_context_unlock(context); /* --------------------------- context { */ } @@ -1525,7 +1537,7 @@ __lws_vhost_destroy2(struct lws_vhost *vh) #endif #if LWS_MAX_SMP > 1 - lws_mutex_refcount_destroy(&context->mr); + lws_mutex_refcount_destroy(&vh->mr); #endif #if defined(LWS_WITH_UNIX_SOCK) diff --git a/lib/core/context.c b/lib/core/context.c index 541e4c24f..95219e473 100644 --- a/lib/core/context.c +++ b/lib/core/context.c @@ -75,7 +75,8 @@ static const char * system_state_names[] = { "AUTH1", "AUTH2", "OPERATIONAL", - "POLICY_INVALID" + "POLICY_INVALID", + "DESTROYING" }; @@ -2084,6 +2085,9 @@ next: if (context->event_loop_ops->destroy_context2) context->event_loop_ops->destroy_context2(context); + lws_state_transition_steps(&context->mgr_system, + LWS_SYSTATE_CONTEXT_DESTROYING); + /* * finalize destroy of pt and things hanging off it */ diff --git a/minimal-examples/ws-server/minimal-ws-server-threads-smp/minimal-ws-server.c b/minimal-examples/ws-server/minimal-ws-server-threads-smp/minimal-ws-server.c index 4613e9e86..43c5ea37d 100644 --- a/minimal-examples/ws-server/minimal-ws-server-threads-smp/minimal-ws-server.c +++ b/minimal-examples/ws-server/minimal-ws-server-threads-smp/minimal-ws-server.c @@ -41,7 +41,8 @@ static struct lws_protocols protocols[] = { }; static struct lws_context *context; -static int interrupted; +static int interrupted, started; +static pthread_t pthread_service[COUNT_THREADS]; static const struct lws_http_mount mount = { /* .mount_next */ NULL, /* linked-list "next" */ @@ -98,18 +99,58 @@ void *thread_service(void *threadid) return NULL; } +static int +system_notify_cb(lws_state_manager_t *mgr, lws_state_notify_link_t *link, + int current, int target) +{ + struct lws_context *context = mgr->parent; + void *retval; + + if (current != target) + return 0; + + switch (current) { + case LWS_SYSTATE_OPERATIONAL: + lwsl_notice(" Service threads: %d\n", + lws_get_count_threads(context)); + + /* start all the service threads */ + + for (started = 1; started < lws_get_count_threads(context); + started++) + if (pthread_create(&pthread_service[started], NULL, + thread_service, + (void *)(lws_intptr_t)started)) + lwsl_err("Failed to start service thread\n"); + break; + case LWS_SYSTATE_CONTEXT_DESTROYING: + /* wait for all the service threads to exit */ + + while ((--started) >= 1) + pthread_join(pthread_service[started], &retval); + + break; + } + + return 0; +} + +lws_state_notify_link_t notifier = { { NULL, NULL, NULL }, + system_notify_cb, "app" }; +lws_state_notify_link_t *na[] = { ¬ifier, NULL }; + void sigint_handler(int sig) { interrupted = 1; + lws_cancel_service(context); } int main(int argc, const char **argv) { - int n, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; - pthread_t pthread_service[COUNT_THREADS]; + int logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; struct lws_context_creation_info info; const char *p; - void *retval; + int n = 0; signal(SIGINT, sigint_handler); @@ -125,6 +166,7 @@ int main(int argc, const char **argv) info.protocols = protocols; info.pvo = &pvo; /* per-vhost options */ info.count_threads = COUNT_THREADS; + info.register_notifier_list = na; info.options = LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; @@ -134,19 +176,8 @@ int main(int argc, const char **argv) return 1; } - lwsl_notice(" Service threads: %d\n", lws_get_count_threads(context)); - - /* start all the service threads */ - - for (n = 0; n < lws_get_count_threads(context); n++) - if (pthread_create(&pthread_service[n], NULL, thread_service, - (void *)(lws_intptr_t)n)) - lwsl_err("Failed to start service thread\n"); - - /* wait for all the service threads to exit */ - - while ((--n) >= 0) - pthread_join(pthread_service[n], &retval); + while (n >= 0 && !interrupted) + n = lws_service(context, 0); lws_context_destroy(context);