From a9580422419961fffe5b63f1e800e7fb06e0cc55 Mon Sep 17 00:00:00 2001 From: Loic Coenen Date: Mon, 4 May 2026 21:41:23 +0000 Subject: [PATCH] fix: add timeout, memory checks, and CPU limits to prevent system freeze Co-authored-by: aider (deepseek/deepseek-coder) --- test_audio_routing.c | 78 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 73 insertions(+), 5 deletions(-) diff --git a/test_audio_routing.c b/test_audio_routing.c index 0415739..c38f0d2 100644 --- a/test_audio_routing.c +++ b/test_audio_routing.c @@ -10,6 +10,8 @@ #include #include #include +#include +#include #include #ifndef M_PI @@ -18,12 +20,15 @@ // Test configuration #define TEST_SAMPLE_RATE 48000 -#define TEST_NFRAMES 1024 -#define TEST_DURATION_SECONDS 2 +#define TEST_NFRAMES 256 +#define TEST_DURATION_SECONDS 1 #define MAX_CHANNELS 8 #define MAX_SCENES 8 #define MAX_CLIPS 512 +// Hard timeout (seconds) +#define TEST_TIMEOUT_SECONDS 30 + // Global test state static pid_t looper_pid = -1; static jack_client_t *test_client = NULL; @@ -32,10 +37,11 @@ static jack_port_t *test_audio_in[MAX_CHANNELS]; static jack_port_t *test_midi_out; static jack_port_t *test_midi_in; static atomic_bool test_running; +static atomic_bool test_timeout; static atomic_int tests_passed; static atomic_int tests_failed; -// Test audio buffers +// Test audio buffers (reduced size) static float test_output_buffer[MAX_CHANNELS][TEST_NFRAMES * TEST_DURATION_SECONDS]; static atomic_size_t test_output_count[MAX_CHANNELS]; @@ -92,9 +98,21 @@ static int test_process_callback(jack_nframes_t nframes, void *arg) { // ============================================================ static int start_looper(const char *client_name) { + // Check if binary exists + if (access("./jack-looper", X_OK) != 0) { + fprintf(stderr, "Error: ./jack-looper binary not found or not executable\n"); + return -1; + } + looper_pid = fork(); if (looper_pid == 0) { // Child process - start jack-looper + // Set CPU limit for child + struct rlimit cpu_limit; + cpu_limit.rlim_cur = 10; // 10 seconds CPU time + cpu_limit.rlim_max = 10; + setrlimit(RLIMIT_CPU, &cpu_limit); + execl("./jack-looper", "jack-looper", "-n", client_name, "-i", NULL); perror("execl failed"); exit(1); @@ -190,7 +208,21 @@ static void send_sine_wave(int channel, float frequency, float amplitude, jack_n jack_nframes_t total_samples = (sample_rate * duration) / 1000; jack_nframes_t processed = 0; - while (processed < total_samples && atomic_load(&test_running)) { + struct timeval start_time; + gettimeofday(&start_time, NULL); + + while (processed < total_samples && atomic_load(&test_running) && !atomic_load(&test_timeout)) { + // Check for timeout + struct timeval now; + gettimeofday(&now, NULL); + double elapsed = (now.tv_sec - start_time.tv_sec) + + (now.tv_usec - start_time.tv_usec) / 1000000.0; + if (elapsed > TEST_TIMEOUT_SECONDS) { + fprintf(stderr, "TIMEOUT: send_sine_wave exceeded %d seconds\n", TEST_TIMEOUT_SECONDS); + atomic_store(&test_timeout, true); + break; + } + jack_nframes_t nframes = (total_samples - processed < TEST_NFRAMES) ? (total_samples - processed) : TEST_NFRAMES; @@ -202,12 +234,13 @@ static void send_sine_wave(int channel, float frequency, float amplitude, jack_n } processed += nframes; - usleep(10000); // 10ms delay to let JACK process + usleep(50000); // 50ms delay to reduce CPU usage } } static void send_midi_note(int note, int velocity) { if (!test_client) return; + if (atomic_load(&test_timeout)) return; jack_nframes_t nframes = TEST_NFRAMES; void *buf = jack_port_get_buffer(test_midi_out, nframes); @@ -369,13 +402,48 @@ static void test_volume_control(void) { // Main test runner // ============================================================ +// Watchdog thread - hard kill if test hangs +static void *watchdog_thread(void *arg) { + (void)arg; + sleep(TEST_TIMEOUT_SECONDS + 5); // 5 seconds grace period + if (atomic_load(&test_running)) { + fprintf(stderr, "WATCHDOG: Test exceeded %d seconds, killing process\n", + TEST_TIMEOUT_SECONDS + 5); + kill(getpid(), SIGKILL); + } + return NULL; +} + int main(void) { printf("=== Audio and MIDI Routing Integration Tests ===\n"); printf("Note: These tests require JACK to be running\n\n"); + // Check available memory + long pages = sysconf(_SC_PHYS_PAGES); + long page_size = sysconf(_SC_PAGE_SIZE); + long phys_mem = pages * page_size; + long needed = sizeof(test_output_buffer); + if (needed > phys_mem / 2) { + fprintf(stderr, "Not enough memory for test buffers (need %ld, have %ld)\n", + needed, phys_mem / 2); + return 1; + } + + // Set CPU limit for this process + struct rlimit cpu_limit; + cpu_limit.rlim_cur = TEST_TIMEOUT_SECONDS; + cpu_limit.rlim_max = TEST_TIMEOUT_SECONDS; + setrlimit(RLIMIT_CPU, &cpu_limit); + atomic_store(&tests_passed, 0); atomic_store(&tests_failed, 0); atomic_store(&test_running, true); + atomic_store(&test_timeout, false); + + // Start watchdog thread + pthread_t watchdog; + pthread_create(&watchdog, NULL, watchdog_thread, NULL); + pthread_detach(watchdog); // Start jack-looper printf("Starting jack-looper...\n");