mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-04 10:48:20 +00:00
Merge pull request #2409 from DeusData/test/daemon-ipc-startup-gate-no-timeout
test(daemon-ipc): wait for the startup gate event, not an attempt budget
This commit is contained in:
1 file changed
+48
-20
+48
-20
@@ -401,6 +401,7 @@ static int ipc_test_win_lock_child(const char *kind, const char *key, const char
|
||||
typedef struct {
|
||||
const cbm_daemon_ipc_endpoint_t *endpoint;
|
||||
int result;
|
||||
atomic_bool finished; /* set once the startup call has fully returned */
|
||||
} ipc_test_win_startup_call_t;
|
||||
|
||||
typedef struct {
|
||||
@@ -421,16 +422,22 @@ static void ipc_test_startup_gate(void *context) {
|
||||
}
|
||||
}
|
||||
|
||||
static bool ipc_test_startup_gate_wait_acquired(ipc_test_startup_gate_t *gate) {
|
||||
/* `acquired` never clears, so this wait cannot miss the event; the bound
|
||||
* only catches a thread that never started at all. */
|
||||
for (size_t attempt = 0; attempt < 50000U; attempt++) {
|
||||
if (atomic_load(&gate->acquired)) {
|
||||
return true;
|
||||
}
|
||||
/* Waits until the startup thread has either parked in the gate or returned
|
||||
* without reaching it. Both flags only ever go false -> true, and one of them
|
||||
* is always eventually set: before the gate the startup path only try-acquires
|
||||
* (no blocking wait on anything this test holds), and a thread parked in the
|
||||
* gate cannot return until the test releases it. So no attempt or time bound
|
||||
* is needed, and none may decide the verdict: the previous 50000 x
|
||||
* cbm_usleep(200) budget meant ~10 s on POSIX but, since cbm_usleep(200) is
|
||||
* Sleep(0) on Windows, only 50000 bare yields there -- a loaded CI runner
|
||||
* exhausted them before the thread reached the gate. A thread that never
|
||||
* started is caught by the caller's thread_started check. */
|
||||
static bool ipc_test_startup_gate_wait_acquired(ipc_test_startup_gate_t *gate,
|
||||
ipc_test_win_startup_call_t *call) {
|
||||
while (!atomic_load(&gate->acquired) && !atomic_load(&call->finished)) {
|
||||
cbm_usleep(200);
|
||||
}
|
||||
return false;
|
||||
return atomic_load(&gate->acquired);
|
||||
}
|
||||
|
||||
static void *ipc_test_win_startup_call(void *opaque) {
|
||||
@@ -441,6 +448,7 @@ static void *ipc_test_win_startup_call(void *opaque) {
|
||||
call->result = 0;
|
||||
}
|
||||
cbm_daemon_ipc_startup_lock_release(&startup);
|
||||
atomic_store(&call->finished, true);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -1395,6 +1403,7 @@ TEST(daemon_ipc_windows_startup_retries_transient_rendezvous_reader) {
|
||||
bool startup_observed = false;
|
||||
int join_status = -1;
|
||||
ipc_test_win_startup_call_t call = {.result = -1};
|
||||
atomic_init(&call.finished, false);
|
||||
ipc_test_startup_gate_t gate;
|
||||
atomic_init(&gate.acquired, false);
|
||||
atomic_init(&gate.may_proceed, false);
|
||||
@@ -1420,7 +1429,7 @@ TEST(daemon_ipc_windows_startup_retries_transient_rendezvous_reader) {
|
||||
thread_started = cbm_thread_create(&thread, 0, ipc_test_win_startup_call, &call) == 0;
|
||||
}
|
||||
if (thread_started) {
|
||||
startup_observed = ipc_test_startup_gate_wait_acquired(&gate);
|
||||
startup_observed = ipc_test_startup_gate_wait_acquired(&gate, &call);
|
||||
}
|
||||
ipc_test_win_lock_release(&record_reader);
|
||||
atomic_store(&gate.may_proceed, true);
|
||||
@@ -1441,7 +1450,14 @@ TEST(daemon_ipc_windows_startup_retries_transient_rendezvous_reader) {
|
||||
ASSERT_EQ(directory_status, CBM_PRIVATE_FILE_LOCK_OK);
|
||||
ASSERT_EQ(record_status, CBM_PRIVATE_FILE_LOCK_OK);
|
||||
ASSERT_TRUE(thread_started);
|
||||
ASSERT_TRUE(startup_observed);
|
||||
if (!startup_observed) {
|
||||
/* The startup thread returned without reaching the gate; its
|
||||
* try-acquire status is the true reason. */
|
||||
char reason[96];
|
||||
(void)snprintf(reason, sizeof(reason), "startup call returned %d without reaching the gate",
|
||||
call.result);
|
||||
FAIL(reason);
|
||||
}
|
||||
ASSERT_EQ(join_status, 0);
|
||||
ASSERT_EQ(call.result, 1);
|
||||
ASSERT_TRUE(address[0] != '\0');
|
||||
@@ -1464,6 +1480,7 @@ TEST(daemon_ipc_windows_rendezvous_bridges_concurrent_lifetime_owner) {
|
||||
bool startup_observed = false;
|
||||
int join_status = -1;
|
||||
ipc_test_win_startup_call_t call = {.result = -1};
|
||||
atomic_init(&call.finished, false);
|
||||
|
||||
bool parent_ok = ipc_test_parent_new(parent, "win-lifetime-bridge");
|
||||
if (parent_ok) {
|
||||
@@ -1501,7 +1518,7 @@ TEST(daemon_ipc_windows_rendezvous_bridges_concurrent_lifetime_owner) {
|
||||
thread_started = cbm_thread_create(&thread, 0, ipc_test_win_startup_call, &call) == 0;
|
||||
}
|
||||
if (thread_started) {
|
||||
startup_observed = ipc_test_startup_gate_wait_acquired(&gate);
|
||||
startup_observed = ipc_test_startup_gate_wait_acquired(&gate, &call);
|
||||
}
|
||||
/* Claim the lifetime lock while startup is parked in the gate, so the
|
||||
* concurrent-owner bridge under test is exercised deterministically. */
|
||||
@@ -1534,7 +1551,14 @@ TEST(daemon_ipc_windows_rendezvous_bridges_concurrent_lifetime_owner) {
|
||||
ASSERT_EQ(directory_status, CBM_PRIVATE_FILE_LOCK_OK);
|
||||
ASSERT_EQ(record_status, CBM_PRIVATE_FILE_LOCK_OK);
|
||||
ASSERT_TRUE(thread_started);
|
||||
ASSERT_TRUE(startup_observed);
|
||||
if (!startup_observed) {
|
||||
/* The startup thread returned without reaching the gate; its
|
||||
* try-acquire status is the true reason. */
|
||||
char reason[96];
|
||||
(void)snprintf(reason, sizeof(reason), "startup call returned %d without reaching the gate",
|
||||
call.result);
|
||||
FAIL(reason);
|
||||
}
|
||||
ASSERT_EQ(lifetime_status, CBM_PRIVATE_FILE_LOCK_OK);
|
||||
ASSERT_EQ(join_status, 0);
|
||||
ASSERT_EQ(call.result, 0);
|
||||
@@ -2442,6 +2466,7 @@ typedef struct {
|
||||
cbm_daemon_ipc_listener_t *listener;
|
||||
cbm_daemon_ipc_connection_t *connection;
|
||||
atomic_bool waiting;
|
||||
atomic_bool finished; /* set once the server thread has returned */
|
||||
int receive_result;
|
||||
} ipc_forever_wait_server_t;
|
||||
|
||||
@@ -2464,6 +2489,7 @@ static void *ipc_forever_wait_server(void *opaque) {
|
||||
server->connection, CBM_DAEMON_IPC_WAIT_FOREVER, &frame, &payload);
|
||||
}
|
||||
free(payload);
|
||||
atomic_store_explicit(&server->finished, true, memory_order_release);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -2480,6 +2506,7 @@ TEST(daemon_ipc_wait_forever_is_interruptible) {
|
||||
int join_result = -1;
|
||||
ipc_forever_wait_server_t server = {0};
|
||||
atomic_init(&server.waiting, false);
|
||||
atomic_init(&server.finished, false);
|
||||
|
||||
if (ipc_test_parent_new(parent, "wait-forever")) {
|
||||
endpoint = cbm_daemon_ipc_endpoint_new(key, parent);
|
||||
@@ -2495,9 +2522,11 @@ TEST(daemon_ipc_wait_forever_is_interruptible) {
|
||||
if (thread_started) {
|
||||
client = cbm_daemon_ipc_connect(endpoint, 2000);
|
||||
}
|
||||
uint64_t deadline = cbm_now_ms() + 2000;
|
||||
while (client && cbm_now_ms() < deadline &&
|
||||
!atomic_load_explicit(&server.waiting, memory_order_acquire)) {
|
||||
/* Wait for the event itself, never a deadline: the server either enters
|
||||
* its forever-wait or returns (its accept is bounded by the call's own
|
||||
* timeout), and both flags only ever go false -> true. */
|
||||
while (client && !atomic_load_explicit(&server.waiting, memory_order_acquire) &&
|
||||
!atomic_load_explicit(&server.finished, memory_order_acquire)) {
|
||||
cbm_usleep(1000);
|
||||
}
|
||||
reached_wait = atomic_load_explicit(&server.waiting, memory_order_acquire);
|
||||
@@ -2561,14 +2590,13 @@ TEST(daemon_ipc_connect_waits_for_delayed_listener) {
|
||||
thread_started = cbm_thread_create(&thread, 0, ipc_delayed_listener_start, &delayed) == 0;
|
||||
}
|
||||
if (thread_started) {
|
||||
/* `delay_started` is the thread's first store, so it cannot return
|
||||
* without setting it: wait for that event with no attempt bound. */
|
||||
struct timespec poll_delay = {.tv_sec = 0, .tv_nsec = 1000000};
|
||||
for (size_t i = 0; i < 2000; i++) {
|
||||
if (atomic_load_explicit(&delayed.delay_started, memory_order_acquire)) {
|
||||
delay_observed = true;
|
||||
break;
|
||||
}
|
||||
while (!atomic_load_explicit(&delayed.delay_started, memory_order_acquire)) {
|
||||
(void)cbm_nanosleep(&poll_delay, NULL);
|
||||
}
|
||||
delay_observed = true;
|
||||
}
|
||||
if (delay_observed) {
|
||||
client = cbm_daemon_ipc_connect(endpoint, 1500);
|
||||
|
||||
Reference in new issue
Block a user