X-Git-Url: https://codewiz.org/gitweb?a=blobdiff_plain;f=bertos%2Fkern%2Fsem_test.c;h=6a1f471bee78d3e2478df1f8e51fc8749801da5c;hb=84d7581d4e99d656db0064086ecee8d9f30aeba3;hp=494af8a807ed3ce51561441f26a8064ed02ec9a4;hpb=a37178ed9c5c48e1e18cd64dcb03927c078ff4de;p=bertos.git diff --git a/bertos/kern/sem_test.c b/bertos/kern/sem_test.c index 494af8a8..6a1f471b 100644 --- a/bertos/kern/sem_test.c +++ b/bertos/kern/sem_test.c @@ -29,38 +29,367 @@ * Copyright 2008 Develer S.r.l. (http://www.develer.com/) * --> * - * \brief Semaphore based synchronization services. + * \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. * - * \version $Id$ * \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 "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(); - sem_init(&sem); + + kprintf("Init Timer.."); + timer_init(); + kprintf("Done.\n"); + + kprintf("Init Process.."); + proc_init(); + kprintf("Done.\n"); + return 0; } int sem_testTearDown(void) { - sem_release(&sem); + kputs("TearDown Semaphore test.\n"); return 0; } -TEST_MAIN(sem); \ No newline at end of file +TEST_MAIN(sem);