// test_uasync_random.c - Test uasync instance with random timeouts #include "u_async.h" #include #include #include #include #define MAX_TIMEOUTS 1000 #define MAX_TIMEOUT_TB 10000 // 1 second in timebase units (0.1 ms) #define CANCEL_PROBABILITY 20 // 20% chance to cancel a timeout #define NUM_ITERATIONS 50000 typedef struct { int id; int fired; int cancelled; uasync_t* ua; } test_context_t; static int g_fired_count = 0; static int g_cancelled_count = 0; static int g_expected_fired = 0; static void timeout_callback(void* arg) { test_context_t* ctx = (test_context_t*)arg; if (!ctx) return; if (ctx->cancelled) { printf("ERROR: Cancelled timeout %d fired!\n", ctx->id); exit(1); } ctx->fired = 1; g_fired_count++; // Verify the timeout was removed from heap // (we can't directly check, but we trust the implementation) } static void run_random_test(void) { printf("=== Random uasync timeout test ===\n"); // Create uasync instance uasync_t* ua = uasync_create(); if (!ua) { printf("FAIL: uasync_create returned NULL\n"); exit(1); } printf("Created uasync instance\n"); // Allocate test contexts test_context_t* contexts = malloc(MAX_TIMEOUTS * sizeof(test_context_t)); if (!contexts) { printf("FAIL: malloc failed\n"); uasync_destroy(ua); exit(1); } memset(contexts, 0, MAX_TIMEOUTS * sizeof(test_context_t)); srand(time(NULL)); // Set random timeouts int active_timeouts = 0; for (int i = 0; i < MAX_TIMEOUTS; i++) { contexts[i].id = i; contexts[i].ua = ua; // Random timeout between 1 and MAX_TIMEOUT_TB int timeout_tb = (rand() % MAX_TIMEOUT_TB) + 1; void* handle = uasync_set_timeout(ua, timeout_tb, &contexts[i], timeout_callback); if (!handle) { printf("WARN: uasync_set_timeout failed for timeout %d\n", i); continue; } // Store handle in context (we could store it, but we'll just track) contexts[i].fired = 0; contexts[i].cancelled = 0; active_timeouts++; // Randomly cancel some timeouts if (rand() % 100 < CANCEL_PROBABILITY) { if (uasync_cancel_timeout(ua, handle) == ERR_OK) { contexts[i].cancelled = 1; g_cancelled_count++; active_timeouts--; } else { printf("WARN: Failed to cancel timeout %d\n", i); } } } g_expected_fired = active_timeouts; printf("Set %d timeouts, cancelled %d, expecting %d to fire\n", MAX_TIMEOUTS, g_cancelled_count, g_expected_fired); // Run event loop for enough time to fire all timeouts // Calculate maximum timeout in milliseconds plus some margin int max_timeout_ms = (MAX_TIMEOUT_TB + 1000) / 10; // convert to ms with margin int iterations = 0; while (g_fired_count < g_expected_fired && iterations < NUM_ITERATIONS) { // Poll with short timeout (10ms = 100 timebase units) uasync_poll(ua, 100); iterations++; // Check if we've waited long enough if (iterations * 10 > max_timeout_ms * 5) { // We've waited five times the max timeout, something might be wrong if (g_fired_count < g_expected_fired) { printf("WARN: Only %d of %d timeouts fired after %d ms\n", g_fired_count, g_expected_fired, iterations * 10); break; } } } // Verify all non-cancelled timeouts fired int failed_fires = 0; for (int i = 0; i < MAX_TIMEOUTS; i++) { if (!contexts[i].cancelled && !contexts[i].fired) { failed_fires++; if (failed_fires < 10) { // Limit output printf("ERROR: Timeout %d didn't fire (not cancelled)\n", i); } } if (contexts[i].cancelled && contexts[i].fired) { printf("ERROR: Cancelled timeout %d fired\n", i); failed_fires++; } } if (failed_fires > 0) { printf("FAIL: %d timeout failures\n", failed_fires); } else { printf("PASS: All %d expected timeouts fired correctly\n", g_fired_count); } // Verify that no cancelled timeouts fired (already checked above) printf("Cancelled timeouts: %d (none should fire)\n", g_cancelled_count); // Cleanup free(contexts); uasync_destroy(ua); if (failed_fires == 0) { printf("=== Test PASSED ===\n"); } else { printf("=== Test FAILED ===\n"); exit(1); } } static void simple_callback(void* arg) { int* flag = (int*)arg; *flag = 1; } static void free_callback(void* arg) { free(arg); } static void test_instance_isolation(void) { printf("\n=== Instance isolation test ===\n"); // Create two independent instances uasync_t* ua1 = uasync_create(); uasync_t* ua2 = uasync_create(); if (!ua1 || !ua2) { printf("FAIL: Failed to create instances\n"); exit(1); } int fired1 = 0; int fired2 = 0; void* timeout1 = uasync_set_timeout(ua1, 10, &fired1, simple_callback); void* timeout2 = uasync_set_timeout(ua2, 20, &fired2, simple_callback); if (!timeout1 || !timeout2) { printf("FAIL: Failed to set timeouts\n"); exit(1); } // Run first instance - should fire timeout1 only uasync_poll(ua1, 50); // 5ms wait if (fired1 != 1) { printf("FAIL: Instance 1 timeout didn't fire\n"); exit(1); } if (fired2 != 0) { printf("FAIL: Instance 2 timeout fired incorrectly\n"); exit(1); } // Cancel timeout2 if (uasync_cancel_timeout(ua2, timeout2) != ERR_OK) { printf("FAIL: Failed to cancel timeout2\n"); exit(1); } // Run second instance - nothing should fire fired1 = 0; uasync_poll(ua2, 50); if (fired1 != 0) { printf("FAIL: Instance 1 timeout fired from instance 2\n"); exit(1); } if (fired2 != 0) { printf("FAIL: Cancelled timeout fired\n"); exit(1); } uasync_destroy(ua1); uasync_destroy(ua2); printf("PASS: Instance isolation works correctly\n"); } static void test_memory_cleanup(void) { printf("\n=== Memory cleanup test ===\n"); uasync_t* ua = uasync_create(); if (!ua) { printf("FAIL: uasync_create failed\n"); exit(1); } // Set many timeouts for (int i = 0; i < 100; i++) { int* counter = malloc(sizeof(int)); *counter = i; uasync_set_timeout(ua, 1000 + i, counter, free_callback); } // Destroy instance without waiting for timeouts // This should free all pending timeout nodes uasync_destroy(ua); printf("PASS: Memory cleanup completed\n"); } int main(void) { printf("Starting uasync random timeout tests\n"); printf("====================================\n"); run_random_test(); test_instance_isolation(); test_memory_cleanup(); printf("\n====================================\n"); printf("All tests PASSED!\n"); return 0; }