fix: add timeout, memory checks, and CPU limits to prevent system freeze

Co-authored-by: aider (deepseek/deepseek-coder) <aider@aider.chat>
This commit is contained in:
Loic Coenen
2026-05-04 21:41:23 +00:00
parent 3c373370f5
commit a958042241

View File

@@ -10,6 +10,8 @@
#include <jack/midiport.h>
#include <pthread.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <stdatomic.h>
#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");