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:
@@ -10,6 +10,8 @@
|
|||||||
#include <jack/midiport.h>
|
#include <jack/midiport.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/resource.h>
|
||||||
#include <stdatomic.h>
|
#include <stdatomic.h>
|
||||||
|
|
||||||
#ifndef M_PI
|
#ifndef M_PI
|
||||||
@@ -18,12 +20,15 @@
|
|||||||
|
|
||||||
// Test configuration
|
// Test configuration
|
||||||
#define TEST_SAMPLE_RATE 48000
|
#define TEST_SAMPLE_RATE 48000
|
||||||
#define TEST_NFRAMES 1024
|
#define TEST_NFRAMES 256
|
||||||
#define TEST_DURATION_SECONDS 2
|
#define TEST_DURATION_SECONDS 1
|
||||||
#define MAX_CHANNELS 8
|
#define MAX_CHANNELS 8
|
||||||
#define MAX_SCENES 8
|
#define MAX_SCENES 8
|
||||||
#define MAX_CLIPS 512
|
#define MAX_CLIPS 512
|
||||||
|
|
||||||
|
// Hard timeout (seconds)
|
||||||
|
#define TEST_TIMEOUT_SECONDS 30
|
||||||
|
|
||||||
// Global test state
|
// Global test state
|
||||||
static pid_t looper_pid = -1;
|
static pid_t looper_pid = -1;
|
||||||
static jack_client_t *test_client = NULL;
|
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_out;
|
||||||
static jack_port_t *test_midi_in;
|
static jack_port_t *test_midi_in;
|
||||||
static atomic_bool test_running;
|
static atomic_bool test_running;
|
||||||
|
static atomic_bool test_timeout;
|
||||||
static atomic_int tests_passed;
|
static atomic_int tests_passed;
|
||||||
static atomic_int tests_failed;
|
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 float test_output_buffer[MAX_CHANNELS][TEST_NFRAMES * TEST_DURATION_SECONDS];
|
||||||
static atomic_size_t test_output_count[MAX_CHANNELS];
|
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) {
|
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();
|
looper_pid = fork();
|
||||||
if (looper_pid == 0) {
|
if (looper_pid == 0) {
|
||||||
// Child process - start jack-looper
|
// 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);
|
execl("./jack-looper", "jack-looper", "-n", client_name, "-i", NULL);
|
||||||
perror("execl failed");
|
perror("execl failed");
|
||||||
exit(1);
|
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 total_samples = (sample_rate * duration) / 1000;
|
||||||
jack_nframes_t processed = 0;
|
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)
|
jack_nframes_t nframes = (total_samples - processed < TEST_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;
|
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) {
|
static void send_midi_note(int note, int velocity) {
|
||||||
if (!test_client) return;
|
if (!test_client) return;
|
||||||
|
if (atomic_load(&test_timeout)) return;
|
||||||
|
|
||||||
jack_nframes_t nframes = TEST_NFRAMES;
|
jack_nframes_t nframes = TEST_NFRAMES;
|
||||||
void *buf = jack_port_get_buffer(test_midi_out, nframes);
|
void *buf = jack_port_get_buffer(test_midi_out, nframes);
|
||||||
@@ -369,13 +402,48 @@ static void test_volume_control(void) {
|
|||||||
// Main test runner
|
// 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) {
|
int main(void) {
|
||||||
printf("=== Audio and MIDI Routing Integration Tests ===\n");
|
printf("=== Audio and MIDI Routing Integration Tests ===\n");
|
||||||
printf("Note: These tests require JACK to be running\n\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_passed, 0);
|
||||||
atomic_store(&tests_failed, 0);
|
atomic_store(&tests_failed, 0);
|
||||||
atomic_store(&test_running, true);
|
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
|
// Start jack-looper
|
||||||
printf("Starting jack-looper...\n");
|
printf("Starting jack-looper...\n");
|
||||||
|
|||||||
Reference in New Issue
Block a user