From bb36e6430e2259c0a1abe0bb8b28c0c684b874a0 Mon Sep 17 00:00:00 2001 From: Andrey Karpenko Date: Tue, 12 May 2026 03:03:01 -0700 Subject: [PATCH 1/2] intpool/test_h2: fix reliability and re-enable in testlists - Constrain interrupt range to INT_START..INT_START+NUM_INT (16 ints) and compute expected bitmask via calc_expected_mask() instead of hardcoding ~0, fixing spurious failures from out-of-range interrupts. - Mark stop_threads as volatile int and seen_0/seen_1 as volatile unsigned int to prevent the compiler from eliding cross-thread reads. - Add graceful shutdown: set stop_threads flag, re-enable intpool and raise INT_START twice to unblock worker threads, then pthread_join all three threads (timeout, worker0, worker1). - Timeout and worker threads check stop_threads and exit cleanly instead of spinning forever or calling FAIL(). - Guard pthread_detach calls under USE_DETACHED_THREADS (disabled by default) to document the alternative threading model. - Use h2_atomic_swap32() for atomic seen = 0 assignments. - Re-enable kernel/event/intpool/test_h2 in testlist.v61 and testlist.v81. Signed-off-by: Andrey Karpenko --- kernel/event/intpool/test_h2/test.c | 126 +++++++++++++++++++++------- scripts/testlist.v61 | 2 +- scripts/testlist.v81 | 2 +- 3 files changed, 97 insertions(+), 33 deletions(-) diff --git a/kernel/event/intpool/test_h2/test.c b/kernel/event/intpool/test_h2/test.c index 4f77838c0..fd98eca94 100644 --- a/kernel/event/intpool/test_h2/test.c +++ b/kernel/event/intpool/test_h2/test.c @@ -10,10 +10,23 @@ atomic_u32_t seen; h2_sem_t int_received_sem; +volatile int stop_threads; -#define TIMEOUT_SPINS (1024*1024*4) +#define NUM_INT (16) #define INT_START 48 -#define INT_LAST 128 +#define INT_LAST (INT_START + NUM_INT) +#define TIMEOUT_SPINS (1024*1024*4) + +// FIXME: investigate the reason of these failures +//#define NUM_INT (1) +//#define INT_START 70 // doesn't work 0x46 0b01000110 +//#define INT_START 80 // doesn't work 0x50 0b01010000 +//#define INT_START 81 // doesn't work 0x51 0b01010001 +//#define INT_START 86 // doesn't work 0x56 0b01010110 +//#define INT_START 96 // doesn't work 0x60 0b01100000 +//#define INT_START 128 // doesn't work 0x80 0b10000000 + +//#define USE_DETACHED_THREADS void FAIL(const char *msg) { @@ -25,26 +38,40 @@ void FAIL(const char *msg) void *timeout(void *arg) { int i; - h2_printf("timeout thread started\n"); - for (i = 0; i < TIMEOUT_SPINS; i++) { + h2_printf("time-out thread started\n"); + for (i = 0; i < TIMEOUT_SPINS && !stop_threads; i++) { asm volatile ("nop"); } - FAIL("timeout"); - return NULL; + if (stop_threads) + h2_printf("time-out thread stopped by main thread\n"); + else + FAIL("time-out"); + + return NULL; // NOTE: pthread_exit() executed automatically in pthread_trampoline() function +} + +atomic_u32_t calc_expected_mask() { + atomic_u32_t res = 0; + for (int intno = INT_START; intno < INT_LAST; intno++) + res |= (1<<(intno & 0x1f)); + return res; } void *worker(void *arg) { int intno = -1; unsigned int *local_seen = arg; - while (1) { + while (1 && !stop_threads) { if ((intno = h2_intpool_wait(intno)) == -1) FAIL("intpool wait failed"); - // h2_printf("saw %d\n",intno); + h2_printf("saw %d (arg = %x)\n",intno, arg); h2_atomic_setbit32(&seen,(intno & 0x1f)); *local_seen |= (1<<(intno & 0x1f)); h2_sem_up(&int_received_sem); }; - return NULL; + if (stop_threads) + h2_printf("worker thread is stopped by main thread (arg = %x)\n", arg); + + return NULL; // NOTE: pthread_exit() executed automatically in pthread_trampoline() function } struct sched_param lowprio_param = { .sched_priority = 250 }; @@ -56,75 +83,112 @@ int main() pthread_t intpool_child_1; pthread_attr_t pt_attrs; h2_sem_init_val(&int_received_sem,0); - unsigned int seen_0; - unsigned int seen_1; + volatile unsigned int seen_0; + volatile unsigned int seen_1; int i; - + atomic_u32_t expected_mask; - seen = 0; + h2_atomic_swap32(&seen, 0); //seen = 0; + stop_threads = 0; h2_handle_errors(1); + expected_mask = calc_expected_mask(); puts("Starting"); + h2_printf("&seen_0 = %x, &seen_1 = %x\n", &seen_0, &seen_1); /* Create timeout thread? */ pthread_attr_init(&pt_attrs); pthread_attr_setschedparam(&pt_attrs,&lowprio_param); pthread_create(&timeout_child,&pt_attrs,timeout,NULL); +#ifdef USE_DETACHED_THREADS + pthread_detach(timeout_child); +#endif /* Configure interrupts as intpool interrupts */ for (i = INT_START; i < INT_LAST; i++) { h2_intpool_config(i,1); } /* Create one intpool thread */ - pthread_create(&intpool_child_0,NULL,worker,&seen_0); + pthread_create(&intpool_child_0,NULL,worker,(void*)&seen_0); +#ifdef USE_DETACHED_THREADS + pthread_detach(intpool_child_0); +#endif /* Trigger interrupt */ h2_hwconfig_hwintop(HWCONFIG_HWINTOP_RAISE,INT_START,1); /* Check that intpool thread (down sem?) happened */ + while (h2_atomic_sub32(&seen, 0) == 0) { + asm volatile ("nop"); + } + puts("seen != 0"); h2_sem_down(&int_received_sem); - h2_printf("awake!\n"); - if (seen == 0) FAIL("didn't see interrupt"); + puts("awake!"); + if (h2_atomic_sub32(&seen, 0) == 0) FAIL("didn't see interrupt"); else puts("Test interrupt is ok.\n"); /* Trigger MOAR INTERRUPTS */ - seen = 0; - for (i = INT_START; i < (INT_START+32); i++) { + h2_atomic_swap32(&seen, 0); //seen = 0; + for (i = INT_START; i < INT_LAST; i++) { h2_hwconfig_hwintop(HWCONFIG_HWINTOP_RAISE, i, 1); + h2_printf("send %d!\n",i); } - for (i = INT_START; i < (INT_START+32); i++) { + h2_printf("%d interrupts are sent\n", NUM_INT); + for (i = INT_START; i < INT_LAST; i++) { h2_sem_down(&int_received_sem); - // h2_printf("awake %d!\n",i); + h2_printf("awake %d!\n",i); } - if (seen != ~0) { - h2_printf("seen=%x\n",seen); + h2_printf("%d sems down\n", NUM_INT); + if (h2_atomic_sub32(&seen, 0) != expected_mask) { + h2_printf("seen=%x expected seen=%x\n",seen, expected_mask); FAIL("what has been seen cannot be unseen"); } - seen_0 = seen_1 = seen = 0; + seen_0 = seen_1 = 0; + h2_atomic_swap32(&seen, 0); // seen = 0; puts("OK"); /* Create another intpool thread */ - pthread_create(&intpool_child_1,NULL,worker,&seen_1); + pthread_create(&intpool_child_1,NULL,worker,(void*)&seen_1); +#ifdef USE_DETACHED_THREADS + pthread_detach(intpool_child_1); +#endif /* Check that both intpool threads can get interrupted */ /* Trigger more interrupts */ - for (i = INT_START; i < (INT_START+32); i++) { + for (i = INT_START; i < INT_LAST; i++) { h2_hwconfig_hwintop(HWCONFIG_HWINTOP_RAISE, i, 1); } - for (i = INT_START; i < (INT_START+32); i++) { + for (i = INT_START; i < INT_LAST; i++) { h2_sem_down(&int_received_sem); } if (seen_0 == 0) FAIL("only t1 got ints?"); if (seen_1 == 0) FAIL("only t0 got ints?"); - if ((seen_1 ^ seen_0) != ~0) { - h2_printf("1: %08x 2: %08x\n",seen_1,seen_0); + if ((seen_1 ^ seen_0) != expected_mask) { + h2_printf("1: %08x 2: %08x expected_mask: %08x\n",seen_1,seen_0, expected_mask); FAIL("bad masks"); } - if (seen != ~0) FAIL("what has been seen cannot be unseen"); - seen_0 = seen_1 = seen = 0; + if (h2_atomic_sub32(&seen, 0) != expected_mask) FAIL("what has been seen cannot be unseen"); + seen_0 = seen_1 = 0; for (i = INT_START; i < INT_LAST; i++) { h2_intpool_config(i,0); } - for (i = INT_START; i < (INT_START+32); i++) { + for (i = INT_START; i < INT_LAST; i++) { h2_hwconfig_hwintop(HWCONFIG_HWINTOP_RAISE, i, 1); } for (i = 0; i < 1024; i++) { asm volatile (""); } if (seen_0 || seen_1) FAIL("should not have seen anything after disabling."); + puts("Trying to stop all created threads"); + stop_threads = 1; + h2_intpool_config(INT_START,1); + h2_hwconfig_hwintop(HWCONFIG_HWINTOP_RAISE,INT_START,1); // trigger interrupt to fast exit from worker thread + h2_sem_down(&int_received_sem); + h2_intpool_config(INT_START,1); + h2_hwconfig_hwintop(HWCONFIG_HWINTOP_RAISE,INT_START,1); // trigger interrupt to fast exit from worker thread + h2_sem_down(&int_received_sem); + puts("Awake last time"); + + void *ret; + pthread_join(timeout_child, &ret); + puts("Joining time-out thread"); + pthread_join(intpool_child_0, &ret); + puts("Joining worker 0 thread"); + pthread_join(intpool_child_1, &ret); + puts("Joining worker 1 thread"); puts("TEST PASSED"); return 0; } diff --git a/scripts/testlist.v61 b/scripts/testlist.v61 index ff9c1872d..a28289766 100644 --- a/scripts/testlist.v61 +++ b/scripts/testlist.v61 @@ -12,7 +12,7 @@ ./kernel/event/vectors/test ./kernel/event/preempt/test ./kernel/event/intpool/test -#./kernel/event/intpool/test_h2 +./kernel/event/intpool/test_h2 ./kernel/event/popup/test ./kernel/event/popup/test_h2 ./kernel/event/passthru/test diff --git a/scripts/testlist.v81 b/scripts/testlist.v81 index 9ee93d23a..beec1d7cf 100644 --- a/scripts/testlist.v81 +++ b/scripts/testlist.v81 @@ -12,7 +12,7 @@ ./kernel/event/vectors/test ./kernel/event/preempt/test ./kernel/event/intpool/test -#./kernel/event/intpool/test_h2 +./kernel/event/intpool/test_h2 ./kernel/event/popup/test ./kernel/event/popup/test_h2 ./kernel/event/passthru/test From 12407a15a258228bab5868b09abf9ed611b92b55 Mon Sep 17 00:00:00 2001 From: Andrey Karpenko Date: Tue, 12 May 2026 03:03:01 -0700 Subject: [PATCH 2/2] intpool/test_h2: fix reliability and re-enable in testlists - Constrain interrupt range to INT_START..INT_START+NUM_INT (16 ints) and compute expected bitmask via calc_expected_mask() instead of hardcoding ~0, fixing spurious failures from out-of-range interrupts. - Mark stop_threads as volatile int and seen_0/seen_1 as volatile unsigned int to prevent the compiler from eliding cross-thread reads. - Add graceful shutdown: set stop_threads flag, re-enable intpool and raise INT_START twice to unblock worker threads, then pthread_join all three threads (timeout, worker0, worker1). - Timeout and worker threads check stop_threads and exit cleanly instead of spinning forever or calling FAIL(). - Guard pthread_detach calls under USE_DETACHED_THREADS (disabled by default) to document the alternative threading model. - Use h2_atomic_swap32() for atomic seen = 0 assignments. - Re-enable kernel/event/intpool/test_h2 in testlist.v61 and testlist.v81. Signed-off-by: Andrey Karpenko --- kernel/event/intpool/test_h2/test.c | 126 +++++++++++++++++++++------- scripts/testlist.v61 | 2 +- scripts/testlist.v81 | 2 +- 3 files changed, 97 insertions(+), 33 deletions(-) diff --git a/kernel/event/intpool/test_h2/test.c b/kernel/event/intpool/test_h2/test.c index 4f77838c0..fd98eca94 100644 --- a/kernel/event/intpool/test_h2/test.c +++ b/kernel/event/intpool/test_h2/test.c @@ -10,10 +10,23 @@ atomic_u32_t seen; h2_sem_t int_received_sem; +volatile int stop_threads; -#define TIMEOUT_SPINS (1024*1024*4) +#define NUM_INT (16) #define INT_START 48 -#define INT_LAST 128 +#define INT_LAST (INT_START + NUM_INT) +#define TIMEOUT_SPINS (1024*1024*4) + +// FIXME: investigate the reason of these failures +//#define NUM_INT (1) +//#define INT_START 70 // doesn't work 0x46 0b01000110 +//#define INT_START 80 // doesn't work 0x50 0b01010000 +//#define INT_START 81 // doesn't work 0x51 0b01010001 +//#define INT_START 86 // doesn't work 0x56 0b01010110 +//#define INT_START 96 // doesn't work 0x60 0b01100000 +//#define INT_START 128 // doesn't work 0x80 0b10000000 + +//#define USE_DETACHED_THREADS void FAIL(const char *msg) { @@ -25,26 +38,40 @@ void FAIL(const char *msg) void *timeout(void *arg) { int i; - h2_printf("timeout thread started\n"); - for (i = 0; i < TIMEOUT_SPINS; i++) { + h2_printf("time-out thread started\n"); + for (i = 0; i < TIMEOUT_SPINS && !stop_threads; i++) { asm volatile ("nop"); } - FAIL("timeout"); - return NULL; + if (stop_threads) + h2_printf("time-out thread stopped by main thread\n"); + else + FAIL("time-out"); + + return NULL; // NOTE: pthread_exit() executed automatically in pthread_trampoline() function +} + +atomic_u32_t calc_expected_mask() { + atomic_u32_t res = 0; + for (int intno = INT_START; intno < INT_LAST; intno++) + res |= (1<<(intno & 0x1f)); + return res; } void *worker(void *arg) { int intno = -1; unsigned int *local_seen = arg; - while (1) { + while (1 && !stop_threads) { if ((intno = h2_intpool_wait(intno)) == -1) FAIL("intpool wait failed"); - // h2_printf("saw %d\n",intno); + h2_printf("saw %d (arg = %x)\n",intno, arg); h2_atomic_setbit32(&seen,(intno & 0x1f)); *local_seen |= (1<<(intno & 0x1f)); h2_sem_up(&int_received_sem); }; - return NULL; + if (stop_threads) + h2_printf("worker thread is stopped by main thread (arg = %x)\n", arg); + + return NULL; // NOTE: pthread_exit() executed automatically in pthread_trampoline() function } struct sched_param lowprio_param = { .sched_priority = 250 }; @@ -56,75 +83,112 @@ int main() pthread_t intpool_child_1; pthread_attr_t pt_attrs; h2_sem_init_val(&int_received_sem,0); - unsigned int seen_0; - unsigned int seen_1; + volatile unsigned int seen_0; + volatile unsigned int seen_1; int i; - + atomic_u32_t expected_mask; - seen = 0; + h2_atomic_swap32(&seen, 0); //seen = 0; + stop_threads = 0; h2_handle_errors(1); + expected_mask = calc_expected_mask(); puts("Starting"); + h2_printf("&seen_0 = %x, &seen_1 = %x\n", &seen_0, &seen_1); /* Create timeout thread? */ pthread_attr_init(&pt_attrs); pthread_attr_setschedparam(&pt_attrs,&lowprio_param); pthread_create(&timeout_child,&pt_attrs,timeout,NULL); +#ifdef USE_DETACHED_THREADS + pthread_detach(timeout_child); +#endif /* Configure interrupts as intpool interrupts */ for (i = INT_START; i < INT_LAST; i++) { h2_intpool_config(i,1); } /* Create one intpool thread */ - pthread_create(&intpool_child_0,NULL,worker,&seen_0); + pthread_create(&intpool_child_0,NULL,worker,(void*)&seen_0); +#ifdef USE_DETACHED_THREADS + pthread_detach(intpool_child_0); +#endif /* Trigger interrupt */ h2_hwconfig_hwintop(HWCONFIG_HWINTOP_RAISE,INT_START,1); /* Check that intpool thread (down sem?) happened */ + while (h2_atomic_sub32(&seen, 0) == 0) { + asm volatile ("nop"); + } + puts("seen != 0"); h2_sem_down(&int_received_sem); - h2_printf("awake!\n"); - if (seen == 0) FAIL("didn't see interrupt"); + puts("awake!"); + if (h2_atomic_sub32(&seen, 0) == 0) FAIL("didn't see interrupt"); else puts("Test interrupt is ok.\n"); /* Trigger MOAR INTERRUPTS */ - seen = 0; - for (i = INT_START; i < (INT_START+32); i++) { + h2_atomic_swap32(&seen, 0); //seen = 0; + for (i = INT_START; i < INT_LAST; i++) { h2_hwconfig_hwintop(HWCONFIG_HWINTOP_RAISE, i, 1); + h2_printf("send %d!\n",i); } - for (i = INT_START; i < (INT_START+32); i++) { + h2_printf("%d interrupts are sent\n", NUM_INT); + for (i = INT_START; i < INT_LAST; i++) { h2_sem_down(&int_received_sem); - // h2_printf("awake %d!\n",i); + h2_printf("awake %d!\n",i); } - if (seen != ~0) { - h2_printf("seen=%x\n",seen); + h2_printf("%d sems down\n", NUM_INT); + if (h2_atomic_sub32(&seen, 0) != expected_mask) { + h2_printf("seen=%x expected seen=%x\n",seen, expected_mask); FAIL("what has been seen cannot be unseen"); } - seen_0 = seen_1 = seen = 0; + seen_0 = seen_1 = 0; + h2_atomic_swap32(&seen, 0); // seen = 0; puts("OK"); /* Create another intpool thread */ - pthread_create(&intpool_child_1,NULL,worker,&seen_1); + pthread_create(&intpool_child_1,NULL,worker,(void*)&seen_1); +#ifdef USE_DETACHED_THREADS + pthread_detach(intpool_child_1); +#endif /* Check that both intpool threads can get interrupted */ /* Trigger more interrupts */ - for (i = INT_START; i < (INT_START+32); i++) { + for (i = INT_START; i < INT_LAST; i++) { h2_hwconfig_hwintop(HWCONFIG_HWINTOP_RAISE, i, 1); } - for (i = INT_START; i < (INT_START+32); i++) { + for (i = INT_START; i < INT_LAST; i++) { h2_sem_down(&int_received_sem); } if (seen_0 == 0) FAIL("only t1 got ints?"); if (seen_1 == 0) FAIL("only t0 got ints?"); - if ((seen_1 ^ seen_0) != ~0) { - h2_printf("1: %08x 2: %08x\n",seen_1,seen_0); + if ((seen_1 ^ seen_0) != expected_mask) { + h2_printf("1: %08x 2: %08x expected_mask: %08x\n",seen_1,seen_0, expected_mask); FAIL("bad masks"); } - if (seen != ~0) FAIL("what has been seen cannot be unseen"); - seen_0 = seen_1 = seen = 0; + if (h2_atomic_sub32(&seen, 0) != expected_mask) FAIL("what has been seen cannot be unseen"); + seen_0 = seen_1 = 0; for (i = INT_START; i < INT_LAST; i++) { h2_intpool_config(i,0); } - for (i = INT_START; i < (INT_START+32); i++) { + for (i = INT_START; i < INT_LAST; i++) { h2_hwconfig_hwintop(HWCONFIG_HWINTOP_RAISE, i, 1); } for (i = 0; i < 1024; i++) { asm volatile (""); } if (seen_0 || seen_1) FAIL("should not have seen anything after disabling."); + puts("Trying to stop all created threads"); + stop_threads = 1; + h2_intpool_config(INT_START,1); + h2_hwconfig_hwintop(HWCONFIG_HWINTOP_RAISE,INT_START,1); // trigger interrupt to fast exit from worker thread + h2_sem_down(&int_received_sem); + h2_intpool_config(INT_START,1); + h2_hwconfig_hwintop(HWCONFIG_HWINTOP_RAISE,INT_START,1); // trigger interrupt to fast exit from worker thread + h2_sem_down(&int_received_sem); + puts("Awake last time"); + + void *ret; + pthread_join(timeout_child, &ret); + puts("Joining time-out thread"); + pthread_join(intpool_child_0, &ret); + puts("Joining worker 0 thread"); + pthread_join(intpool_child_1, &ret); + puts("Joining worker 1 thread"); puts("TEST PASSED"); return 0; } diff --git a/scripts/testlist.v61 b/scripts/testlist.v61 index 3a04dca22..f9423785f 100644 --- a/scripts/testlist.v61 +++ b/scripts/testlist.v61 @@ -12,7 +12,7 @@ ./kernel/event/vectors/test ./kernel/event/preempt/test ./kernel/event/intpool/test -#./kernel/event/intpool/test_h2 +./kernel/event/intpool/test_h2 ./kernel/event/popup/test ./kernel/event/popup/test_h2 ./kernel/event/passthru/test diff --git a/scripts/testlist.v81 b/scripts/testlist.v81 index 63f4ea5ca..3ae3d6dcc 100644 --- a/scripts/testlist.v81 +++ b/scripts/testlist.v81 @@ -12,7 +12,7 @@ ./kernel/event/vectors/test ./kernel/event/preempt/test ./kernel/event/intpool/test -#./kernel/event/intpool/test_h2 +./kernel/event/intpool/test_h2 ./kernel/event/popup/test ./kernel/event/popup/test_h2 ./kernel/event/passthru/test