/** * \file * * * \brief Semaphore test. * * For testing priority inversion (avoidance) a set of processes * interacting among each others by means of two semaphores are * disturbed by an unrelated process, i.e., a process not using * any semaphore at all. * * In case of priority inversion, high priority processes * are affected (delayed!) by such process, even it has lower * priority, because of semaphores. On the other hand, when priority * inheritance is enabled, non interacting low priority processes * can't affect the execution of high priority ones. * * It all can be seen looking at the finishing time of the various * processes involved in sem_inv_test (logged). * * Notice that priority inheritance makes sense iff priorities * exist, so the whole test depends on CONFIG_KERN_PRI. * * \author Daniele Basile * \author Stefano Fedrigo * \author Dario Faggioli * * $test$: cp bertos/cfg/cfg_proc.h $cfgdir/ * $test$: echo "#undef CONFIG_KERN" >> $cfgdir/cfg_proc.h * $test$: echo "#define CONFIG_KERN 1" >> $cfgdir/cfg_proc.h * $test$: echo "#undef CONFIG_KERN_PRI" >> $cfgdir/cfg_proc.h * $test$: echo "#define CONFIG_KERN_PRI 1" >> $cfgdir/cfg_proc.h * $test$: echo "#undef CONFIG_KERN_PRI_INHERIT" >> $cfgdir/cfg_proc.h * $test$: echo "#define CONFIG_KERN_PRI_INHERIT 1" >> $cfgdir/cfg_proc.h * $test$: cp bertos/cfg/cfg_sem.h $cfgdir/ * $test$: echo "#undef CONFIG_KERN_SEMAPHORES" >> $cfgdir/cfg_sem.h * $test$: echo "#define CONFIG_KERN_SEMAPHORES 1" >> $cfgdir/cfg_sem.h */ #include #include #include #include #include #include // Global settings for the serialization test. #define MAX_GLOBAL_COUNT 1024 #define TEST_TIME_OUT_MS 6000 #define DELAY 5 // Settings for the test processes (serialization test). //Process 1 #define INC_PROC_T1 1 #define DELAY_PROC_T1 INC_PROC_T1*DELAY //Process 2 #define INC_PROC_T2 3 #define DELAY_PROC_T2 INC_PROC_T2*DELAY //Process 3 #define INC_PROC_T3 5 #define DELAY_PROC_T3 INC_PROC_T3*DELAY //Process 4 #define INC_PROC_T4 7 #define DELAY_PROC_T4 INC_PROC_T4*DELAY //Process 5 #define INC_PROC_T5 11 #define DELAY_PROC_T5 INC_PROC_T5*DELAY //Process 6 #define INC_PROC_T6 13 #define DELAY_PROC_T6 INC_PROC_T6*DELAY //Process 7 #define INC_PROC_T7 17 #define DELAY_PROC_T7 INC_PROC_T7*DELAY //Process 8 #define INC_PROC_T8 19 #define DELAY_PROC_T8 INC_PROC_T8*DELAY Semaphore sem; unsigned int global_count = 0; /* * These macros generate the code needed to create the test process functions. */ #define PROC_TEST(num) static void proc_semTest##num(void) \ { \ unsigned int local_count = 0; \ \ for (int i = 0; i < INC_PROC_T##num; ++i) \ { \ sem_obtain(&sem); \ kprintf("> test%d: Obtain semaphore.\n", num); \ local_count = global_count; \ kprintf("> test%d: Read global count [%d]\n", num, local_count); \ timer_delay(DELAY_PROC_T##num); \ local_count += INC_PROC_T##num; \ global_count = local_count; \ kprintf("> test%d: Update count g[%d] l[%d]\n", num, global_count, local_count); \ sem_release(&sem); \ kprintf("> test%d: Relase semaphore.\n", num); \ } \ } \ #define PROC_TEST_STACK(num) PROC_DEFINE_STACK(proc_sem_test##num##_stack, KERN_MINSTACKSIZE * 2) #define PROC_TEST_INIT(num) proc_new(proc_semTest##num, NULL, sizeof(proc_sem_test##num##_stack), proc_sem_test##num##_stack); // Define processes for the serialization test. PROC_TEST(1) PROC_TEST(2) PROC_TEST(3) PROC_TEST(4) PROC_TEST(5) PROC_TEST(6) PROC_TEST(7) PROC_TEST(8) #if CONFIG_KERN_PRI // Global settings for the priority inversion test. // 0.5 secs, enough for seeing the effects #define BASETIME 500 Semaphore s1, s2; unsigned int loops = 0; // For counting iterations int finishing_time[8]; typedef enum ProcType {NONE, S1, S2, S1S2} ProcType; /* * Macros for the processes of the priority inversion test. */ #define PROC_INV_TEST(num) static void proc_semInvTest##num(void) \ { \ ProcType p_type = (ProcType)((ssize_t) proc_currentUserData()); \ int mult = p_type == NONE ? 5 : 1; \ unsigned int i, local_count = 0; \ ticks_t start; \ \ kprintf("> test%d(%d): Start.\n", num, proc_current()->link.pri); \ finishing_time[num-1] = timer_clock(); \ \ if (p_type == S1 || p_type == S1S2) { \ kprintf("> test%d(prio=%d): Obtain %p..\n", num, \ proc_current()->link.pri, &s1); \ sem_obtain(&s1); \ kprintf("> test%d(prio=%d): Obtained %p.\n", num, \ proc_current()->link.pri, &s1); \ } \ if (p_type == S2 || p_type == S1S2) { \ kprintf("> test%d(prio=%d): Obtain %p..\n", num, \ proc_current()->link.pri, &s2); \ sem_obtain(&s2); \ kprintf("> test%d(prio=%d): Obtained %p.\n", num, \ proc_current()->link.pri, &s2); \ } \ \ start = timer_clock(); \ for (i = 0; i < loops * mult && (((unsigned)timer_clock()-start) <= loops*mult); i++) { \ local_count++; \ } \ \ sem_obtain(&sem); \ global_count += local_count; \ kprintf("> test%d(prio=%d): global_count=%u..\n", num, \ proc_current()->link.pri, global_count); \ sem_release(&sem); \ \ if (p_type == S2 || p_type == S1S2) { \ kprintf("> test%d(prio=%d): Release %p..\n", num, \ proc_current()->link.pri, &s2); \ sem_release(&s2); \ kprintf("> test%d(prio=%d): %p Released.\n", num, \ proc_current()->link.pri, &s2); \ } \ if (p_type == S1 || p_type == S1S2) { \ kprintf("> test%d(prio=%d): Release %p..\n", num, \ proc_current()->link.pri, &s1); \ sem_release(&s1); \ kprintf("> test%d(prio=%d): %p Released.\n", num, \ proc_current()->link.pri, &s1); \ } \ \ finishing_time[num-1] = timer_clock() - finishing_time[num-1]; \ kprintf("> test%d(prio=%d): Exit.\n", num, proc_current()->link.pri); \ } \ #define PROC_INV_TEST_INIT(num, pri, type) \ do { \ struct Process *p; \ \ timer_delay(10); \ p = proc_new(proc_semInvTest##num, \ ((void*)type), sizeof(proc_sem_test##num##_stack), \ proc_sem_test##num##_stack); \ proc_setPri(p, pri); \ } while (0) \ // Define processes for the priority inversion test. PROC_INV_TEST(1) PROC_INV_TEST(2) PROC_INV_TEST(3) PROC_INV_TEST(4) PROC_INV_TEST(5) PROC_INV_TEST(6) PROC_INV_TEST(7) PROC_INV_TEST(8) #endif /* CONFIG_KERN_PRI */ // Define process stacks for both of the tests. PROC_TEST_STACK(1) PROC_TEST_STACK(2) PROC_TEST_STACK(3) PROC_TEST_STACK(4) PROC_TEST_STACK(5) PROC_TEST_STACK(6) PROC_TEST_STACK(7) PROC_TEST_STACK(8) static int sem_ser_test(void) { ticks_t start_time = timer_clock(); sem_init(&sem); global_count = 0; kprintf("Run semaphore serialization test..\n"); // Initialize the processes. PROC_TEST_INIT(1) PROC_TEST_INIT(2) PROC_TEST_INIT(3) PROC_TEST_INIT(4) PROC_TEST_INIT(5) PROC_TEST_INIT(6) PROC_TEST_INIT(7) PROC_TEST_INIT(8) kputs("> Main: Processes created\n"); /* * Wait until all processes exit, if something goes wrong we return an * error after timeout_ms. */ while((timer_clock() - start_time) < ms_to_ticks(TEST_TIME_OUT_MS)) { if (sem_attempt(&sem)) { kputs("> Main: Check if test has finished..\n"); if(global_count == MAX_GLOBAL_COUNT) { kputs("> Main: Test Finished..Ok!\n"); return 0; } sem_release(&sem); kputs("> Main: Test is still running..\n"); } proc_yield(); } kputs("Semaphore serialization test failed..\n"); return -1; } #if CONFIG_KERN_PRI static int sem_inv_test(void) { int i, orig_pri = proc_current()->link.pri; ticks_t fake, start_time; sem_init(&sem); global_count = 0; loops = 0; sem_init(&s1); sem_init(&s2); kputs("> Main: calibration for the busy wait cycle..\n"); proc_setPri(proc_current(), 10); fake = start_time = timer_clock(); while ((fake - start_time) < ms_to_ticks(BASETIME)) { fake = timer_clock(); loops++; } kprintf("> Main: calibration done, %dms equals to %u cycles!\n", BASETIME, loops); kputs("> Main: Run Priority Inversion test...\n"); // Will take s2 PROC_INV_TEST_INIT(1, 2, S2); // 2 will block on s2; 3 will take s2 and still block on s2 PROC_INV_TEST_INIT(2, 3, S2); PROC_INV_TEST_INIT(3, 4, S1S2); // Will block on s1, nothing happens.. PROC_INV_TEST_INIT(4, 5, S1); // No semaphore, without PI this will delay everyone! PROC_INV_TEST_INIT(5, 6, NONE); // Will block on s1 and boost PROC_INV_TEST_INIT(6, 7, S1); PROC_INV_TEST_INIT(7, 8, S1); PROC_INV_TEST_INIT(8, 9, S1); // All processes created, let them run. proc_setPri(proc_current(), orig_pri); while ((timer_clock() - start_time) < ms_to_ticks(TEST_TIME_OUT_MS*2)) { if (sem_attempt(&sem)) { if (global_count >= loops*7 + loops*5) { for (i = 0; i < 8; i++) kprintf("> Main: I-O latency of %d = %ldms\n", i+1, (long) ms_to_ticks(finishing_time[i])); kputs("> Main: Test Finished..Ok!\n"); return 0; } sem_release(&sem); } proc_yield(); } kputs("> Main: Priority Inversion Test failed..\n"); return -1; } #else void sem_inv_test(void) { } #endif /* CONFIG_KERN_PRI */ /** * Run semaphore test */ int sem_testRun(void) { /* Start tests */ sem_ser_test(); // Serialization sem_inv_test(); // Priority Inversion return 0; } int sem_testSetup(void) { kdbg_init(); kprintf("Init Timer.."); timer_init(); kprintf("Done.\n"); kprintf("Init Process.."); proc_init(); kprintf("Done.\n"); return 0; } int sem_testTearDown(void) { kputs("TearDown Semaphore test.\n"); return 0; } TEST_MAIN(sem);