Skip to content

Commit 0872f87

Browse files
committed
feat: Enhance daemon watch functionality with client management and status reporting
1 parent 30b9e2b commit 0872f87

1 file changed

Lines changed: 110 additions & 28 deletions

File tree

src_v2/build/test_daemon_runtime.c

Lines changed: 110 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ static bool daemon_spawn_worker(Test_Daemon_Server *server,
159159
uint64_t request_id);
160160
static bool daemon_finish_active_run(Test_Daemon_Server *server);
161161
static void daemon_handle_client_disconnect(Test_Daemon_Server *server);
162+
static bool daemon_watch_send_infof(Test_Daemon_Server *server, const char *fmt, ...);
162163
static void daemon_watch_reset_session(Test_Daemon_Server *server);
163164
static bool daemon_begin_worker_cancel(Test_Daemon_Server *server,
164165
bool mark_watch_cancel,
@@ -252,6 +253,10 @@ static bool daemon_flush_buffered_worker_output(Test_Daemon_Server *server) {
252253
bool ok = true;
253254

254255
if (!server) return false;
256+
if (server->watch.active && server->current_client_fd < 0) {
257+
daemon_clear_buffered_worker_output(server);
258+
return true;
259+
}
255260
if (server->buffered_stderr.count > 0) {
256261
ok = test_daemon_send_message(server->current_client_fd,
257262
TEST_DAEMON_MESSAGE_STDERR,
@@ -551,16 +556,42 @@ static void daemon_remove_state_files(Test_Daemon_Server *server, bool record_ex
551556
}
552557
}
553558

559+
static bool daemon_watch_has_attached_client(const Test_Daemon_Server *server) {
560+
return server && server->watch.active && server->current_client_fd >= 0;
561+
}
562+
563+
static const char *daemon_watch_status_summary(const Test_Daemon_Server *server) {
564+
if (!server) return "watching";
565+
return daemon_watch_has_attached_client(server) ? "watching (attached)" : "watching (detached)";
566+
}
567+
568+
static void daemon_watch_detach_client(Test_Daemon_Server *server, const char *reason) {
569+
if (!server || !server->watch.active) return;
570+
server->watch.client_source = sd_event_source_unref(server->watch.client_source);
571+
daemon_close_fd(&server->current_client_fd);
572+
if (reason && reason[0] != '\0') {
573+
daemon_set_status_detail(server, "%s", reason);
574+
} else {
575+
daemon_set_status_detail(server, "watch session detached from its originating client");
576+
}
577+
}
578+
554579
static bool daemon_watch_send_text(Test_Daemon_Server *server, Test_Daemon_Message_Type type, const char *text) {
555580
uint64_t request_id = 0;
556581

557-
if (!server || server->current_client_fd < 0 || !text) return false;
582+
if (!server || !text) return false;
583+
if (server->watch.active && server->current_client_fd < 0) return true;
584+
if (server->current_client_fd < 0) return false;
558585
request_id = server->watch.active ? server->watch.request_id : server->current_request_id;
559586
if (!test_daemon_send_message(server->current_client_fd,
560587
type,
561588
request_id,
562589
text,
563590
(uint32_t)strlen(text))) {
591+
if (server->watch.active) {
592+
daemon_watch_detach_client(server, "watch session detached after client connection closed");
593+
return true;
594+
}
564595
daemon_handle_client_disconnect(server);
565596
return false;
566597
}
@@ -569,16 +600,31 @@ static bool daemon_watch_send_text(Test_Daemon_Server *server, Test_Daemon_Messa
569600

570601
static void daemon_handle_client_disconnect(Test_Daemon_Server *server) {
571602
if (!server) return;
572-
daemon_close_fd(&server->current_client_fd);
573603
if (server->watch.active) {
574-
if (server->worker_pid > 0) {
575-
(void)daemon_begin_worker_cancel(server, true, true);
576-
} else {
577-
daemon_watch_reset_session(server);
578-
}
604+
daemon_watch_detach_client(server, "watch session detached after client disconnect");
605+
return;
606+
}
607+
daemon_close_fd(&server->current_client_fd);
608+
if (server->worker_pid > 0) {
609+
daemon_set_status_detail(server, "foreground client disconnected while a run was active");
579610
}
580611
}
581612

613+
static bool daemon_watch_report_issue(Test_Daemon_Server *server, const char *fmt, ...) {
614+
char buffer[TEST_RUNNER_SUMMARY_CAPACITY * 2] = {0};
615+
va_list args;
616+
int n = 0;
617+
618+
if (!server || !fmt) return false;
619+
va_start(args, fmt);
620+
n = vsnprintf(buffer, sizeof(buffer), fmt, args);
621+
va_end(args);
622+
if (n < 0 || (size_t)n >= sizeof(buffer)) return false;
623+
daemon_set_status_detail(server, "%s", buffer);
624+
nob_log(NOB_ERROR, "[watch] %s", buffer);
625+
return daemon_watch_send_infof(server, "[watch] %s\n", buffer);
626+
}
627+
582628
static bool daemon_watch_send_infof(Test_Daemon_Server *server, const char *fmt, ...) {
583629
char buffer[TEST_RUNNER_SUMMARY_CAPACITY * 2] = {0};
584630
va_list args;
@@ -1352,24 +1398,23 @@ static bool daemon_watch_start_next_module(Test_Daemon_Server *server) {
13521398

13531399
daemon_reset_request_telemetry(server);
13541400
if (!daemon_ensure_preflight(server, request.profile_id)) {
1355-
return daemon_send_structured_error(server,
1356-
server->current_client_fd,
1357-
server->watch.request_id,
1358-
TEST_DAEMON_ERROR_PREFLIGHT_FAILED,
1359-
"daemon preflight failed");
1401+
daemon_watch_clear_batch(server);
1402+
return daemon_watch_report_issue(server, "daemon preflight failed; watch session remains active");
13601403
}
1404+
daemon_set_status_detail(server,
1405+
"%s running %s (%s)",
1406+
daemon_watch_has_attached_client(server) ? "watch session" : "detached watch session",
1407+
module->name,
1408+
profile->name);
13611409
if (!daemon_watch_send_infof(server,
13621410
"[watch] running %s (%s)\n",
13631411
module->name,
13641412
profile->name)) {
13651413
return false;
13661414
}
13671415
if (!daemon_spawn_worker(server, &request, server->watch.request_id)) {
1368-
return daemon_send_structured_error(server,
1369-
server->current_client_fd,
1370-
server->watch.request_id,
1371-
TEST_DAEMON_ERROR_WORKER_START_FAILED,
1372-
"watch failed to start worker");
1416+
daemon_watch_clear_batch(server);
1417+
return daemon_watch_report_issue(server, "watch failed to start worker; session remains active");
13731418
}
13741419
return true;
13751420
}
@@ -1385,12 +1430,7 @@ static int daemon_on_watch_client(sd_event_source *source,
13851430
if (!(revents & (EPOLLHUP | EPOLLERR | EPOLLRDHUP))) return 0;
13861431
if (!server || !server->watch.active) return 0;
13871432

1388-
if (server->worker_pid > 0) {
1389-
(void)daemon_begin_worker_cancel(server, true, true);
1390-
} else {
1391-
daemon_watch_reset_session(server);
1392-
if (server->shutting_down) (void)sd_event_exit(server->event, 0);
1393-
}
1433+
daemon_watch_detach_client(server, "watch session detached after client connection closed");
13941434
return 0;
13951435
}
13961436

@@ -1408,6 +1448,9 @@ static int daemon_on_watch_debounce(sd_event_source *source,
14081448

14091449
if (server->worker_pid > 0) {
14101450
server->watch.rerun_pending = true;
1451+
daemon_set_status_detail(server,
1452+
"%s queued rerun after newer changes",
1453+
daemon_watch_has_attached_client(server) ? "watch session" : "detached watch session");
14111454
if (!server->watch.cancel_requested) {
14121455
(void)daemon_watch_send_infof(server, "[watch] canceling stale run after newer changes\n");
14131456
}
@@ -1530,6 +1573,7 @@ static bool daemon_watch_start_session(Test_Daemon_Server *server,
15301573
server->current_worker_request_valid = false;
15311574
server->current_policy = TEST_DAEMON_POLICY_WATCH_REPLACE_RUNNING;
15321575
server->state = TEST_DAEMON_STATE_WATCHING;
1576+
daemon_set_status_detail(server, "watch session starting");
15331577

15341578
if (!daemon_watch_register_roots(server)) goto defer;
15351579
if (sd_event_add_io(server->event,
@@ -1539,17 +1583,31 @@ static bool daemon_watch_start_session(Test_Daemon_Server *server,
15391583
daemon_on_watch_client,
15401584
server) < 0) {
15411585
nob_log(NOB_ERROR, "daemon failed to watch active client connection");
1542-
goto defer;
1586+
daemon_watch_detach_client(server, "watch session detached because client monitoring could not be installed");
15431587
}
15441588

1545-
if (!test_daemon_send_message(fd, TEST_DAEMON_MESSAGE_ACK, message->header.request_id, NULL, 0)) goto defer;
1589+
if (server->current_client_fd >= 0 &&
1590+
!test_daemon_send_message(server->current_client_fd,
1591+
TEST_DAEMON_MESSAGE_ACK,
1592+
message->header.request_id,
1593+
NULL,
1594+
0)) {
1595+
daemon_watch_detach_client(server, "watch session detached before the startup ACK completed");
1596+
}
15461597
if (request.mode == TEST_RUNNER_WATCH_MODE_AUTO) {
1598+
daemon_set_status_detail(server,
1599+
"watch session started for auto routing (%s)",
1600+
daemon_watch_has_attached_client(server) ? "attached" : "detached");
15471601
if (!daemon_watch_send_infof(server, "[watch] auto session started (%zu roots)\n", server->watch.roots.count)) {
15481602
goto defer;
15491603
}
15501604
} else {
15511605
const Test_Runner_Module_Def *module = test_runner_get_module_def(request.module_id);
15521606
if (!module) goto defer;
1607+
daemon_set_status_detail(server,
1608+
"watch session started for %s (%s)",
1609+
module->name,
1610+
daemon_watch_has_attached_client(server) ? "attached" : "detached");
15531611
if (!daemon_watch_send_infof(server,
15541612
"[watch] module session started for %s (%zu roots)\n",
15551613
module->name,
@@ -1648,17 +1706,22 @@ static bool daemon_finish_active_run(Test_Daemon_Server *server) {
16481706
bool ok = false;
16491707
bool watch_active = false;
16501708
bool close_client_fd = false;
1709+
bool have_attached_client = false;
16511710

1652-
if (!server || server->current_client_fd < 0) return false;
1711+
if (!server) return false;
16531712
if (!server->worker_exited || !server->stdout_closed || !server->stderr_closed) return true;
16541713
watch_active = server->watch.active;
1714+
have_attached_client = server->current_client_fd >= 0;
16551715

16561716
if (watch_active && server->watch.cancel_requested) {
16571717
if (!daemon_watch_send_infof(server, "[watch] stale run aborted after newer changes\n")) {
16581718
daemon_watch_reset_session(server);
16591719
return false;
16601720
}
16611721
if (server->worker_result_path[0] != '\0') (void)unlink(server->worker_result_path);
1722+
daemon_set_status_detail(server,
1723+
"%s canceled a stale run after newer changes",
1724+
daemon_watch_has_attached_client(server) ? "watch session" : "detached watch session");
16621725
server->watch.cancel_requested = false;
16631726
close_client_fd = server->watch.closing;
16641727
daemon_reset_worker_state(server, close_client_fd);
@@ -1702,6 +1765,17 @@ static bool daemon_finish_active_run(Test_Daemon_Server *server) {
17021765
server->last_result = runner_result;
17031766
server->last_result_valid = true;
17041767
server->last_telemetry = server->current_telemetry;
1768+
if (watch_active) {
1769+
daemon_set_status_detail(server,
1770+
"%s last result: %s",
1771+
daemon_watch_has_attached_client(server) ? "watch session" : "detached watch session",
1772+
runner_result.summary[0] != '\0' ? runner_result.summary : (runner_result.ok ? "PASS" : "FAIL"));
1773+
} else if (!have_attached_client) {
1774+
daemon_set_status_detail(server,
1775+
"foreground run completed after client disconnect: %s",
1776+
runner_result.summary[0] != '\0' ? runner_result.summary
1777+
: (runner_result.ok ? "PASS" : "FAIL"));
1778+
}
17051779

17061780
if (!test_daemon_result_from_runner(daemon_effective_state(server),
17071781
getpid(),
@@ -1714,7 +1788,14 @@ static bool daemon_finish_active_run(Test_Daemon_Server *server) {
17141788
payload.cache_launcher_reason = (uint32_t)server->current_telemetry.launcher_cache_reason;
17151789
payload.cache_preflight_reason = (uint32_t)server->current_telemetry.preflight_reason;
17161790
(void)daemon_copy_string(payload.detail, sizeof(payload.detail), server->status_detail);
1717-
ok = test_daemon_send_result(server->current_client_fd, server->current_request_id, &payload);
1791+
if (!have_attached_client) {
1792+
ok = true;
1793+
} else if (!test_daemon_send_result(server->current_client_fd, server->current_request_id, &payload)) {
1794+
daemon_handle_client_disconnect(server);
1795+
ok = true;
1796+
} else {
1797+
ok = true;
1798+
}
17181799
}
17191800

17201801
if (server->worker_result_path[0] != '\0') (void)unlink(server->worker_result_path);
@@ -2017,7 +2098,8 @@ static bool daemon_handle_control(Test_Daemon_Server *server,
20172098
return daemon_send_status_result(fd,
20182099
request_id,
20192100
server,
2020-
test_daemon_state_name(state));
2101+
server->watch.active ? daemon_watch_status_summary(server)
2102+
: test_daemon_state_name(state));
20212103

20222104
case TEST_DAEMON_CONTROL_STOP:
20232105
if (server->shutting_down) {

0 commit comments

Comments
 (0)