Skip to content

Commit d44e1d6

Browse files
committed
Core - Adding mutex_allocator (port reload)
1 parent a1f7e8b commit d44e1d6

1 file changed

Lines changed: 123 additions & 9 deletions

File tree

src/webui.c

Lines changed: 123 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -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);
676678
static bool _webui_mutex_is_server_running(_webui_window_t* win, int update);
677679
static bool _webui_mutex_is_thread_running(_webui_window_t* win, int update);
678680
static 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);
679683
static _webui_window_t* _webui_dereference_win_num(size_t num);
680684
static void _webui_start_server_thread(_webui_window_t* win);
681685
static 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

16311642
bool 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

36423666
void 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+
65086560
static 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

995010005
static 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

Comments
 (0)