@@ -680,6 +680,9 @@ static bool _webui_mutex_is_thread_running(_webui_window_t* win, int update);
680680static bool _webui_mutex_is_start_requested(_webui_window_t* win, int update);
681681static bool _webui_mutex_is_reload_requested(_webui_window_t* win, int update);
682682static void _webui_window_request_reload(_webui_window_t* win);
683+ static void _webui_window_wait_for_reload(_webui_window_t* win);
684+ static bool _webui_window_thread_end(_webui_window_t* win);
685+ static bool _webui_window_cycle_end(_webui_window_t* win);
683686static _webui_window_t* _webui_dereference_win_num(size_t num);
684687static void _webui_start_server_thread(_webui_window_t* win);
685688static void _webui_window_close_ui(_webui_window_t* win);
@@ -3617,6 +3620,9 @@ const char* webui_get_url(size_t window) {
36173620 if (win == NULL)
36183621 return NULL;
36193622
3623+ // Report the URL of the new server if a reload is pending
3624+ _webui_window_wait_for_reload(win);
3625+
36203626 // Check if local server is started
36213627 if (_webui_is_empty(win->url)) {
36223628 // Start local server
@@ -6545,8 +6551,10 @@ static bool _webui_mutex_is_reload_requested(_webui_window_t* win, int update) {
65456551static void _webui_window_request_reload(_webui_window_t* win) {
65466552
65476553 // Ask the persistent server thread of a window to restart its web
6548- // server using the current settings. If clients are connected they
6549- // get closed first: the thread reloads once the serve cycle ends.
6554+ // server using the current settings. The flag also ends the current
6555+ // serve cycle, so the thread reaches the reload immediately. The
6556+ // connected clients are asked to close: they are bound to the old
6557+ // server, which is about to be stopped.
65506558
65516559 #ifdef WEBUI_LOG
65526560 _webui_log_debug("[Core]\t\t_webui_window_request_reload([%zu])\n", win->num);
@@ -6557,6 +6565,50 @@ static void _webui_window_request_reload(_webui_window_t* win) {
65576565 webui_close(win->num);
65586566}
65596567
6568+ static void _webui_window_wait_for_reload(_webui_window_t* win) {
6569+
6570+ // Wait for a pending reload of this window to complete. The reload
6571+ // flag stays set until the web server is listening again, so after
6572+ // this the window port and URL are the final ones.
6573+
6574+ if (!_webui_mutex_is_reload_requested(win, WEBUI_MUTEX_GET_STATUS))
6575+ return;
6576+
6577+ #ifdef WEBUI_LOG
6578+ _webui_log_debug("[Core]\t\t_webui_window_wait_for_reload([%zu])\n", win->num);
6579+ #endif
6580+
6581+ _webui_timer_t timer;
6582+ _webui_timer_start(&timer);
6583+ for (;;) {
6584+ _webui_sleep(1);
6585+ if (!_webui_mutex_is_reload_requested(win, WEBUI_MUTEX_GET_STATUS))
6586+ break;
6587+ if (!_webui_mutex_is_thread_running(win, WEBUI_MUTEX_GET_STATUS))
6588+ break;
6589+ if (_webui_mutex_app_is_exit_now(WEBUI_MUTEX_GET_STATUS))
6590+ break;
6591+ if (_webui_timer_is_end(&timer, 5000))
6592+ break;
6593+ }
6594+ }
6595+
6596+ static bool _webui_window_thread_end(_webui_window_t* win) {
6597+
6598+ // Should the persistent server thread of this window exit?
6599+ return (_webui_mutex_app_is_exit_now(WEBUI_MUTEX_GET_STATUS) ||
6600+ _webui_mutex_win_is_exit_now(win, WEBUI_MUTEX_GET_STATUS));
6601+ }
6602+
6603+ static bool _webui_window_cycle_end(_webui_window_t* win) {
6604+
6605+ // Should the current serve cycle of this window end? A reload ends
6606+ // the cycle as well, so the server gets restarted right away instead
6607+ // of waiting for the connection timeouts to expire.
6608+ return (_webui_window_thread_end(win) ||
6609+ _webui_mutex_is_reload_requested(win, WEBUI_MUTEX_GET_STATUS));
6610+ }
6611+
65606612static void _webui_window_task_count(_webui_window_t* win, int delta) {
65616613
65626614 _webui_mutex_lock(&win->mutex_win_thread);
@@ -9496,6 +9548,11 @@ static bool _webui_show_window_impl(_webui_window_t* win, struct mg_connection*
94969548 }
94979549 #endif
94989550
9551+ // A pending reload is about to change this window's port and URL.
9552+ // Let the server thread finish it first, so this show uses the
9553+ // new ones instead of pointing the UI at the old server.
9554+ _webui_window_wait_for_reload(win);
9555+
94999556 // Initialization
95009557 bool keep_user_index_file = false;
95019558 if (type == WEBUI_SHOW_URL && win->url != NULL && !_webui_is_empty(content)) {
@@ -11084,6 +11141,7 @@ static WEBUI_THREAD_SERVER_START {
1108411141 // and stays listening until this thread exits.
1108511142 struct mg_context * http_ctx = NULL;
1108611143 char* server_port = NULL;
11144+ bool resume_after_reload = false;
1108711145
1108811146 // This thread is persistent: it parks until `webui_show()` requests a
1108911147 // serve cycle, serves until the window gets closed, then parks again.
@@ -11092,25 +11150,26 @@ static WEBUI_THREAD_SERVER_START {
1109211150
1109311151 // Park: wait for a serve request, a client
1109411152 // re-connection, or a reload request
11095- if (_webui_mutex_app_is_exit_now(WEBUI_MUTEX_GET_STATUS) || _webui_mutex_win_is_exit_now( win, WEBUI_MUTEX_GET_STATUS ))
11153+ if (_webui_window_thread_end( win))
1109611154 break;
1109711155
1109811156 // Reload: restart the web server of this window using the
1109911157 // current settings. Requested by setters like `webui_set_port()`.
11100- // The requester closes this window's clients, so the reload runs
11101- // once the serve cycle has ended and the clients are gone.
11158+ // The reload request also ended the serve cycle, so we get here
11159+ // immediately. The reload flag stays set until the server is
11160+ // listening again, so `webui_show()` waits for the new port.
1110211161 bool rebind_only = false;
11103- if (_webui_mutex_is_reload_requested(win, WEBUI_MUTEX_GET_STATUS) &&
11104- !_webui_mutex_is_connected(win, WEBUI_MUTEX_GET_STATUS)) {
11105-
11106- _webui_mutex_is_reload_requested(win, WEBUI_MUTEX_SET_FALSE);
11162+ if (_webui_mutex_is_reload_requested(win, WEBUI_MUTEX_GET_STATUS)) {
1110711163
1110811164 if (http_ctx != NULL) {
1110911165
1111011166 #ifdef WEBUI_LOG
1111111167 _webui_log_debug("[Core]\t\t_webui_server_thread([%zu]) -> Reloading server...\n", win->num);
1111211168 #endif
1111311169
11170+ // Any remaining client is bound to the old server
11171+ _webui_mutex_is_connected(win, WEBUI_MUTEX_SET_FALSE);
11172+
1111411173 // Wait for this window's in-flight event tasks, then
1111511174 // stop the server services
1111611175 _webui_window_wait_for_tasks(win);
@@ -11136,6 +11195,10 @@ static WEBUI_THREAD_SERVER_START {
1113611195 // Restart listening right away, then park again
1113711196 rebind_only = true;
1113811197 }
11198+ else {
11199+ // Nothing to reload, the server was never started
11200+ _webui_mutex_is_reload_requested(win, WEBUI_MUTEX_SET_FALSE);
11201+ }
1113911202 }
1114011203
1114111204 if (!rebind_only) {
@@ -11225,6 +11288,9 @@ static WEBUI_THREAD_SERVER_START {
1122511288 win->server_port = 0;
1122611289 _webui_free_mem((void*)server_port);
1122711290 server_port = NULL;
11291+
11292+ // A failed reload is still a finished reload
11293+ _webui_mutex_is_reload_requested(win, WEBUI_MUTEX_SET_FALSE);
1122811294 continue;
1122911295 }
1123011296
@@ -11246,8 +11312,17 @@ static WEBUI_THREAD_SERVER_START {
1124611312 }
1124711313
1124811314 if (rebind_only) {
11315+
1124911316 // The reload is done. The server is listening
1125011317 // again, using the new settings
11318+ _webui_mutex_is_reload_requested(win, WEBUI_MUTEX_SET_FALSE);
11319+
11320+ // Resume serving if this window was serving when the reload
11321+ // was requested, so it stays active for `webui_wait()`
11322+ if (resume_after_reload) {
11323+ resume_after_reload = false;
11324+ _webui_mutex_is_start_requested(win, WEBUI_MUTEX_SET_TRUE);
11325+ }
1125111326 continue;
1125211327 }
1125311328
@@ -11291,7 +11366,7 @@ static WEBUI_THREAD_SERVER_START {
1129111366 _webui_sleep(1);
1129211367
1129311368 // Stop if we get exit signal
11294- if (_webui_mutex_app_is_exit_now(WEBUI_MUTEX_GET_STATUS) || _webui_mutex_win_is_exit_now( win, WEBUI_MUTEX_GET_STATUS )) {
11369+ if (_webui_window_cycle_end( win)) {
1129511370 stop = true;
1129611371 break;
1129711372 }
@@ -11328,7 +11403,7 @@ static WEBUI_THREAD_SERVER_START {
1132811403 for (;;) {
1132911404
1133011405 // Stop if we get exit signal
11331- if (_webui_mutex_app_is_exit_now(WEBUI_MUTEX_GET_STATUS) || _webui_mutex_win_is_exit_now( win, WEBUI_MUTEX_GET_STATUS )) {
11406+ if (_webui_window_cycle_end( win)) {
1133211407 stop = true;
1133311408 break;
1133411409 }
@@ -11380,7 +11455,7 @@ static WEBUI_THREAD_SERVER_START {
1138011455 _webui_sleep(1);
1138111456
1138211457 // Exit signal
11383- if (_webui_mutex_app_is_exit_now(WEBUI_MUTEX_GET_STATUS) || _webui_mutex_win_is_exit_now( win, WEBUI_MUTEX_GET_STATUS )) {
11458+ if (_webui_window_cycle_end( win)) {
1138411459 stop = true;
1138511460 break;
1138611461 }
@@ -11416,7 +11491,7 @@ static WEBUI_THREAD_SERVER_START {
1141611491 for (;;) {
1141711492
1141811493 // Stop if we get exit signal
11419- if (_webui_mutex_app_is_exit_now(WEBUI_MUTEX_GET_STATUS) || _webui_mutex_win_is_exit_now( win, WEBUI_MUTEX_GET_STATUS )) {
11494+ if (_webui_window_cycle_end( win)) {
1142011495 stop = true;
1142111496 break;
1142211497 }
@@ -11481,7 +11556,7 @@ static WEBUI_THREAD_SERVER_START {
1148111556 // Wait forever
1148211557 for (;;) {
1148311558 _webui_sleep(1);
11484- if (_webui_mutex_app_is_exit_now(WEBUI_MUTEX_GET_STATUS) || _webui_mutex_win_is_exit_now( win, WEBUI_MUTEX_GET_STATUS ))
11559+ if (_webui_window_cycle_end( win))
1148511560 break;
1148611561 }
1148711562 }
@@ -11495,6 +11570,11 @@ static WEBUI_THREAD_SERVER_START {
1149511570 _webui_mutex_is_connected(win, WEBUI_MUTEX_SET_FALSE);
1149611571 _webui_window_close_ui(win);
1149711572
11573+ // A reload ended this cycle. Serving resumes once
11574+ // the server is listening again.
11575+ if (_webui_mutex_is_reload_requested(win, WEBUI_MUTEX_GET_STATUS))
11576+ resume_after_reload = true;
11577+
1149811578 // Make window reusable, so user can
1149911579 // call `webui_show()` again if needed.
1150011580 _webui_make_window_reusable(win);
0 commit comments