Skip to content

Commit b0968b9

Browse files
author
Viktor Jannicke
committed
Add WebUI-specific CivetWeb quiesce support
1 parent 37b0206 commit b0968b9

1 file changed

Lines changed: 118 additions & 8 deletions

File tree

src/civetweb/civetweb.c

Lines changed: 118 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2431,7 +2431,9 @@ struct mg_context {
24312431

24322432
/* Thread related */
24332433
stop_flag_t stop_flag; /* Should we stop event loop */
2434+
stop_flag_t stop_accepting; /* Should newly accepted sockets be rejected */
24342435
pthread_mutex_t thread_mutex; /* Protects client_socks or queue */
2436+
pthread_mutex_t socket_mutex; /* Synchronizes worker socket scan and close */
24352437

24362438
pthread_t masterthreadid; /* The master thread ID */
24372439
unsigned int cfg_max_worker_threads; /* How many worker-threads we are
@@ -18405,6 +18407,9 @@ close_connection(struct mg_connection *conn)
1840518407
conn->ssl = NULL;
1840618408
}
1840718409
#endif
18410+
if (conn->phys_ctx->context_type == CONTEXT_SERVER) {
18411+
(void)pthread_mutex_lock(&conn->phys_ctx->socket_mutex);
18412+
}
1840818413
if (conn->client.sock != INVALID_SOCKET) {
1840918414
#if defined(__ZEPHYR__)
1841018415
closesocket(conn->client.sock);
@@ -18413,6 +18418,9 @@ close_connection(struct mg_connection *conn)
1841318418
#endif
1841418419
conn->client.sock = INVALID_SOCKET;
1841518420
}
18421+
if (conn->phys_ctx->context_type == CONTEXT_SERVER) {
18422+
(void)pthread_mutex_unlock(&conn->phys_ctx->socket_mutex);
18423+
}
1841618424

1841718425
/* call the connection_closed callback if assigned */
1841818426
if (conn->phys_ctx->callbacks.connection_closed != NULL) {
@@ -18493,6 +18501,59 @@ mg_close_connection(struct mg_connection *conn)
1849318501
}
1849418502

1849518503

18504+
/* WebUI-private extension: quiesce a server without releasing connection
18505+
* storage retained by WebUI callback tasks. Not part of the CivetWeb API. */
18506+
void
18507+
webui_civetweb_shutdown_context_connections(struct mg_context *ctx)
18508+
{
18509+
if ((ctx == NULL) || (ctx->worker_connections == NULL)) {
18510+
return;
18511+
}
18512+
18513+
/* Quiesce socket dispatch before taking the worker snapshot. A socket that
18514+
* was accepted just before this flag changed is rejected by produce_socket,
18515+
* and dynamic worker publication uses the same mutex. Workers themselves
18516+
* remain alive so external users may safely retire their conn pointers. */
18517+
(void)pthread_mutex_lock(&ctx->thread_mutex);
18518+
STOP_FLAG_ASSIGN(&ctx->stop_accepting, 1);
18519+
(void)pthread_mutex_lock(&ctx->socket_mutex);
18520+
18521+
#if defined(ALTERNATIVE_QUEUE)
18522+
for (unsigned int i = 0; i < ctx->spawned_worker_threads; i++) {
18523+
if ((ctx->client_socks != NULL)
18524+
&& (ctx->client_socks[i].in_use == 1)
18525+
&& (ctx->client_socks[i].sock != INVALID_SOCKET)) {
18526+
shutdown(ctx->client_socks[i].sock, SHUTDOWN_BOTH);
18527+
}
18528+
}
18529+
#else
18530+
/* No worker may consume the queue while thread_mutex is held. Close queued
18531+
* sockets so every connection that can still be used is represented in
18532+
* worker_connections below. */
18533+
while (ctx->sq_tail < ctx->sq_head) {
18534+
struct socket *queued = &ctx->squeue[ctx->sq_tail % ctx->sq_size];
18535+
if (queued->sock != INVALID_SOCKET) {
18536+
set_blocking_mode(queued->sock);
18537+
closesocket(queued->sock);
18538+
queued->sock = INVALID_SOCKET;
18539+
}
18540+
ctx->sq_tail++;
18541+
}
18542+
(void)pthread_cond_broadcast(&ctx->sq_empty);
18543+
#endif
18544+
18545+
unsigned int count = ctx->spawned_worker_threads;
18546+
for (unsigned int i = 0; i < count; i++) {
18547+
struct mg_connection *conn = &ctx->worker_connections[i];
18548+
if (conn->client.sock != INVALID_SOCKET) {
18549+
shutdown(conn->client.sock, SHUTDOWN_BOTH);
18550+
}
18551+
}
18552+
(void)pthread_mutex_unlock(&ctx->socket_mutex);
18553+
(void)pthread_mutex_unlock(&ctx->thread_mutex);
18554+
}
18555+
18556+
1849618557
static struct mg_connection *
1849718558
mg_connect_client_impl(const struct mg_client_options *client_options,
1849818559
int use_ssl,
@@ -20099,17 +20160,29 @@ static void
2009920160
produce_socket(struct mg_context *ctx, const struct socket *sp)
2010020161
{
2010120162
unsigned int i;
20163+
(void)pthread_mutex_lock(&ctx->thread_mutex);
20164+
int accepting = STOP_FLAG_IS_ZERO(&ctx->stop_flag)
20165+
&& STOP_FLAG_IS_ZERO(&ctx->stop_accepting);
20166+
(void)pthread_mutex_unlock(&ctx->thread_mutex);
20167+
if (!accepting) {
20168+
set_blocking_mode(sp->sock);
20169+
closesocket(sp->sock);
20170+
return;
20171+
}
2010220172

2010320173
(void)mg_start_worker_thread(
2010420174
ctx, 1); /* will start a worker-thread only if there aren't currently
2010520175
any idle worker-threads */
2010620176

20107-
while (!ctx->stop_flag) {
20177+
while (STOP_FLAG_IS_ZERO(&ctx->stop_flag)
20178+
&& STOP_FLAG_IS_ZERO(&ctx->stop_accepting)) {
2010820179
for (i = 0; i < ctx->spawned_worker_threads; i++) {
2010920180
/* find a free worker slot and signal it */
2011020181
if (ctx->client_socks[i].in_use == 2) {
2011120182
(void)pthread_mutex_lock(&ctx->thread_mutex);
20112-
if ((ctx->client_socks[i].in_use == 2) && !ctx->stop_flag) {
20183+
if ((ctx->client_socks[i].in_use == 2)
20184+
&& STOP_FLAG_IS_ZERO(&ctx->stop_flag)
20185+
&& STOP_FLAG_IS_ZERO(&ctx->stop_accepting)) {
2011320186
ctx->client_socks[i] = *sp;
2011420187
ctx->client_socks[i].in_use = 1;
2011520188
/* socket has been moved to the consumer */
@@ -20149,6 +20222,11 @@ consume_socket(struct mg_context *ctx,
2014920222

2015020223
(void)pthread_mutex_lock(&ctx->thread_mutex);
2015120224
*sp = ctx->client_socks[thread_index];
20225+
/* Ownership moves to the worker connection. Do not leave a stale socket
20226+
* number in the handoff slot where a later shutdown scan could mistake it
20227+
* for an unconsumed connection after the descriptor has been reused. */
20228+
ctx->client_socks[thread_index].sock = INVALID_SOCKET;
20229+
ctx->client_socks[thread_index].in_use = 0;
2015220230
if (ctx->stop_flag) {
2015320231
(void)pthread_mutex_unlock(&ctx->thread_mutex);
2015420232
if (sp->in_use == 1) {
@@ -20223,13 +20301,22 @@ static void
2022320301
produce_socket(struct mg_context *ctx, const struct socket *sp)
2022420302
{
2022520303
int queue_filled;
20304+
int queued = 0;
2022620305

2022720306
(void)pthread_mutex_lock(&ctx->thread_mutex);
20307+
if (!STOP_FLAG_IS_ZERO(&ctx->stop_flag)
20308+
|| !STOP_FLAG_IS_ZERO(&ctx->stop_accepting)) {
20309+
(void)pthread_mutex_unlock(&ctx->thread_mutex);
20310+
set_blocking_mode(sp->sock);
20311+
closesocket(sp->sock);
20312+
return;
20313+
}
2022820314

2022920315
queue_filled = ctx->sq_head - ctx->sq_tail;
2023020316

2023120317
/* If the queue is full, wait */
2023220318
while (STOP_FLAG_IS_ZERO(&ctx->stop_flag)
20319+
&& STOP_FLAG_IS_ZERO(&ctx->stop_accepting)
2023320320
&& (queue_filled >= ctx->sq_size)) {
2023420321
ctx->sq_blocked = 1; /* Status information: All threads busy */
2023520322
#if defined(USE_SERVER_STATS)
@@ -20242,10 +20329,13 @@ produce_socket(struct mg_context *ctx, const struct socket *sp)
2024220329
queue_filled = ctx->sq_head - ctx->sq_tail;
2024320330
}
2024420331

20245-
if (queue_filled < ctx->sq_size) {
20332+
if (STOP_FLAG_IS_ZERO(&ctx->stop_flag)
20333+
&& STOP_FLAG_IS_ZERO(&ctx->stop_accepting)
20334+
&& (queue_filled < ctx->sq_size)) {
2024620335
/* Copy socket to the queue and increment head */
2024720336
ctx->squeue[ctx->sq_head % ctx->sq_size] = *sp;
2024820337
ctx->sq_head++;
20338+
queued = 1;
2024920339
DEBUG_TRACE("queued socket %d", sp ? sp->sock : -1);
2025020340
}
2025120341

@@ -20258,6 +20348,11 @@ produce_socket(struct mg_context *ctx, const struct socket *sp)
2025820348

2025920349
(void)pthread_cond_signal(&ctx->sq_full);
2026020350
(void)pthread_mutex_unlock(&ctx->thread_mutex);
20351+
if (!queued) {
20352+
set_blocking_mode(sp->sock);
20353+
closesocket(sp->sock);
20354+
return;
20355+
}
2026120356

2026220357
(void)mg_start_worker_thread(
2026320358
ctx, 1); /* will start a worker-thread only if there aren't currently
@@ -20740,6 +20835,7 @@ master_thread_run(struct mg_context *ctx)
2074020835
* Therefore, we're checking pfd[i].revents & POLLIN, not
2074120836
* pfd[i].revents == POLLIN. */
2074220837
if (STOP_FLAG_IS_ZERO(&ctx->stop_flag)
20838+
&& STOP_FLAG_IS_ZERO(&ctx->stop_accepting)
2074320839
&& (pfd[i].revents & POLLIN)) {
2074420840
accept_new_connection(&ctx->listening_sockets[i], ctx);
2074520841
}
@@ -20865,6 +20961,7 @@ free_context(struct mg_context *ctx)
2086520961
* condvars
2086620962
*/
2086720963
(void)pthread_mutex_destroy(&ctx->thread_mutex);
20964+
(void)pthread_mutex_destroy(&ctx->socket_mutex);
2086820965

2086920966
#if defined(ALTERNATIVE_QUEUE)
2087020967
mg_free(ctx->client_socks);
@@ -20979,7 +21076,7 @@ mg_stop(struct mg_context *ctx)
2097921076
STOP_FLAG_ASSIGN(&ctx->stop_flag, 1);
2098021077

2098121078
/* Closing this socket will cause mg_poll() in all the I/O threads to return
20982-
* immediately */
21079+
* immediately. */
2098321080
closesocket(ctx->user_shutdown_notification_socket);
2098421081
ctx->user_shutdown_notification_socket =
2098521082
-1; /* to avoid calling closesocket() again in free_context() */
@@ -21154,13 +21251,24 @@ mg_socketpair(int *sockA, int *sockB)
2115421251
static int
2115521252
mg_start_worker_thread(struct mg_context *ctx, int only_if_no_idle_threads)
2115621253
{
21254+
/* Keep worker reservation, creation and publication atomic with respect to
21255+
* an external stop/connection-interrupt request. The new worker may start
21256+
* running immediately, but it cannot consume a socket until this mutex is
21257+
* released. */
21258+
(void)pthread_mutex_lock(&ctx->thread_mutex);
2115721259
const unsigned int i = ctx->spawned_worker_threads;
2115821260
if (i >= ctx->cfg_max_worker_threads) {
21261+
(void)pthread_mutex_unlock(&ctx->thread_mutex);
2115921262
return -1; /* Oops, we hit our worker-thread limit! No more worker
2116021263
threads, ever! */
2116121264
}
2116221265

21163-
(void)pthread_mutex_lock(&ctx->thread_mutex);
21266+
if (!STOP_FLAG_IS_ZERO(&ctx->stop_flag)
21267+
|| !STOP_FLAG_IS_ZERO(&ctx->stop_accepting)) {
21268+
(void)pthread_mutex_unlock(&ctx->thread_mutex);
21269+
return -1;
21270+
}
21271+
2116421272
#if defined(ALTERNATIVE_QUEUE)
2116521273
if ((only_if_no_idle_threads) && (ctx->idle_worker_thread_count > 0)) {
2116621274
#else
@@ -21175,7 +21283,6 @@ mg_start_worker_thread(struct mg_context *ctx, int only_if_no_idle_threads)
2117521283
ctx->idle_worker_thread_count++; /* we do this here to avoid a race
2117621284
condition while the thread is starting
2117721285
up */
21178-
(void)pthread_mutex_unlock(&ctx->thread_mutex);
2117921286

2118021287
ctx->worker_connections[i].phys_ctx = ctx;
2118121288
int ret = mg_start_thread_with_id(worker_thread,
@@ -21186,10 +21293,9 @@ mg_start_worker_thread(struct mg_context *ctx, int only_if_no_idle_threads)
2118621293
the table */
2118721294
DEBUG_TRACE("Started worker_thread #%i", ctx->spawned_worker_threads);
2118821295
} else {
21189-
(void)pthread_mutex_lock(&ctx->thread_mutex);
2119021296
ctx->idle_worker_thread_count--; /* whoops, roll-back on error */
21191-
(void)pthread_mutex_unlock(&ctx->thread_mutex);
2119221297
}
21298+
(void)pthread_mutex_unlock(&ctx->thread_mutex);
2119321299
return ret;
2119421300
}
2119521301

@@ -21266,6 +21372,7 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error)
2126621372
pthread_setspecific(sTlsKey, &tls);
2126721373

2126821374
ok = (0 == pthread_mutex_init(&ctx->thread_mutex, &pthread_mutex_attr));
21375+
ok &= (0 == pthread_mutex_init(&ctx->socket_mutex, &pthread_mutex_attr));
2126921376
#if !defined(ALTERNATIVE_QUEUE)
2127021377
ok &= (0 == pthread_cond_init(&ctx->sq_empty, NULL));
2127121378
ok &= (0 == pthread_cond_init(&ctx->sq_full, NULL));
@@ -21773,6 +21880,9 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error)
2177321880
pthread_setspecific(sTlsKey, NULL);
2177421881
return NULL;
2177521882
}
21883+
for (i = 0; (unsigned)i < ctx->cfg_max_worker_threads; i++) {
21884+
ctx->worker_connections[i].client.sock = INVALID_SOCKET;
21885+
}
2177621886

2177721887
#if defined(ALTERNATIVE_QUEUE)
2177821888
ctx->client_wait_events =

0 commit comments

Comments
 (0)