@@ -458,6 +458,7 @@ typedef struct _webui_window_t {
458458 bool thread_running; // Persistent server thread is alive
459459 bool server_start_requested; // `webui_show()` requested a serve cycle
460460 bool destroy_requested; // Deferred destroy from a callback of this window
461+ bool reload_requested; // A setter changed the web server settings
461462 size_t tasks; // In-flight event tasks using this window
462463 // WebView
463464 bool allow_webview;
@@ -523,6 +524,7 @@ typedef struct _webui_core_t {
523524 webui_mutex_t mutex_win_connect;
524525 webui_mutex_t mutex_app_exit_now;
525526 webui_mutex_t mutex_is_more_servers;
527+ webui_mutex_t mutex_allocator; // Window ID and port allocators
526528 webui_mutex_t mutex_http_handler;
527529 webui_mutex_t mutex_client;
528530 webui_mutex_t mutex_async_response;
@@ -676,6 +678,8 @@ static bool _webui_mutex_is_webview_update(_webui_window_t* win, int update);
676678static bool _webui_mutex_is_server_running(_webui_window_t* win, int update);
677679static bool _webui_mutex_is_thread_running(_webui_window_t* win, int update);
678680static bool _webui_mutex_is_start_requested(_webui_window_t* win, int update);
681+ static bool _webui_mutex_is_reload_requested(_webui_window_t* win, int update);
682+ static void _webui_window_request_reload(_webui_window_t* win);
679683static _webui_window_t* _webui_dereference_win_num(size_t num);
680684static void _webui_start_server_thread(_webui_window_t* win);
681685static void _webui_window_close_ui(_webui_window_t* win);
@@ -1261,7 +1265,9 @@ size_t webui_new_window_id(size_t num) {
12611265
12621266 // Create a new window
12631267 _webui_window_t* win = (_webui_window_t* ) _webui_malloc(sizeof(_webui_window_t));
1268+ _webui_mutex_lock(&_webui.mutex_allocator);
12641269 _webui.wins[num] = win;
1270+ _webui_mutex_unlock(&_webui.mutex_allocator);
12651271
12661272 // Mutex Initialisation
12671273 _webui_mutex_init(&win->mutex_win_exit_now);
@@ -1317,12 +1323,15 @@ size_t webui_get_new_window_id(void) {
13171323 if (_webui_mutex_app_is_exit_now(WEBUI_MUTEX_GET_STATUS))
13181324 return 0;
13191325
1326+ _webui_mutex_lock(&_webui.mutex_allocator);
13201327 for (size_t i = 1; i < WEBUI_MAX_IDS; i++) {
13211328 if (_webui.wins[i] == NULL && !_webui.wins_reserved[i]) {
13221329 _webui.wins_reserved[i] = true;
1330+ _webui_mutex_unlock(&_webui.mutex_allocator);
13231331 return i;
13241332 }
13251333 }
1334+ _webui_mutex_unlock(&_webui.mutex_allocator);
13261335
13271336 // We should never reach here
13281337 WEBUI_ASSERT("webui_get_new_window_id() failed");
@@ -1622,10 +1631,12 @@ void webui_destroy(size_t window) {
16221631 // and its buffers stay allocated until `webui_clean()`, because a
16231632 // detached event task, or a WebView thread, may still hold pointers
16241633 // to them.
1634+ _webui_mutex_lock(&_webui.mutex_allocator);
16251635 if (_webui.wins[window] == win) {
16261636 _webui.wins[window] = NULL;
16271637 _webui.wins_reserved[window] = false;
16281638 }
1639+ _webui_mutex_unlock(&_webui.mutex_allocator);
16291640}
16301641
16311642bool webui_is_shown(size_t window) {
@@ -3124,6 +3135,12 @@ bool webui_set_port(size_t window, size_t port) {
31243135 return false;
31253136
31263137 win->custom_server_port = port;
3138+
3139+ if (_webui_mutex_is_server_running(win, WEBUI_MUTEX_GET_STATUS)) {
3140+ // The server is already listening: reload
3141+ // it, so the new port takes effect
3142+ _webui_window_request_reload(win);
3143+ }
31273144 return true;
31283145}
31293146
@@ -3636,7 +3653,14 @@ void webui_set_public(size_t window, bool status) {
36363653 if (win == NULL)
36373654 return;
36383655
3639- win -> is_public = status ;
3656+ if (win->is_public != status) {
3657+ win->is_public = status;
3658+ if (_webui_mutex_is_server_running(win, WEBUI_MUTEX_GET_STATUS)) {
3659+ // The server is already listening: reload it,
3660+ // so the new host binding takes effect
3661+ _webui_window_request_reload(win);
3662+ }
3663+ }
36403664}
36413665
36423666void webui_send_raw_client(webui_event_t* e, const char* function, const void* raw, size_t size) {
@@ -4323,8 +4347,7 @@ bool webui_set_root_folder(size_t window, const char* path) {
43234347 }
43244348 #endif
43254349
4326- if (_webui_mutex_is_server_running (win , WEBUI_MUTEX_GET_STATUS ) ||
4327- _webui_is_empty (path ) ||
4350+ if (_webui_is_empty(path) ||
43284351 (_webui_strlen(path) > WEBUI_MAX_PATH) ||
43294352 !_webui_folder_exist(path)) {
43304353
@@ -4338,6 +4361,9 @@ bool webui_set_root_folder(size_t window, const char* path) {
43384361 #ifdef WEBUI_LOG
43394362 _webui_log_info("[User] webui_set_root_folder() -> Success\n");
43404363 #endif
4364+ // The HTTP handler and the file serving logic read this path on
4365+ // every request, so a running window server picks it up live,
4366+ // without any reload
43414367 WEBUI_SN_PRINTF_DYN(win->server_root_path, WEBUI_MAX_PATH, "%s", path);
43424368 return true;
43434369}
@@ -6505,6 +6531,32 @@ static bool _webui_mutex_is_start_requested(_webui_window_t* win, int update) {
65056531 return status;
65066532}
65076533
6534+ static bool _webui_mutex_is_reload_requested(_webui_window_t* win, int update) {
6535+
6536+ bool status = false;
6537+ _webui_mutex_lock(&win->mutex_win_thread);
6538+ if (update == WEBUI_MUTEX_SET_TRUE) win->reload_requested = true;
6539+ else if (update == WEBUI_MUTEX_SET_FALSE) win->reload_requested = false;
6540+ status = win->reload_requested;
6541+ _webui_mutex_unlock(&win->mutex_win_thread);
6542+ return status;
6543+ }
6544+
6545+ static void _webui_window_request_reload(_webui_window_t* win) {
6546+
6547+ // 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.
6550+
6551+ #ifdef WEBUI_LOG
6552+ _webui_log_debug("[Core]\t\t_webui_window_request_reload([%zu])\n", win->num);
6553+ #endif
6554+
6555+ _webui_mutex_is_reload_requested(win, WEBUI_MUTEX_SET_TRUE);
6556+ if (_webui_mutex_is_connected(win, WEBUI_MUTEX_GET_STATUS))
6557+ webui_close(win->num);
6558+ }
6559+
65086560static void _webui_window_task_count(_webui_window_t* win, int delta) {
65096561
65106562 _webui_mutex_lock(&win->mutex_win_thread);
@@ -8052,6 +8104,7 @@ static void _webui_clean(void) {
80528104 _webui_mutex_destroy(&_webui.mutex_token);
80538105 _webui_mutex_destroy(&_webui.mutex_js_run_id);
80548106 _webui_mutex_destroy(&_webui.mutex_ws_process_number);
8107+ _webui_mutex_destroy(&_webui.mutex_allocator);
80558108 _webui_condition_destroy(&_webui.condition_wait);
80568109
80578110 _webui.initialized = false;
@@ -9939,12 +9992,14 @@ static void _webui_free_port(size_t port) {
99399992 if (port == 0)
99409993 return;
99419994
9995+ _webui_mutex_lock(&_webui.mutex_allocator);
99429996 for (size_t i = 0; i < WEBUI_MAX_IDS; i++) {
99439997 if (_webui.used_ports[i] == port) {
99449998 _webui.used_ports[i] = 0;
99459999 break;
994610000 }
994710001 }
10002+ _webui_mutex_unlock(&_webui.mutex_allocator);
994810003}
994910004
995010005static size_t _webui_get_free_port(void) {
@@ -9953,6 +10008,8 @@ static size_t _webui_get_free_port(void) {
995310008 _webui_log_debug("[Core]\t\t_webui_get_free_port()\n");
995410009 #endif
995510010
10011+ _webui_mutex_lock(&_webui.mutex_allocator);
10012+
995610013 size_t port = (rand() % (WEBUI_MAX_PORT + 1 - WEBUI_MIN_PORT)) + WEBUI_MIN_PORT;
995710014
995810015 for (size_t i = WEBUI_MIN_PORT; i <= WEBUI_MAX_PORT; i++) {
@@ -9987,6 +10044,7 @@ static size_t _webui_get_free_port(void) {
998710044 }
998810045 }
998910046
10047+ _webui_mutex_unlock(&_webui.mutex_allocator);
999010048 return port;
999110049}
999210050
@@ -10024,6 +10082,7 @@ static void _webui_init(void) {
1002410082 _webui_mutex_init(&_webui.mutex_token);
1002510083 _webui_mutex_init(&_webui.mutex_js_run_id);
1002610084 _webui_mutex_init(&_webui.mutex_ws_process_number);
10085+ _webui_mutex_init(&_webui.mutex_allocator);
1002710086 _webui_condition_init(&_webui.condition_wait);
1002810087
1002910088 // Random
@@ -11031,15 +11090,62 @@ static WEBUI_THREAD_SERVER_START {
1103111090 // It exits only when the window is destroyed or the app is exiting.
1103211091 for (;;) {
1103311092
11034- // Park: wait for a serve request, or a client re-connection
11093+ // Park: wait for a serve request, a client
11094+ // re-connection, or a reload request
1103511095 if (_webui_mutex_app_is_exit_now(WEBUI_MUTEX_GET_STATUS) || _webui_mutex_win_is_exit_now(win, WEBUI_MUTEX_GET_STATUS))
1103611096 break;
11037- if (!_webui_mutex_is_start_requested (win , WEBUI_MUTEX_GET_STATUS ) &&
11038- !(http_ctx && _webui_mutex_is_connected (win , WEBUI_MUTEX_GET_STATUS ))) {
11039- _webui_sleep (1 );
11040- continue ;
11097+
11098+ // Reload: restart the web server of this window using the
11099+ // 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.
11102+ 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);
11107+
11108+ if (http_ctx != NULL) {
11109+
11110+ #ifdef WEBUI_LOG
11111+ _webui_log_debug("[Core]\t\t_webui_server_thread([%zu]) -> Reloading server...\n", win->num);
11112+ #endif
11113+
11114+ // Wait for this window's in-flight event tasks, then
11115+ // stop the server services
11116+ _webui_window_wait_for_tasks(win);
11117+ mg_stop(http_ctx);
11118+ http_ctx = NULL;
11119+ _webui_mutex_is_server_running(win, WEBUI_MUTEX_SET_FALSE);
11120+
11121+ // Apply the port settings
11122+ size_t new_port = (win->custom_server_port > 0 ? win->custom_server_port : win->server_port);
11123+ if (new_port != win->server_port) {
11124+ _webui_free_port(win->server_port);
11125+ win->server_port = new_port;
11126+ }
11127+ _webui_free_mem((void*)server_port);
11128+ server_port = NULL;
11129+
11130+ // Regenerate the URL
11131+ char* old_url = win->url;
11132+ win->url = (char*)_webui_malloc(32); // [http][domain][port]
11133+ WEBUI_SN_PRINTF_DYN(win->url, 32, WEBUI_HTTP_PROTOCOL "localhost:%zu", win->server_port);
11134+ _webui_free_mem((void*)old_url);
11135+
11136+ // Restart listening right away, then park again
11137+ rebind_only = true;
11138+ }
11139+ }
11140+
11141+ if (!rebind_only) {
11142+ if (!_webui_mutex_is_start_requested(win, WEBUI_MUTEX_GET_STATUS) &&
11143+ !(http_ctx && _webui_mutex_is_connected(win, WEBUI_MUTEX_GET_STATUS))) {
11144+ _webui_sleep(1);
11145+ continue;
11146+ }
11147+ _webui_mutex_is_start_requested(win, WEBUI_MUTEX_SET_FALSE);
1104111148 }
11042- _webui_mutex_is_start_requested (win , WEBUI_MUTEX_SET_FALSE );
1104311149
1104411150 // Initialization
1104511151 if (_webui.startup_timeout < 1)
@@ -11139,6 +11245,12 @@ static WEBUI_THREAD_SERVER_START {
1113911245 #endif
1114011246 }
1114111247
11248+ if (rebind_only) {
11249+ // The reload is done. The server is listening
11250+ // again, using the new settings
11251+ continue;
11252+ }
11253+
1114211254 // This window is now active. `wait()` waits for it
1114311255 _webui_servers_count(+1);
1114411256
@@ -11458,10 +11570,12 @@ static WEBUI_THREAD_SERVER_START {
1145811570 bool destroy_requested = win->destroy_requested;
1145911571 _webui_mutex_unlock(&win->mutex_win_thread);
1146011572 if (destroy_requested) {
11573+ _webui_mutex_lock(&_webui.mutex_allocator);
1146111574 if (_webui.wins[win->num] == win) {
1146211575 _webui.wins[win->num] = NULL;
1146311576 _webui.wins_reserved[win->num] = false;
1146411577 }
11578+ _webui_mutex_unlock(&_webui.mutex_allocator);
1146511579 }
1146611580
1146711581 // Let `webui_destroy()` and `webui_clean()` know
0 commit comments