codebase-memory-mcp/tests/test_daemon_frontend.c

1400 lines
62 KiB
C

/*
* test_daemon_frontend.c — Exact JSON-RPC cancellation and maintenance.
*/
#include "test_framework.h"
#include "test_helpers.h"
#include "daemon/frontend.h"
#include "daemon/ipc.h"
#include "daemon/service.h"
#include "daemon/version_cohort.h"
#include "foundation/compat.h"
#include "foundation/compat_thread.h"
#include "foundation/platform.h"
#include <stdint.h>
#include <stdatomic.h>
#include <stdio.h>
#include <string.h>
#ifndef _WIN32
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/wait.h>
#include <unistd.h>
#endif
enum { FRONTEND_TEST_PATH_CAP = 1024 };
typedef struct {
char parent[FRONTEND_TEST_PATH_CAP];
cbm_daemon_ipc_endpoint_t *endpoint;
cbm_version_cohort_manager_t *manager;
} frontend_maintenance_fixture_t;
static bool frontend_maintenance_fixture_start(frontend_maintenance_fixture_t *fixture,
const char *tag) {
memset(fixture, 0, sizeof(*fixture));
/* Endpoint parents need production-shaped ancestry (LocalAppData on
* Windows); temp roots are refused by the runtime ancestry validation. */
if (!th_secure_runtime_parent_new(fixture->parent, sizeof(fixture->parent), tag)) {
return false;
}
fixture->endpoint = cbm_daemon_ipc_endpoint_new("0123456789abcdef", fixture->parent);
fixture->manager = fixture->endpoint ? cbm_version_cohort_manager_new(fixture->endpoint) : NULL;
return fixture->endpoint && fixture->manager;
}
static void frontend_maintenance_fixture_finish(frontend_maintenance_fixture_t *fixture) {
while (fixture->manager &&
cbm_version_cohort_manager_free(&fixture->manager) != CBM_PRIVATE_FILE_LOCK_OK) {
cbm_usleep(1000);
}
cbm_daemon_ipc_endpoint_free(fixture->endpoint);
if (fixture->parent[0]) {
(void)th_rmtree(fixture->parent);
}
memset(fixture, 0, sizeof(*fixture));
}
#ifndef _WIN32
static const char FRONTEND_TEST_BUILD[] =
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
static const char FRONTEND_TEST_CACHE[] =
"cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc";
enum {
/* Hang guards, not benchmarks: starved 3-4-core CI runners need several
* seconds for frontend/daemon startup paths that finish instantly on a
* dev machine (see the index-supervisor calibration). */
FRONTEND_EOF_TEST_REQUEST_TIMEOUT_MS = 30000,
FRONTEND_EOF_TEST_CATASTROPHIC_TIMEOUT_S = 90,
FRONTEND_BACKPRESSURE_MESSAGE_BYTES = 2 * 1024 * 1024,
FRONTEND_BACKPRESSURE_FRONTEND_TIMEOUT_S = 90,
FRONTEND_BACKPRESSURE_DAEMON_TIMEOUT_S = 90,
FRONTEND_BACKPRESSURE_RUNTIME_TIMEOUT_MS = 90000,
FRONTEND_BACKPRESSURE_CLEANUP_TIMEOUT_MS = 30000,
/* The production queue is deliberately bounded below this count. Keep the
* regression black-box: it must remain valid if the exact capacity changes
* while still proving that overload cannot hide an already-pending EOF. */
FRONTEND_EOF_TEST_OVERFLOW_MESSAGES = 32,
};
typedef struct {
atomic_int requests;
atomic_int session_cancels;
atomic_int sessions_opened;
atomic_int first_session_cancels;
atomic_int second_session_cancels;
atomic_bool block_first_request;
atomic_bool first_request_started;
int request_observed_fd;
int session_cancel_fd;
} frontend_eof_application_context_t;
typedef struct {
frontend_eof_application_context_t *context;
atomic_bool cancelled;
int session_index;
} frontend_eof_application_session_t;
typedef struct {
int fd;
bool overflow;
frontend_eof_application_context_t *application;
atomic_bool finished;
atomic_bool succeeded;
} frontend_eof_writer_t;
typedef struct {
char conflict_log[FRONTEND_TEST_PATH_CAP];
cbm_daemon_ipc_endpoint_t *endpoint;
cbm_version_cohort_manager_t *manager;
cbm_daemon_runtime_service_t *service;
cbm_daemon_runtime_client_t *client;
frontend_eof_application_context_t application;
} frontend_eof_fixture_t;
static cbm_daemon_build_identity_t frontend_test_identity(void) {
cbm_daemon_build_identity_t identity = {
.semantic_version = "2.4.0",
.build_fingerprint = FRONTEND_TEST_BUILD,
.cache_fingerprint = FRONTEND_TEST_CACHE,
.protocol_abi = 3,
.store_abi = 11,
.feature_abi = 7,
};
return identity;
}
static cbm_daemon_runtime_application_session_t *frontend_eof_application_session_open(
void *opaque, cbm_daemon_client_id_t client_id, uint64_t authenticated_process_id) {
frontend_eof_application_context_t *context = opaque;
frontend_eof_application_session_t *session = calloc(1, sizeof(*session));
if (!context || !session || client_id == CBM_DAEMON_CLIENT_ID_INVALID ||
authenticated_process_id == 0) {
free(session);
return NULL;
}
session->context = context;
session->session_index =
atomic_fetch_add_explicit(&context->sessions_opened, 1, memory_order_relaxed);
atomic_init(&session->cancelled, false);
return (cbm_daemon_runtime_application_session_t *)session;
}
static cbm_daemon_runtime_application_status_t frontend_eof_application_request(
void *opaque, cbm_daemon_runtime_application_session_t *opaque_session,
cbm_daemon_runtime_application_token_t request_token, const uint8_t *request,
uint32_t request_length, uint8_t **response_out, uint32_t *response_length_out) {
(void)request_token;
frontend_eof_application_context_t *context = opaque;
frontend_eof_application_session_t *session =
(frontend_eof_application_session_t *)opaque_session;
if (!context || !session || session->context != context || !response_out ||
!response_length_out || (request_length > 0 && !request)) {
return CBM_DAEMON_RUNTIME_APPLICATION_HANDLER_ERROR;
}
*response_out = NULL;
*response_length_out = 0;
int request_index = atomic_fetch_add_explicit(&context->requests, 1, memory_order_relaxed);
if (request_index == 0 &&
atomic_load_explicit(&context->block_first_request, memory_order_acquire)) {
atomic_store_explicit(&context->first_request_started, true, memory_order_release);
while (!atomic_load_explicit(&session->cancelled, memory_order_acquire)) {
cbm_usleep(1000);
}
return CBM_DAEMON_RUNTIME_APPLICATION_CANCELLED;
}
if (request_length == 0) {
return CBM_DAEMON_RUNTIME_APPLICATION_OK;
}
uint8_t *response = malloc(request_length);
if (!response) {
return CBM_DAEMON_RUNTIME_APPLICATION_HANDLER_ERROR;
}
memcpy(response, request, request_length);
*response_out = response;
*response_length_out = request_length;
if (context->request_observed_fd >= 0) {
const char marker = 'Q';
(void)write(context->request_observed_fd, &marker, 1);
}
return CBM_DAEMON_RUNTIME_APPLICATION_OK;
}
static void frontend_eof_application_request_cancel(
void *opaque, cbm_daemon_runtime_application_session_t *opaque_session,
cbm_daemon_runtime_application_token_t request_token) {
(void)request_token;
frontend_eof_application_context_t *context = opaque;
frontend_eof_application_session_t *session =
(frontend_eof_application_session_t *)opaque_session;
if (context && session && session->context == context) {
atomic_store_explicit(&session->cancelled, true, memory_order_release);
}
}
static void frontend_eof_application_session_cancel(
void *opaque, cbm_daemon_runtime_application_session_t *opaque_session) {
frontend_eof_application_context_t *context = opaque;
frontend_eof_application_session_t *session =
(frontend_eof_application_session_t *)opaque_session;
if (context && session && session->context == context) {
atomic_store_explicit(&session->cancelled, true, memory_order_release);
if (session->session_index == 0) {
(void)atomic_fetch_add_explicit(&context->first_session_cancels, 1,
memory_order_relaxed);
} else if (session->session_index == 1) {
(void)atomic_fetch_add_explicit(&context->second_session_cancels, 1,
memory_order_relaxed);
}
/* Publish the total only after the exact-session counter. The daemon
* fixture acquires this value before inspecting attribution. */
(void)atomic_fetch_add_explicit(&context->session_cancels, 1, memory_order_release);
if (context->session_cancel_fd >= 0) {
const char marker = 'C';
(void)write(context->session_cancel_fd, &marker, 1);
}
}
}
static void frontend_eof_application_session_close(
void *opaque, cbm_daemon_runtime_application_session_t *opaque_session) {
frontend_eof_application_context_t *context = opaque;
frontend_eof_application_session_t *session =
(frontend_eof_application_session_t *)opaque_session;
if (context && session && session->context == context) {
free(session);
}
}
static bool frontend_eof_write_all(int fd, const char *bytes, size_t length) {
size_t offset = 0;
while (offset < length) {
ssize_t written = write(fd, bytes + offset, length - offset);
if (written > 0) {
offset += (size_t)written;
} else if (written < 0 && errno == EINTR) {
continue;
} else {
return false;
}
}
return true;
}
static void *frontend_eof_writer(void *opaque) {
frontend_eof_writer_t *writer = opaque;
static const char first[] =
"{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"test/block\",\"params\":{}}\n";
bool ok = frontend_eof_write_all(writer->fd, first, sizeof(first) - 1);
uint64_t deadline = cbm_now_ms() + FRONTEND_EOF_TEST_REQUEST_TIMEOUT_MS;
while (
ok &&
!atomic_load_explicit(&writer->application->first_request_started, memory_order_acquire) &&
cbm_now_ms() < deadline) {
cbm_usleep(1000);
}
ok = ok &&
atomic_load_explicit(&writer->application->first_request_started, memory_order_acquire);
for (int index = 0; ok && writer->overflow && index < FRONTEND_EOF_TEST_OVERFLOW_MESSAGES;
index++) {
char message[160];
int written =
snprintf(message, sizeof(message),
"{\"jsonrpc\":\"2.0\",\"id\":%d,\"method\":\"test/queued\",\"params\":{}}\n",
index + 2);
ok = written > 0 && written < (int)sizeof(message) &&
frontend_eof_write_all(writer->fd, message, (size_t)written);
}
ok = close(writer->fd) == 0 && ok;
writer->fd = -1;
atomic_store_explicit(&writer->succeeded, ok, memory_order_release);
atomic_store_explicit(&writer->finished, true, memory_order_release);
return NULL;
}
static bool frontend_eof_fixture_start(frontend_eof_fixture_t *fixture, const char *parent) {
memset(fixture, 0, sizeof(*fixture));
atomic_init(&fixture->application.requests, 0);
atomic_init(&fixture->application.session_cancels, 0);
atomic_init(&fixture->application.sessions_opened, 0);
atomic_init(&fixture->application.first_session_cancels, 0);
atomic_init(&fixture->application.second_session_cancels, 0);
atomic_init(&fixture->application.block_first_request, true);
atomic_init(&fixture->application.first_request_started, false);
fixture->application.request_observed_fd = -1;
fixture->application.session_cancel_fd = -1;
char key[CBM_DAEMON_KEY_SIZE];
char build[CBM_DAEMON_BUILD_FINGERPRINT_SIZE];
int log_written = snprintf(fixture->conflict_log, sizeof(fixture->conflict_log),
"%s/conflicts.ndjson", parent);
if (log_written <= 0 || log_written >= (int)sizeof(fixture->conflict_log) ||
!cbm_daemon_rendezvous_key(key) ||
!cbm_daemon_runtime_process_build_fingerprint((uint64_t)getpid(), build)) {
return false;
}
fixture->endpoint = cbm_daemon_ipc_endpoint_new(key, parent);
fixture->manager = fixture->endpoint ? cbm_version_cohort_manager_new(fixture->endpoint) : NULL;
cbm_daemon_build_identity_t identity = {
.semantic_version = "2.4.0",
.build_fingerprint = build,
.cache_fingerprint = FRONTEND_TEST_CACHE,
.protocol_abi = 3,
.store_abi = 11,
.feature_abi = 7,
};
cbm_daemon_runtime_application_callbacks_t callbacks = {
.context = &fixture->application,
.session_open = frontend_eof_application_session_open,
.request = frontend_eof_application_request,
.request_cancel = frontend_eof_application_request_cancel,
.session_cancel = frontend_eof_application_session_cancel,
.session_close = frontend_eof_application_session_close,
};
cbm_daemon_runtime_service_config_t config = {
.endpoint = fixture->endpoint,
.identity = identity,
.conflict_log_path = fixture->conflict_log,
.conflict_log_cap_bytes = 64U * 1024U,
.max_clients = 4,
.lease_timeout_ms = 5000,
.request_timeout_ms = FRONTEND_EOF_TEST_REQUEST_TIMEOUT_MS,
.shutdown_timeout_ms = FRONTEND_EOF_TEST_REQUEST_TIMEOUT_MS,
.application = callbacks,
};
fixture->service = fixture->manager ? cbm_daemon_runtime_service_start(&config) : NULL;
cbm_daemon_runtime_connect_result_t connect_result = {0};
fixture->client = fixture->service
? cbm_daemon_runtime_client_connect(fixture->endpoint, &identity,
FRONTEND_EOF_TEST_REQUEST_TIMEOUT_MS,
&connect_result)
: NULL;
return fixture->client && connect_result.status == CBM_DAEMON_RUNTIME_CONNECT_ACCEPTED;
}
static bool frontend_eof_fixture_finish(frontend_eof_fixture_t *fixture) {
bool ok = true;
if (fixture->client) {
ok = cbm_daemon_runtime_client_close(fixture->client,
FRONTEND_EOF_TEST_REQUEST_TIMEOUT_MS) &&
ok;
fixture->client = NULL;
}
if (fixture->service) {
if (cbm_daemon_runtime_service_state(fixture->service) !=
CBM_DAEMON_RUNTIME_SERVICE_EXITED) {
ok = cbm_daemon_runtime_service_stop(fixture->service,
FRONTEND_EOF_TEST_REQUEST_TIMEOUT_MS) &&
ok;
}
ok = cbm_daemon_runtime_service_free(fixture->service) && ok;
fixture->service = NULL;
}
if (fixture->manager) {
ok = cbm_version_cohort_manager_free(&fixture->manager) == CBM_PRIVATE_FILE_LOCK_OK && ok;
}
cbm_daemon_ipc_endpoint_free(fixture->endpoint);
fixture->endpoint = NULL;
return ok;
}
static int frontend_eof_child_run(const char *parent, bool overflow) {
frontend_eof_fixture_t fixture;
if (!frontend_eof_fixture_start(&fixture, parent)) {
(void)frontend_eof_fixture_finish(&fixture);
return 70;
}
int input_pipe[2] = {-1, -1};
if (pipe(input_pipe) != 0) {
(void)frontend_eof_fixture_finish(&fixture);
return 71;
}
FILE *input = fdopen(input_pipe[0], "rb");
FILE *output = tmpfile();
frontend_eof_writer_t writer = {
.fd = input_pipe[1],
.overflow = overflow,
.application = &fixture.application,
};
atomic_init(&writer.finished, false);
atomic_init(&writer.succeeded, false);
cbm_thread_t writer_thread;
bool writer_started =
input && output && cbm_thread_create(&writer_thread, 0, frontend_eof_writer, &writer) == 0;
if (!writer_started) {
if (input) {
(void)fclose(input);
} else {
(void)close(input_pipe[0]);
}
(void)close(input_pipe[1]);
if (output) {
(void)fclose(output);
}
(void)frontend_eof_fixture_finish(&fixture);
return 72;
}
cbm_daemon_runtime_client_t *frontend_client = fixture.client;
fixture.client = NULL; /* cbm_daemon_frontend_mcp_run consumes it. */
int result = cbm_daemon_frontend_mcp_run(frontend_client, fixture.manager, input, output);
bool joined = cbm_thread_join(&writer_thread) == 0;
bool writer_ok = atomic_load_explicit(&writer.finished, memory_order_acquire) &&
atomic_load_explicit(&writer.succeeded, memory_order_acquire);
bool request_started =
atomic_load_explicit(&fixture.application.first_request_started, memory_order_acquire);
bool session_cancelled =
atomic_load_explicit(&fixture.application.session_cancels, memory_order_acquire) > 0;
bool input_closed = fclose(input) == 0;
bool output_closed = fclose(output) == 0;
bool streams_closed = input_closed && output_closed;
bool fixture_closed = frontend_eof_fixture_finish(&fixture);
bool result_matches_contract = overflow ? result < 0 : result == 0;
return result_matches_contract && joined && writer_ok && request_started && session_cancelled &&
streams_closed && fixture_closed
? 0
: 73;
}
static bool frontend_eof_run_isolated(const char *tag, bool overflow) {
char parent[FRONTEND_TEST_PATH_CAP];
int written =
snprintf(parent, sizeof(parent), "%s/cbm-frontend-eof-%s-XXXXXX", cbm_tmpdir(), tag);
if (written <= 0 || written >= (int)sizeof(parent) || !cbm_mkdtemp(parent)) {
return false;
}
pid_t child = fork();
if (child == 0) {
(void)signal(SIGALRM, SIG_DFL);
/* This alarm bounds a broken isolated harness, not product behavior.
* Valid startup, drain/watchdog, close, and fixture teardown can exceed
* ten seconds under sanitizers while remaining within their own bounds. */
(void)alarm(FRONTEND_EOF_TEST_CATASTROPHIC_TIMEOUT_S);
_exit(frontend_eof_child_run(parent, overflow));
}
int status = 0;
pid_t waited;
do {
waited = child > 0 ? waitpid(child, &status, 0) : -1;
} while (waited < 0 && errno == EINTR);
bool cleaned = th_rmtree(parent) == 0;
bool child_ok = child > 0 && waited == child && WIFEXITED(status) && WEXITSTATUS(status) == 0;
if (!child_ok) {
if (child <= 0) {
(void)fprintf(stderr, "frontend EOF fixture %s: fork failed\n", tag);
} else if (waited != child) {
(void)fprintf(stderr, "frontend EOF fixture %s: waitpid failed errno=%d\n", tag, errno);
} else if (WIFSIGNALED(status)) {
(void)fprintf(stderr, "frontend EOF fixture %s: child signal=%d\n", tag,
WTERMSIG(status));
} else if (WIFEXITED(status)) {
(void)fprintf(stderr, "frontend EOF fixture %s: child exit=%d\n", tag,
WEXITSTATUS(status));
} else {
(void)fprintf(stderr, "frontend EOF fixture %s: unexpected child status\n", tag);
}
}
if (!cleaned) {
(void)fprintf(stderr, "frontend EOF fixture %s: cleanup failed\n", tag);
}
return child_ok && cleaned;
}
static void frontend_test_release_lease(cbm_version_cohort_lease_t **lease) {
while (lease && *lease && cbm_version_cohort_lease_release(lease) != CBM_PRIVATE_FILE_LOCK_OK) {
cbm_usleep(1000);
}
}
static bool frontend_test_release_lease_until(cbm_version_cohort_lease_t **lease,
uint64_t deadline_ms) {
while (lease && *lease) {
if (cbm_version_cohort_lease_release(lease) == CBM_PRIVATE_FILE_LOCK_OK) {
continue;
}
if (cbm_now_ms() >= deadline_ms) {
return false;
}
cbm_usleep(1000);
}
return !lease || !*lease;
}
static bool frontend_test_read_byte(int fd, char *observed, uint64_t deadline_ms) {
if (!observed) {
return false;
}
*observed = '\0';
int flags = fcntl(fd, F_GETFL, 0);
if (flags < 0 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) != 0) {
return false;
}
for (;;) {
char value = '\0';
ssize_t count = read(fd, &value, 1);
if (count == 1) {
*observed = value;
return true;
}
if (count == 0 || (count < 0 && errno != EAGAIN && errno != EWOULDBLOCK) ||
cbm_now_ms() >= deadline_ms) {
return false;
}
cbm_usleep(1000);
}
}
static bool frontend_test_wait_byte(int fd, char expected, uint64_t deadline_ms) {
char observed = '\0';
return frontend_test_read_byte(fd, &observed, deadline_ms) && observed == expected;
}
typedef struct {
int fd;
atomic_bool finished;
atomic_bool succeeded;
} frontend_backpressure_writer_t;
static cbm_version_cohort_quiesce_result_t frontend_test_quiesce_requested(void *context);
static bool frontend_backpressure_identity(cbm_daemon_build_identity_t *identity,
const char build[CBM_DAEMON_BUILD_FINGERPRINT_SIZE]) {
if (!identity || !build || strlen(build) != CBM_DAEMON_BUILD_FINGERPRINT_SIZE - 1) {
return false;
}
*identity = (cbm_daemon_build_identity_t){
.semantic_version = "2.4.0",
.build_fingerprint = build,
.cache_fingerprint = FRONTEND_TEST_CACHE,
.protocol_abi = 3,
.store_abi = 11,
.feature_abi = 7,
};
return true;
}
static void *frontend_backpressure_writer(void *opaque) {
frontend_backpressure_writer_t *writer = opaque;
static const char prefix[] = "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"test/large\","
"\"params\":{\"blob\":\"";
static const char suffix[] = "\"}}\n";
char *message = malloc(FRONTEND_BACKPRESSURE_MESSAGE_BYTES);
size_t prefix_length = sizeof(prefix) - 1;
size_t suffix_length = sizeof(suffix) - 1;
bool valid = message && prefix_length + suffix_length < FRONTEND_BACKPRESSURE_MESSAGE_BYTES;
if (valid) {
memcpy(message, prefix, prefix_length);
memset(message + prefix_length, 'x',
FRONTEND_BACKPRESSURE_MESSAGE_BYTES - prefix_length - suffix_length);
memcpy(message + FRONTEND_BACKPRESSURE_MESSAGE_BYTES - suffix_length, suffix,
suffix_length);
valid = frontend_eof_write_all(writer->fd, message, FRONTEND_BACKPRESSURE_MESSAGE_BYTES);
}
free(message);
valid = close(writer->fd) == 0 && valid;
writer->fd = -1;
atomic_store_explicit(&writer->succeeded, valid, memory_order_release);
atomic_store_explicit(&writer->finished, true, memory_order_release);
return NULL;
}
static int frontend_backpressure_daemon_run(const char *parent, int ready_fd, int cancel_fd,
int control_fd,
const char build[CBM_DAEMON_BUILD_FINGERPRINT_SIZE]) {
(void)alarm(FRONTEND_BACKPRESSURE_DAEMON_TIMEOUT_S);
char key[CBM_DAEMON_KEY_SIZE];
char conflict_log[FRONTEND_TEST_PATH_CAP];
cbm_daemon_build_identity_t identity = {0};
int log_written = snprintf(conflict_log, sizeof(conflict_log), "%s/conflicts.ndjson", parent);
cbm_daemon_ipc_endpoint_t *endpoint =
log_written > 0 && log_written < (int)sizeof(conflict_log) &&
cbm_daemon_rendezvous_key(key) && frontend_backpressure_identity(&identity, build)
? cbm_daemon_ipc_endpoint_new(key, parent)
: NULL;
frontend_eof_application_context_t application;
memset(&application, 0, sizeof(application));
atomic_init(&application.requests, 0);
atomic_init(&application.session_cancels, 0);
atomic_init(&application.sessions_opened, 0);
atomic_init(&application.first_session_cancels, 0);
atomic_init(&application.second_session_cancels, 0);
atomic_init(&application.block_first_request, false);
atomic_init(&application.first_request_started, false);
application.request_observed_fd = ready_fd;
application.session_cancel_fd = cancel_fd;
cbm_daemon_runtime_application_callbacks_t callbacks = {
.context = &application,
.session_open = frontend_eof_application_session_open,
.request = frontend_eof_application_request,
.request_cancel = frontend_eof_application_request_cancel,
.session_cancel = frontend_eof_application_session_cancel,
.session_close = frontend_eof_application_session_close,
};
cbm_daemon_runtime_service_config_t config = {
.endpoint = endpoint,
.identity = identity,
.conflict_log_path = conflict_log,
.conflict_log_cap_bytes = 64U * 1024U,
.max_clients = 4,
.lease_timeout_ms = 30000,
.request_timeout_ms = FRONTEND_EOF_TEST_REQUEST_TIMEOUT_MS,
.shutdown_timeout_ms = FRONTEND_EOF_TEST_REQUEST_TIMEOUT_MS,
.application = callbacks,
};
cbm_daemon_runtime_service_t *service =
endpoint ? cbm_daemon_runtime_service_start(&config) : NULL;
const char ready = 'R';
bool announced = service && write(ready_fd, &ready, 1) == 1;
uint64_t cancellation_deadline = cbm_now_ms() + FRONTEND_BACKPRESSURE_RUNTIME_TIMEOUT_MS;
while (announced &&
atomic_load_explicit(&application.session_cancels, memory_order_acquire) == 0 &&
cbm_now_ms() < cancellation_deadline) {
cbm_usleep(1000);
}
int target_cancellations =
atomic_load_explicit(&application.session_cancels, memory_order_acquire);
int sentinel_cancellations_before_close =
atomic_load_explicit(&application.first_session_cancels, memory_order_acquire);
int target_session_cancellations =
atomic_load_explicit(&application.second_session_cancels, memory_order_acquire);
bool target_cancelled_exactly_once = announced && target_cancellations == 1 &&
sentinel_cancellations_before_close == 0 &&
target_session_cancellations == 1;
const char checkpoint = target_cancelled_exactly_once ? 'T' : 'E';
bool checkpoint_written = write(ready_fd, &checkpoint, 1) == 1;
char control = '\0';
bool shutdown_released =
checkpoint_written &&
frontend_test_read_byte(control_fd, &control,
cbm_now_ms() + FRONTEND_BACKPRESSURE_RUNTIME_TIMEOUT_MS) &&
control == 'S';
int final_cancellations =
atomic_load_explicit(&application.session_cancels, memory_order_acquire);
int sentinel_session_cancellations =
atomic_load_explicit(&application.first_session_cancels, memory_order_acquire);
int final_target_session_cancellations =
atomic_load_explicit(&application.second_session_cancels, memory_order_acquire);
bool sentinel_closed_exactly_once = final_cancellations == 2 &&
sentinel_session_cancellations == 1 &&
final_target_session_cancellations == 1;
bool stopped =
service && cbm_daemon_runtime_service_stop(service, FRONTEND_EOF_TEST_REQUEST_TIMEOUT_MS);
bool exited = stopped && cbm_daemon_runtime_service_wait_exited(service, 0);
bool freed = exited && cbm_daemon_runtime_service_free(service);
cbm_daemon_ipc_endpoint_free(endpoint);
const char done = target_cancelled_exactly_once && shutdown_released &&
sentinel_closed_exactly_once && stopped && exited && freed
? 'D'
: 'E';
bool finished = write(ready_fd, &done, 1) == 1;
(void)close(ready_fd);
(void)close(cancel_fd);
(void)close(control_fd);
return announced && target_cancelled_exactly_once && checkpoint_written && shutdown_released &&
sentinel_closed_exactly_once && stopped && exited && freed && finished
? 0
: 80;
}
static int frontend_backpressure_frontend_run(const char *parent, int input_fd, int input_write_fd,
int output_fd,
const char build[CBM_DAEMON_BUILD_FINGERPRINT_SIZE],
bool keep_input_open) {
(void)alarm(FRONTEND_BACKPRESSURE_FRONTEND_TIMEOUT_S);
char key[CBM_DAEMON_KEY_SIZE];
cbm_daemon_build_identity_t identity = {0};
cbm_daemon_ipc_endpoint_t *endpoint =
cbm_daemon_rendezvous_key(key) && frontend_backpressure_identity(&identity, build)
? cbm_daemon_ipc_endpoint_new(key, parent)
: NULL;
cbm_version_cohort_manager_t *manager =
endpoint ? cbm_version_cohort_manager_new(endpoint) : NULL;
cbm_version_cohort_lease_t *participant = NULL;
cbm_daemon_conflict_t conflict;
cbm_version_cohort_status_t admitted =
manager ? cbm_version_cohort_acquire(manager, &identity, cbm_now_ms() + 2000U, &participant,
&conflict)
: CBM_VERSION_COHORT_IO;
cbm_daemon_runtime_connect_result_t connect_result = {0};
cbm_daemon_runtime_client_t *client =
admitted == CBM_VERSION_COHORT_OK
? cbm_daemon_runtime_client_connect(
endpoint, &identity, FRONTEND_EOF_TEST_REQUEST_TIMEOUT_MS, &connect_result)
: NULL;
FILE *input = client ? fdopen(input_fd, "rb") : NULL;
FILE *output = input ? fdopen(output_fd, "wb") : NULL;
int input_hold_fd = keep_input_open && output ? dup(input_write_fd) : -1;
frontend_backpressure_writer_t writer = {
.fd = input_write_fd,
};
atomic_init(&writer.finished, false);
atomic_init(&writer.succeeded, false);
cbm_thread_t writer_thread;
bool writer_started =
output && (!keep_input_open || input_hold_fd >= 0) &&
cbm_thread_create(&writer_thread, 0, frontend_backpressure_writer, &writer) == 0;
if (!writer_started) {
if (client) {
(void)cbm_daemon_runtime_client_close(client, FRONTEND_EOF_TEST_REQUEST_TIMEOUT_MS);
}
if (input) {
(void)fclose(input);
} else {
(void)close(input_fd);
}
if (output) {
(void)fclose(output);
} else {
(void)close(output_fd);
}
(void)close(input_write_fd);
if (input_hold_fd >= 0) {
(void)close(input_hold_fd);
}
frontend_test_release_lease(&participant);
if (manager) {
(void)cbm_version_cohort_manager_free(&manager);
}
cbm_daemon_ipc_endpoint_free(endpoint);
return 81;
}
int result = cbm_daemon_frontend_mcp_run(client, manager, input, output);
bool joined = cbm_thread_join(&writer_thread) == 0;
bool writer_ok = atomic_load_explicit(&writer.finished, memory_order_acquire) &&
atomic_load_explicit(&writer.succeeded, memory_order_acquire);
bool input_closed = fclose(input) == 0;
bool output_closed = fclose(output) == 0;
bool input_hold_closed = input_hold_fd < 0 || close(input_hold_fd) == 0;
frontend_test_release_lease(&participant);
bool manager_closed = cbm_version_cohort_manager_free(&manager) == CBM_PRIVATE_FILE_LOCK_OK;
cbm_daemon_ipc_endpoint_free(endpoint);
return result < 0 && joined && writer_ok && input_closed && output_closed &&
input_hold_closed && manager_closed
? 47
: 82;
}
static bool frontend_backpressure_run_isolated(bool maintenance) {
char parent[FRONTEND_TEST_PATH_CAP];
char build[CBM_DAEMON_BUILD_FINGERPRINT_SIZE] = {0};
int path_written = snprintf(parent, sizeof(parent),
maintenance ? "%s/cbm-frontend-maintenance-backpressure-XXXXXX"
: "%s/cbm-frontend-backpressure-XXXXXX",
cbm_tmpdir());
int ready_pipe[2] = {-1, -1};
int cancel_pipe[2] = {-1, -1};
int control_pipe[2] = {-1, -1};
bool directory_ready =
path_written > 0 && path_written < (int)sizeof(parent) && cbm_mkdtemp(parent);
bool identity_ready =
directory_ready && cbm_daemon_runtime_process_build_fingerprint((uint64_t)getpid(), build);
bool prepared = identity_ready && pipe(ready_pipe) == 0 && pipe(cancel_pipe) == 0 &&
pipe(control_pipe) == 0;
if (!prepared) {
for (size_t index = 0; index < 2; index++) {
if (ready_pipe[index] >= 0) {
(void)close(ready_pipe[index]);
}
if (cancel_pipe[index] >= 0) {
(void)close(cancel_pipe[index]);
}
if (control_pipe[index] >= 0) {
(void)close(control_pipe[index]);
}
}
if (directory_ready) {
(void)th_rmtree(parent);
}
return false;
}
pid_t daemon = prepared ? fork() : -1;
if (daemon == 0) {
(void)signal(SIGALRM, SIG_DFL);
(void)close(ready_pipe[0]);
(void)close(cancel_pipe[0]);
(void)close(control_pipe[1]);
_exit(frontend_backpressure_daemon_run(parent, ready_pipe[1], cancel_pipe[1],
control_pipe[0], build));
}
if (prepared) {
(void)close(ready_pipe[1]);
(void)close(cancel_pipe[1]);
(void)close(control_pipe[0]);
}
char announced_marker = '\0';
/* The daemon child runs cbm_daemon_runtime_service_start before it can
* announce, and that includes the cohort-claim path whose own internal
* budget is HOST_DAEMON_CLAIM_TIMEOUT_MS (12s). An announce deadline
* below that budget SIGKILLs a slow-but-legitimate startup — on
* oversubscribed CI runners this fired every round (announced=0,
* daemon_signal=9) while fast local machines never saw it. Keep the
* deadline comfortably above the daemon's own worst-case budget. */
bool announced_read = daemon > 0 && frontend_test_read_byte(ready_pipe[0], &announced_marker,
cbm_now_ms() + 30000U);
bool announced = announced_read && announced_marker == 'R';
/* This raw runtime client intentionally owns no cohort lease. It is a
* sentinel for session isolation: maintenance must remove the target
* frontend without touching this independently authenticated session. Its
* completed admission precedes the target fork, so the daemon fixture can
* attribute session-cancel callbacks to sentinel index 0 and target index
* 1 rather than relying on a global callback count. */
char sentinel_key[CBM_DAEMON_KEY_SIZE];
cbm_daemon_build_identity_t sentinel_identity = {0};
cbm_daemon_ipc_endpoint_t *sentinel_endpoint =
announced && cbm_daemon_rendezvous_key(sentinel_key) &&
frontend_backpressure_identity(&sentinel_identity, build)
? cbm_daemon_ipc_endpoint_new(sentinel_key, parent)
: NULL;
cbm_daemon_runtime_connect_result_t sentinel_connect = {0};
cbm_daemon_runtime_client_t *sentinel =
sentinel_endpoint ? cbm_daemon_runtime_client_connect(sentinel_endpoint, &sentinel_identity,
FRONTEND_EOF_TEST_REQUEST_TIMEOUT_MS,
&sentinel_connect)
: NULL;
bool sentinel_ready =
sentinel && sentinel_connect.status == CBM_DAEMON_RUNTIME_CONNECT_ACCEPTED;
if (!sentinel_ready && daemon > 0) {
(void)kill(daemon, SIGKILL);
}
int input_pipe[2] = {-1, -1};
int output_pipe[2] = {-1, -1};
bool input_ready = sentinel_ready && pipe(input_pipe) == 0;
bool frontend_pipes = input_ready && pipe(output_pipe) == 0;
if (input_ready && !frontend_pipes) {
(void)close(input_pipe[0]);
(void)close(input_pipe[1]);
input_pipe[0] = -1;
input_pipe[1] = -1;
}
pid_t frontend = frontend_pipes ? fork() : -1;
if (frontend == 0) {
(void)signal(SIGALRM, SIG_DFL);
(void)close(output_pipe[0]);
(void)close(ready_pipe[0]);
(void)close(cancel_pipe[0]);
(void)close(control_pipe[1]);
_exit(frontend_backpressure_frontend_run(parent, input_pipe[0], input_pipe[1],
output_pipe[1], build, maintenance));
}
if (frontend_pipes) {
(void)close(input_pipe[0]);
(void)close(input_pipe[1]);
(void)close(output_pipe[1]);
}
char request_marker = '\0';
bool request_read = frontend > 0 && frontend_test_read_byte(ready_pipe[0], &request_marker,
cbm_now_ms() + 4000U);
bool request_observed = request_read && request_marker == 'Q';
char output_marker = '\0';
bool output_observed =
!maintenance || (request_observed && frontend_test_read_byte(output_pipe[0], &output_marker,
cbm_now_ms() + 4000U));
cbm_daemon_ipc_endpoint_t *mutation_endpoint = NULL;
cbm_version_cohort_manager_t *mutation_manager = NULL;
cbm_version_cohort_lease_t *mutation = NULL;
cbm_version_cohort_quiesce_result_t quiesce = CBM_VERSION_COHORT_QUIESCE_NOT_NEEDED;
cbm_version_cohort_status_t mutation_status = CBM_VERSION_COHORT_OK;
if (maintenance) {
char mutation_key[CBM_DAEMON_KEY_SIZE];
mutation_endpoint = output_observed && cbm_daemon_rendezvous_key(mutation_key)
? cbm_daemon_ipc_endpoint_new(mutation_key, parent)
: NULL;
mutation_manager =
mutation_endpoint ? cbm_version_cohort_manager_new(mutation_endpoint) : NULL;
mutation_status = mutation_manager
? cbm_version_cohort_reserve_for_mutation(
mutation_manager, cbm_now_ms() + 8000U,
frontend_test_quiesce_requested, NULL, &quiesce, &mutation)
: CBM_VERSION_COHORT_IO;
if (mutation_status != CBM_VERSION_COHORT_OK && frontend > 0) {
(void)kill(frontend, SIGKILL);
}
}
int frontend_status = 0;
pid_t frontend_waited;
if (frontend > 0) {
do {
frontend_waited = waitpid(frontend, &frontend_status, 0);
} while (frontend_waited < 0 && errno == EINTR);
} else {
frontend_waited = -1;
}
char cancel_marker = '\0';
bool cancel_read = request_observed && frontend_test_read_byte(cancel_pipe[0], &cancel_marker,
cbm_now_ms() + 4000U);
bool cancelled = cancel_read && cancel_marker == 'C';
char checkpoint_marker = '\0';
bool checkpoint_read =
frontend_test_read_byte(ready_pipe[0], &checkpoint_marker, cbm_now_ms() + 4000U);
bool target_cancelled_exactly_once = checkpoint_read && checkpoint_marker == 'T';
bool sentinel_usable =
cancelled && target_cancelled_exactly_once && sentinel_ready &&
cbm_daemon_runtime_client_heartbeat(sentinel, FRONTEND_EOF_TEST_REQUEST_TIMEOUT_MS);
bool sentinel_closed =
sentinel && cbm_daemon_runtime_client_close(sentinel, FRONTEND_EOF_TEST_REQUEST_TIMEOUT_MS);
sentinel = NULL;
cbm_daemon_ipc_endpoint_free(sentinel_endpoint);
sentinel_endpoint = NULL;
const char shutdown = 'S';
bool shutdown_sent = checkpoint_read && write(control_pipe[1], &shutdown, 1) == 1;
if (control_pipe[1] >= 0) {
(void)close(control_pipe[1]);
control_pipe[1] = -1;
}
char done_marker = '\0';
bool done_read =
shutdown_sent && frontend_test_read_byte(ready_pipe[0], &done_marker, cbm_now_ms() + 4000U);
bool daemon_done = done_read && done_marker == 'D';
if (!daemon_done && daemon > 0) {
(void)kill(daemon, SIGKILL);
}
int daemon_status = 0;
pid_t daemon_waited;
do {
daemon_waited = daemon > 0 ? waitpid(daemon, &daemon_status, 0) : -1;
} while (daemon_waited < 0 && errno == EINTR);
uint64_t cleanup_now = cbm_now_ms();
uint64_t cleanup_deadline = cleanup_now > UINT64_MAX - FRONTEND_BACKPRESSURE_CLEANUP_TIMEOUT_MS
? UINT64_MAX
: cleanup_now + FRONTEND_BACKPRESSURE_CLEANUP_TIMEOUT_MS;
bool mutation_released = frontend_test_release_lease_until(&mutation, cleanup_deadline);
while (mutation_released && mutation_manager && cbm_now_ms() < cleanup_deadline &&
cbm_version_cohort_manager_free(&mutation_manager) != CBM_PRIVATE_FILE_LOCK_OK) {
cbm_usleep(1000);
}
bool mutation_manager_closed = mutation_manager == NULL;
cbm_daemon_ipc_endpoint_free(mutation_endpoint);
if (output_pipe[0] >= 0) {
(void)close(output_pipe[0]);
}
if (ready_pipe[0] >= 0) {
(void)close(ready_pipe[0]);
}
if (cancel_pipe[0] >= 0) {
(void)close(cancel_pipe[0]);
}
if (control_pipe[1] >= 0) {
(void)close(control_pipe[1]);
}
bool cleaned = prepared && th_rmtree(parent) == 0;
bool frontend_exited_boundedly = frontend_waited == frontend && WIFEXITED(frontend_status) &&
(maintenance ? WEXITSTATUS(frontend_status) == EXIT_SUCCESS
: (WEXITSTATUS(frontend_status) == EXIT_FAILURE ||
WEXITSTATUS(frontend_status) == 47));
bool daemon_clean =
daemon_waited == daemon && WIFEXITED(daemon_status) && WEXITSTATUS(daemon_status) == 0;
bool maintenance_completed =
!maintenance || (output_observed && mutation_status == CBM_VERSION_COHORT_OK &&
quiesce == CBM_VERSION_COHORT_QUIESCE_REQUESTED);
bool passed = prepared && announced && sentinel_ready && frontend_pipes && frontend > 0 &&
request_observed && maintenance_completed && frontend_exited_boundedly &&
cancelled && target_cancelled_exactly_once && sentinel_usable &&
sentinel_closed && shutdown_sent && daemon_done && daemon_clean &&
mutation_released && mutation_manager_closed && cleaned;
if (!passed) {
bool frontend_reaped = frontend > 0 && frontend_waited == frontend;
bool daemon_reaped = daemon > 0 && daemon_waited == daemon;
int frontend_exit =
frontend_reaped && WIFEXITED(frontend_status) ? WEXITSTATUS(frontend_status) : -1;
int frontend_signal =
frontend_reaped && WIFSIGNALED(frontend_status) ? WTERMSIG(frontend_status) : 0;
int daemon_exit =
daemon_reaped && WIFEXITED(daemon_status) ? WEXITSTATUS(daemon_status) : -1;
int daemon_signal =
daemon_reaped && WIFSIGNALED(daemon_status) ? WTERMSIG(daemon_status) : 0;
fprintf(stderr,
"frontend backpressure fixture failed: maintenance=%d prepared=%d daemon_pid=%ld "
"announced=%d announced_marker=0x%02x sentinel_ready=%d frontend_pipes=%d "
"frontend_pid=%ld frontend_reaped=%d frontend_status=0x%x "
"frontend_exit=%d frontend_signal=%d request_read=%d "
"request_marker=0x%02x output_observed=%d output_marker=0x%02x "
"mutation_status=%d quiesce=%d cancel_read=%d cancel_marker=0x%02x "
"checkpoint_read=%d checkpoint_marker=0x%02x sentinel_usable=%d "
"sentinel_closed=%d shutdown_sent=%d "
"done_read=%d done_marker=0x%02x daemon_reaped=%d "
"daemon_status=0x%x daemon_exit=%d daemon_signal=%d "
"mutation_released=%d mutation_manager_closed=%d cleaned=%d\n",
maintenance, prepared, (long)daemon, announced,
(unsigned int)(unsigned char)announced_marker, sentinel_ready, frontend_pipes,
(long)frontend, frontend_reaped, frontend_status, frontend_exit, frontend_signal,
request_read, (unsigned int)(unsigned char)request_marker, output_observed,
(unsigned int)(unsigned char)output_marker, mutation_status, quiesce, cancel_read,
(unsigned int)(unsigned char)cancel_marker, checkpoint_read,
(unsigned int)(unsigned char)checkpoint_marker, sentinel_usable, sentinel_closed,
shutdown_sent, done_read, (unsigned int)(unsigned char)done_marker, daemon_reaped,
daemon_status, daemon_exit, daemon_signal, mutation_released,
mutation_manager_closed, cleaned);
}
return passed;
}
static cbm_version_cohort_quiesce_result_t frontend_test_quiesce_requested(void *context) {
(void)context;
return CBM_VERSION_COHORT_QUIESCE_REQUESTED;
}
static bool frontend_test_cancel_active(void *context) {
int fd = *(int *)context;
const char marker = 'C';
return write(fd, &marker, 1) == 1;
}
static bool frontend_test_cancel_and_mark(void *context) {
atomic_bool *cancelled = context;
atomic_store_explicit(cancelled, true, memory_order_release);
return true;
}
#endif
TEST(daemon_frontend_recognizes_exact_cancellation_notification) {
ASSERT_TRUE(cbm_daemon_frontend_is_cancellation_notification(
"{\"jsonrpc\":\"2.0\",\"method\":\"notifications/cancelled\","
"\"params\":{\"requestId\":7}}"));
ASSERT_TRUE(cbm_daemon_frontend_is_cancellation_notification(
"{ \"params\": {}, \"method\": \"notifications/cancelled\", "
"\"jsonrpc\": \"2.0\" }"));
PASS();
}
/* RED on the whole-session cancellation shortcut: merely recognizing the
* notification method is not authority to close a session. The requestId
* must match the exact numeric/string identity currently being executed. */
TEST(daemon_frontend_correlates_cancellation_to_exact_request) {
ASSERT_TRUE(cbm_daemon_frontend_cancellation_matches_request(
"{\"jsonrpc\":\"2.0\",\"method\":\"notifications/cancelled\","
"\"params\":{\"requestId\":7}}",
7, NULL));
ASSERT_FALSE(cbm_daemon_frontend_cancellation_matches_request(
"{\"jsonrpc\":\"2.0\",\"method\":\"notifications/cancelled\","
"\"params\":{\"requestId\":8}}",
7, NULL));
ASSERT_TRUE(cbm_daemon_frontend_cancellation_matches_request(
"{\"jsonrpc\":\"2.0\",\"method\":\"notifications/cancelled\","
"\"params\":{\"requestId\":\"request-7\"}}",
-1, "request-7"));
ASSERT_FALSE(cbm_daemon_frontend_cancellation_matches_request(
"{\"jsonrpc\":\"2.0\",\"method\":\"notifications/cancelled\","
"\"params\":{\"requestId\":7}}",
-1, "7"));
ASSERT_FALSE(cbm_daemon_frontend_cancellation_matches_request(
"{\"jsonrpc\":\"2.0\",\"method\":\"notifications/cancelled\","
"\"params\":{}}",
7, NULL));
ASSERT_FALSE(
cbm_daemon_frontend_cancellation_matches_request("{\"jsonrpc\":\"2.0\",\"id\":9,"
"\"method\":\"notifications/cancelled\","
"\"params\":{\"requestId\":7}}",
7, NULL));
PASS();
}
TEST(daemon_frontend_ignores_cancellation_text_in_string_content) {
ASSERT_FALSE(cbm_daemon_frontend_is_cancellation_notification(
"{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"tools/call\","
"\"params\":{\"name\":\"search_graph\",\"arguments\":{\"query\":"
"\"notifications/cancelled\"}}}"));
ASSERT_FALSE(cbm_daemon_frontend_is_cancellation_notification(
"{\"jsonrpc\":\"2.0\",\"id\":2,\"method\":\"tools/call\","
"\"params\":{\"method\":\"notifications/cancelled\"}}"));
PASS();
}
TEST(daemon_frontend_rejects_non_notification_cancellation_shapes) {
ASSERT_FALSE(cbm_daemon_frontend_is_cancellation_notification(
"{\"jsonrpc\":\"2.0\",\"id\":3,"
"\"method\":\"notifications/cancelled\"}"));
ASSERT_FALSE(cbm_daemon_frontend_is_cancellation_notification(
"{\"jsonrpc\":\"2.0\",\"method\":\"notifications/cancelled-extra\"}"));
ASSERT_FALSE(cbm_daemon_frontend_is_cancellation_notification(
"{\"jsonrpc\":\"2.0\",\"method\":\"prefix/notifications/cancelled\"}"));
ASSERT_FALSE(cbm_daemon_frontend_is_cancellation_notification(
"{\"jsonrpc\":\"2.0\",\"params\":{\"method\":"
"\"notifications/cancelled\"}}"));
ASSERT_FALSE(cbm_daemon_frontend_is_cancellation_notification("not-json"));
ASSERT_FALSE(cbm_daemon_frontend_is_cancellation_notification(NULL));
PASS();
}
#ifndef _WIN32
/* RED: an MCP frontend's main thread can remain blocked forever in stdio while
* install/update/uninstall owns maintenance intent. The already-present
* frontend worker must observe that native intent and terminate this stateless
* child, releasing its cohort lease and kernel IPC ownership. A deliberately
* invalid client pointer is safe only if the maintenance path exits before
* ordinary EOF/session-close handling, which also pins that exact path. */
TEST(daemon_frontend_maintenance_exits_while_stdio_reader_is_blocked) {
frontend_maintenance_fixture_t fixture;
ASSERT_TRUE(frontend_maintenance_fixture_start(&fixture, "blocked-stdio"));
int input_pipe[2] = {-1, -1};
int ready_pipe[2] = {-1, -1};
bool pipes_ready = pipe(input_pipe) == 0 && pipe(ready_pipe) == 0;
pid_t child = pipes_ready ? fork() : -1;
if (child == 0) {
(void)close(input_pipe[1]);
(void)close(ready_pipe[0]);
cbm_daemon_ipc_endpoint_t *endpoint =
cbm_daemon_ipc_endpoint_new("0123456789abcdef", fixture.parent);
cbm_version_cohort_manager_t *manager =
endpoint ? cbm_version_cohort_manager_new(endpoint) : NULL;
cbm_version_cohort_lease_t *participant = NULL;
cbm_daemon_conflict_t conflict;
cbm_daemon_build_identity_t identity = frontend_test_identity();
cbm_version_cohort_status_t admitted =
manager ? cbm_version_cohort_acquire(manager, &identity, cbm_now_ms() + 2000U,
&participant, &conflict)
: CBM_VERSION_COHORT_IO;
FILE *input = admitted == CBM_VERSION_COHORT_OK ? fdopen(input_pipe[0], "rb") : NULL;
FILE *output = input ? tmpfile() : NULL;
const char ready = 'R';
bool announced = output && write(ready_pipe[1], &ready, 1) == 1;
(void)close(ready_pipe[1]);
if (!announced) {
_exit(70);
}
int result = cbm_daemon_frontend_mcp_run((cbm_daemon_runtime_client_t *)(uintptr_t)1,
manager, input, output);
(void)result;
_exit(71);
}
if (pipes_ready) {
(void)close(input_pipe[0]);
(void)close(ready_pipe[1]);
}
bool announced = child > 0 && frontend_test_wait_byte(ready_pipe[0], 'R', cbm_now_ms() + 2000U);
cbm_version_cohort_lease_t *mutation = NULL;
cbm_version_cohort_quiesce_result_t quiesce = CBM_VERSION_COHORT_QUIESCE_NOT_NEEDED;
cbm_version_cohort_status_t status =
announced ? cbm_version_cohort_reserve_for_mutation(fixture.manager, cbm_now_ms() + 3000U,
frontend_test_quiesce_requested, NULL,
&quiesce, &mutation)
: CBM_VERSION_COHORT_IO;
if (status != CBM_VERSION_COHORT_OK && child > 0) {
(void)kill(child, SIGKILL);
}
int child_status = 0;
bool waited = child > 0 && waitpid(child, &child_status, 0) == child;
if (pipes_ready) {
(void)close(input_pipe[1]);
(void)close(ready_pipe[0]);
}
frontend_test_release_lease(&mutation);
frontend_maintenance_fixture_finish(&fixture);
ASSERT_TRUE(pipes_ready);
ASSERT_TRUE(child > 0);
ASSERT_TRUE(announced);
ASSERT_EQ(status, CBM_VERSION_COHORT_OK);
ASSERT_EQ(quiesce, CBM_VERSION_COHORT_QUIESCE_REQUESTED);
ASSERT_TRUE(waited);
ASSERT_TRUE(WIFEXITED(child_status));
ASSERT_EQ(WEXITSTATUS(child_status), 0);
PASS();
}
/* RED: local CLI/worker work has no standing daemon session to interrupt. Its
* command-lifetime monitor must request cooperative cancellation once, allow a
* bounded grace, then hard-exit the isolated process so SQLite/native locks
* roll back and the mutation barrier can prove that every lifetime participant
* is gone. */
TEST(daemon_local_participant_monitor_cancels_then_bounds_active_operation) {
frontend_maintenance_fixture_t fixture;
ASSERT_TRUE(frontend_maintenance_fixture_start(&fixture, "local-monitor"));
int ready_pipe[2] = {-1, -1};
int cancel_pipe[2] = {-1, -1};
bool pipes_ready = pipe(ready_pipe) == 0 && pipe(cancel_pipe) == 0;
pid_t child = pipes_ready ? fork() : -1;
if (child == 0) {
(void)close(ready_pipe[0]);
(void)close(cancel_pipe[0]);
cbm_daemon_ipc_endpoint_t *endpoint =
cbm_daemon_ipc_endpoint_new("0123456789abcdef", fixture.parent);
cbm_version_cohort_manager_t *manager =
endpoint ? cbm_version_cohort_manager_new(endpoint) : NULL;
cbm_version_cohort_lease_t *participant = NULL;
cbm_daemon_conflict_t conflict;
cbm_daemon_build_identity_t identity = frontend_test_identity();
cbm_version_cohort_status_t admitted =
manager ? cbm_version_cohort_acquire(manager, &identity, cbm_now_ms() + 2000U,
&participant, &conflict)
: CBM_VERSION_COHORT_IO;
cbm_daemon_maintenance_monitor_t *monitor =
admitted == CBM_VERSION_COHORT_OK
? cbm_daemon_maintenance_monitor_start(manager, frontend_test_cancel_active,
&cancel_pipe[1], 37, "test-local-operation")
: NULL;
const char ready = 'R';
bool announced = monitor && write(ready_pipe[1], &ready, 1) == 1;
(void)close(ready_pipe[1]);
if (!announced) {
_exit(72);
}
for (;;) {
cbm_usleep(100000);
}
}
if (pipes_ready) {
(void)close(ready_pipe[1]);
(void)close(cancel_pipe[1]);
}
bool announced = child > 0 && frontend_test_wait_byte(ready_pipe[0], 'R', cbm_now_ms() + 2000U);
cbm_version_cohort_lease_t *mutation = NULL;
cbm_version_cohort_quiesce_result_t quiesce = CBM_VERSION_COHORT_QUIESCE_NOT_NEEDED;
cbm_version_cohort_status_t status =
announced ? cbm_version_cohort_reserve_for_mutation(fixture.manager, cbm_now_ms() + 5000U,
frontend_test_quiesce_requested, NULL,
&quiesce, &mutation)
: CBM_VERSION_COHORT_IO;
bool cancelled = status == CBM_VERSION_COHORT_OK &&
frontend_test_wait_byte(cancel_pipe[0], 'C', cbm_now_ms() + 1000U);
if (status != CBM_VERSION_COHORT_OK && child > 0) {
(void)kill(child, SIGKILL);
}
int child_status = 0;
bool waited = child > 0 && waitpid(child, &child_status, 0) == child;
if (pipes_ready) {
(void)close(ready_pipe[0]);
(void)close(cancel_pipe[0]);
}
frontend_test_release_lease(&mutation);
frontend_maintenance_fixture_finish(&fixture);
ASSERT_TRUE(pipes_ready);
ASSERT_TRUE(child > 0);
ASSERT_TRUE(announced);
ASSERT_EQ(status, CBM_VERSION_COHORT_OK);
ASSERT_EQ(quiesce, CBM_VERSION_COHORT_QUIESCE_REQUESTED);
ASSERT_TRUE(cancelled);
ASSERT_TRUE(waited);
ASSERT_TRUE(WIFEXITED(child_status));
ASSERT_EQ(WEXITSTATUS(child_status), 37);
PASS();
}
/* RED: supervised process-tree cancellation permits one second of graceful
* shutdown followed by one second of forced containment. The maintenance
* observer must not _Exit the owning process before that bounded supervisor
* window can finish and join the observer normally. */
TEST(daemon_local_participant_monitor_allows_supervisor_containment_window) {
frontend_maintenance_fixture_t fixture;
ASSERT_TRUE(frontend_maintenance_fixture_start(&fixture, "monitor-window"));
int ready_pipe[2] = {-1, -1};
bool pipe_ready = pipe(ready_pipe) == 0;
pid_t child = pipe_ready ? fork() : -1;
if (child == 0) {
(void)close(ready_pipe[0]);
cbm_daemon_ipc_endpoint_t *endpoint =
cbm_daemon_ipc_endpoint_new("0123456789abcdef", fixture.parent);
cbm_version_cohort_manager_t *manager =
endpoint ? cbm_version_cohort_manager_new(endpoint) : NULL;
cbm_version_cohort_lease_t *participant = NULL;
cbm_daemon_conflict_t conflict;
cbm_daemon_build_identity_t identity = frontend_test_identity();
cbm_version_cohort_status_t admitted =
manager ? cbm_version_cohort_acquire(manager, &identity, cbm_now_ms() + 2000U,
&participant, &conflict)
: CBM_VERSION_COHORT_IO;
atomic_bool cancelled;
atomic_init(&cancelled, false);
cbm_daemon_maintenance_monitor_t *monitor =
admitted == CBM_VERSION_COHORT_OK
? cbm_daemon_maintenance_monitor_start(manager, frontend_test_cancel_and_mark,
&cancelled, 38, "test-supervisor-window")
: NULL;
const char ready = 'R';
bool announced = monitor && write(ready_pipe[1], &ready, 1) == 1;
(void)close(ready_pipe[1]);
if (!announced) {
_exit(72);
}
while (!atomic_load_explicit(&cancelled, memory_order_acquire)) {
cbm_usleep(1000);
}
/* Model the maximum graceful + forced-settle supervisor bounds. */
cbm_usleep(2100U * 1000U);
bool stopped = cbm_daemon_maintenance_monitor_stop(&monitor);
frontend_test_release_lease(&participant);
while (manager && cbm_version_cohort_manager_free(&manager) != CBM_PRIVATE_FILE_LOCK_OK) {
cbm_usleep(1000);
}
cbm_daemon_ipc_endpoint_free(endpoint);
_exit(stopped ? 0 : 73);
}
if (pipe_ready) {
(void)close(ready_pipe[1]);
}
bool announced = child > 0 && frontend_test_wait_byte(ready_pipe[0], 'R', cbm_now_ms() + 2000U);
cbm_version_cohort_lease_t *mutation = NULL;
cbm_version_cohort_quiesce_result_t quiesce = CBM_VERSION_COHORT_QUIESCE_NOT_NEEDED;
cbm_version_cohort_status_t status =
announced ? cbm_version_cohort_reserve_for_mutation(fixture.manager, cbm_now_ms() + 5000U,
frontend_test_quiesce_requested, NULL,
&quiesce, &mutation)
: CBM_VERSION_COHORT_IO;
if (status != CBM_VERSION_COHORT_OK && child > 0) {
(void)kill(child, SIGKILL);
}
int child_status = 0;
bool waited = child > 0 && waitpid(child, &child_status, 0) == child;
if (pipe_ready) {
(void)close(ready_pipe[0]);
}
frontend_test_release_lease(&mutation);
frontend_maintenance_fixture_finish(&fixture);
ASSERT_TRUE(pipe_ready);
ASSERT_TRUE(child > 0);
ASSERT_TRUE(announced);
ASSERT_EQ(status, CBM_VERSION_COHORT_OK);
ASSERT_EQ(quiesce, CBM_VERSION_COHORT_QUIESCE_REQUESTED);
ASSERT_TRUE(waited);
ASSERT_TRUE(WIFEXITED(child_status));
ASSERT_EQ(WEXITSTATUS(child_status), 0);
PASS();
}
/* RED: once a long request is active, a buffered writer can fill the bounded
* frontend queue and close its pipe. Waiting indefinitely for queue capacity
* prevents the sole reader from observing that already-pending EOF, so neither
* the request nor its daemon session is cancelled. Overload must instead fail
* the frontend promptly and close/cancel the authenticated session. */
TEST(daemon_frontend_over_capacity_input_cannot_hide_eof_behind_active_request) {
ASSERT_TRUE(frontend_eof_run_isolated("overflow", true));
PASS();
}
/* Clean EOF is the normal MCP-session ownership boundary. Accepted work gets a
* bounded drain opportunity; work still active at the deadline is cancelled
* with that exact session, while the frontend itself reports a clean close. */
TEST(daemon_frontend_eof_drain_timeout_cancels_and_returns_success) {
ASSERT_TRUE(frontend_eof_run_isolated("timeout", false));
PASS();
}
/* A daemon response can finish its IPC exchange and then block forever while
* writing to an agent that stopped reading stdout. EOF must still bound the
* thin frontend process. Keep the daemon in a separate child so the frontend's
* terminal watchdog closes a real kernel connection; the daemon must observe
* that close, cancel the exact session, and shut down normally. */
TEST(daemon_frontend_stdout_backpressure_eof_fail_stops_and_cancels_session) {
ASSERT_TRUE(frontend_backpressure_run_isolated(false));
PASS();
}
/* Maintenance must not depend on either MCP stdio thread making progress.
* Keep stdin open, fill stdout with a completed daemon response, then publish
* activation intent. The independent monitor must release the cohort lease and
* kernel session within its bound; the daemon must cancel that exact session. */
TEST(daemon_frontend_stdout_backpressure_maintenance_stops_and_cancels_session) {
ASSERT_TRUE(frontend_backpressure_run_isolated(true));
PASS();
}
#endif
TEST(daemon_local_participant_monitor_joins_before_manager_teardown) {
frontend_maintenance_fixture_t fixture;
ASSERT_TRUE(frontend_maintenance_fixture_start(&fixture, "monitor-join"));
cbm_daemon_maintenance_monitor_t *monitor = cbm_daemon_maintenance_monitor_start(
fixture.manager, NULL, NULL, EXIT_FAILURE, "test-idle-command");
bool started = monitor != NULL;
bool stopped = started && cbm_daemon_maintenance_monitor_stop(&monitor);
bool consumed = monitor == NULL;
frontend_maintenance_fixture_finish(&fixture);
ASSERT_TRUE(started);
ASSERT_TRUE(stopped);
ASSERT_TRUE(consumed);
PASS();
}
SUITE(daemon_frontend) {
RUN_TEST(daemon_frontend_recognizes_exact_cancellation_notification);
RUN_TEST(daemon_frontend_correlates_cancellation_to_exact_request);
RUN_TEST(daemon_frontend_ignores_cancellation_text_in_string_content);
RUN_TEST(daemon_frontend_rejects_non_notification_cancellation_shapes);
#ifndef _WIN32
RUN_TEST(daemon_frontend_maintenance_exits_while_stdio_reader_is_blocked);
RUN_TEST(daemon_local_participant_monitor_cancels_then_bounds_active_operation);
RUN_TEST(daemon_local_participant_monitor_allows_supervisor_containment_window);
RUN_TEST(daemon_frontend_over_capacity_input_cannot_hide_eof_behind_active_request);
RUN_TEST(daemon_frontend_eof_drain_timeout_cancels_and_returns_success);
RUN_TEST(daemon_frontend_stdout_backpressure_eof_fail_stops_and_cancels_session);
RUN_TEST(daemon_frontend_stdout_backpressure_maintenance_stops_and_cancels_session);
#endif
RUN_TEST(daemon_local_participant_monitor_joins_before_manager_teardown);
}