From 87302355709b6d5ac0592024b84207ba86baa82e Mon Sep 17 00:00:00 2001 From: bernie Date: Sun, 10 Aug 2008 20:07:45 +0000 Subject: [PATCH] proc: Split cooperative scheduler in coop.c git-svn-id: https://src.develer.com/svnoss/bertos/trunk@1619 38d2e660-2303-0410-9eaa-f027e97ec537 --- app/demo/demo.mk | 1 + bertos/kern/coop.c | 141 ++++++++++++++++++++++++++++++++++++++ bertos/kern/preempt.c | 80 ++++++++++++++++++++++ bertos/kern/proc.c | 147 ---------------------------------------- bertos/kern/proc_p.h | 2 - bertos/kern/proc_test.c | 1 + 6 files changed, 223 insertions(+), 149 deletions(-) create mode 100644 bertos/kern/coop.c create mode 100644 bertos/kern/preempt.c diff --git a/app/demo/demo.mk b/app/demo/demo.mk index eab6c3cb..20eb2b4e 100644 --- a/app/demo/demo.mk +++ b/app/demo/demo.mk @@ -51,6 +51,7 @@ demo_CSRC = \ bertos/mware/observer.c \ bertos/mware/resource.c \ bertos/mware/sprintf.c \ + bertos/kern/coop.c \ bertos/kern/proc.c \ bertos/kern/proc_test.c \ bertos/kern/sem.c \ diff --git a/bertos/kern/coop.c b/bertos/kern/coop.c new file mode 100644 index 00000000..b62a02e4 --- /dev/null +++ b/bertos/kern/coop.c @@ -0,0 +1,141 @@ +/** + * \file + * + * + * \brief Simple realtime multitasking scheduler. + * Context switching is only done cooperatively. + * + * \version $Id: proc.c 1616 2008-08-10 19:41:26Z bernie $ + * \author Bernie Innocenti + * \author Stefano Fedrigo + */ + +#include "proc_p.h" +#include "proc.h" + +// Log settings for cfg/log.h. +#define LOG_LEVEL KERN_LOG_LEVEL +#define LOG_FORMAT KERN_LOG_FORMAT +#include + +#include +#include +#include +#include + +/** + * CPU dependent context switching routines. + * + * Saving and restoring the context on the stack is done by a CPU-dependent + * support routine which usually needs to be written in assembly. + */ +EXTERN_C void asm_switch_context(cpustack_t **new_sp, cpustack_t **save_sp); + + +/** + * System scheduler: pass CPU control to the next process in + * the ready queue. + */ +void proc_schedule(void) +{ + struct Process *old_process; + cpuflags_t flags; + + ATOMIC(LIST_ASSERT_VALID(&ProcReadyList)); + ASSERT_USER_CONTEXT(); + ASSERT_IRQ_ENABLED(); + + /* Remember old process to save its context later */ + old_process = CurrentProcess; + + /* Poll on the ready queue for the first ready process */ + IRQ_SAVE_DISABLE(flags); + while (!(CurrentProcess = (struct Process *)list_remHead(&ProcReadyList))) + { + /* + * Make sure we physically reenable interrupts here, no matter what + * the current task status is. This is important because if we + * are idle-spinning, we must allow interrupts, otherwise no + * process will ever wake up. + * + * During idle-spinning, an interrupt can occur and it may + * modify \p ProcReadyList. To ensure that compiler reload this + * variable every while cycle we call CPU_MEMORY_BARRIER. + * The memory barrier ensure that all variables used in this context + * are reloaded. + * \todo If there was a way to write sig_wait() so that it does not + * disable interrupts while waiting, there would not be any + * reason to do this. + */ + IRQ_ENABLE; + CPU_IDLE; + MEMORY_BARRIER; + IRQ_DISABLE; + } + IRQ_RESTORE(flags); + + /* + * Optimization: don't switch contexts when the active + * process has not changed. + */ + if (CurrentProcess != old_process) + { + cpustack_t *dummy; + + #if CONFIG_KERN_MONITOR + LOG_INFO("Switch from %p(%s) to %p(%s)\n", + old_process, old_process ? old_process->monitor.name : "NONE", + CurrentProcess, CurrentProcess->monitor.name); + #endif + + /* Save context of old process and switch to new process. If there is no + * old process, we save the old stack pointer into a dummy variable that + * we ignore. In fact, this happens only when the old process has just + * exited. + * TODO: Instead of physically clearing the process at exit time, a zombie + * list should be created. + */ + asm_switch_context(&CurrentProcess->stack, old_process ? &old_process->stack : &dummy); + } + + /* This RET resumes the execution on the new process */ +} + + +/** + * Co-operative context switch + */ +void proc_switch(void) +{ + ATOMIC(SCHED_ENQUEUE(CurrentProcess)); + + proc_schedule(); +} diff --git a/bertos/kern/preempt.c b/bertos/kern/preempt.c new file mode 100644 index 00000000..f068a8a8 --- /dev/null +++ b/bertos/kern/preempt.c @@ -0,0 +1,80 @@ +/** + * \file + * + * + * \brief Simple realtime multitasking scheduler. + * Context switching is only done cooperatively. + * + * \version $Id: proc.c 1616 2008-08-10 19:41:26Z bernie $ + * \author Bernie Innocenti + * \author Stefano Fedrigo + */ + +#include "proc_p.h" +#include "proc.h" + + +/* + * The time sharing scheduler forces a task switch when the current + * process has exhausted its quantum. + */ +uint16_t Quantum; + +/** + * Disable preemptive task switching. + * + * The scheduler maintains a per-process nesting counter. Task switching is + * effectively re-enabled only when the number of calls to proc_permit() + * matches the number of calls to proc_forbid(). + * + * Calling functions that could sleep while task switching is disabled + * is dangerous, although supported. Preemptive task switching is + * resumed while the process is sleeping and disabled again as soon as + * it wakes up again. + * + * \sa proc_permit() + */ +void proc_forbid(void) +{ + /* No need to protect against interrupts here. */ + ++CurrentProcess->forbid_cnt; +} + +/** + * Re-enable preemptive task switching. + * + * \sa proc_forbid() + */ +void proc_permit(void) +{ + /* No need to protect against interrupts here. */ + --CurrentProcess->forbid_cnt; +} diff --git a/bertos/kern/proc.c b/bertos/kern/proc.c index 51cf0c14..9aa04546 100644 --- a/bertos/kern/proc.c +++ b/bertos/kern/proc.c @@ -43,31 +43,15 @@ #include "proc.h" #include "cfg/cfg_arch.h" /* ARCH_EMUL */ -#include #include -// Log settings for cfg/log.h. -#define LOG_LEVEL KERN_LOG_LEVEL -#define LOG_FORMAT KERN_LOG_FORMAT -#include - #include #include #include #include -#include - #include /* memset() */ -/** - * CPU dependent context switching routines. - * - * Saving and restoring the context on the stack is done by a CPU-dependent - * support routine which usually needs to be written in assembly. - */ -EXTERN_C void asm_switch_context(cpustack_t **new_sp, cpustack_t **save_sp); - /* * The scheduer tracks ready processes by enqueuing them in the * ready list. @@ -83,15 +67,6 @@ REGISTER List ProcReadyList; */ REGISTER Process *CurrentProcess; -#if CONFIG_KERN_PREEMPTIVE -/* - * The time sharing scheduler forces a task switch when the current - * process has exhausted its quantum. - */ -uint16_t Quantum; -#endif - - #if (ARCH & ARCH_EMUL) /* * In hosted environments, we must emulate the stack on the real process stack. @@ -251,81 +226,6 @@ void proc_rename(struct Process *proc, const char *name) } -/** - * System scheduler: pass CPU control to the next process in - * the ready queue. - */ -void proc_schedule(void) -{ - struct Process *old_process; - cpuflags_t flags; - - ATOMIC(LIST_ASSERT_VALID(&ProcReadyList)); - ASSERT_USER_CONTEXT(); - ASSERT_IRQ_ENABLED(); - - /* Remember old process to save its context later */ - old_process = CurrentProcess; - - /* Poll on the ready queue for the first ready process */ - IRQ_SAVE_DISABLE(flags); - while (!(CurrentProcess = (struct Process *)list_remHead(&ProcReadyList))) - { - /* - * Make sure we physically reenable interrupts here, no matter what - * the current task status is. This is important because if we - * are idle-spinning, we must allow interrupts, otherwise no - * process will ever wake up. - * - * During idle-spinning, an interrupt can occur and it may - * modify \p ProcReadyList. To ensure that compiler reload this - * variable every while cycle we call CPU_MEMORY_BARRIER. - * The memory barrier ensure that all variables used in this context - * are reloaded. - * \todo If there was a way to write sig_wait() so that it does not - * disable interrupts while waiting, there would not be any - * reason to do this. - */ - IRQ_ENABLE; - CPU_IDLE; - MEMORY_BARRIER; - IRQ_DISABLE; - } - IRQ_RESTORE(flags); - - /* - * Optimization: don't switch contexts when the active - * process has not changed. - */ - if (CurrentProcess != old_process) - { - cpustack_t *dummy; - - #if CONFIG_KERN_MONITOR - LOG_INFO("Switch from %p(%s) to %p(%s)\n", - old_process, old_process ? old_process->monitor.name : "NONE", - CurrentProcess, CurrentProcess->monitor.name); - #endif - - #if CONFIG_KERN_PREEMPTIVE - /* Reset quantum for this process */ - Quantum = CONFIG_KERN_QUANTUM; - #endif - - /* Save context of old process and switch to new process. If there is no - * old process, we save the old stack pointer into a dummy variable that - * we ignore. In fact, this happens only when the old process has just - * exited. - * TODO: Instead of physically clearing the process at exit time, a zombie - * list should be created. - */ - asm_switch_context(&CurrentProcess->stack, old_process ? &old_process->stack : &dummy); - } - - /* This RET resumes the execution on the new process */ -} - - /** * Terminate the current process */ @@ -369,17 +269,6 @@ void proc_exit(void) } -/** - * Co-operative context switch - */ -void proc_switch(void) -{ - ATOMIC(SCHED_ENQUEUE(CurrentProcess)); - - proc_schedule(); -} - - /** * Get the pointer to the current process */ @@ -395,39 +284,3 @@ iptr_t proc_current_user_data(void) { return CurrentProcess->user_data; } - - -#if CONFIG_KERN_PREEMPTIVE - -/** - * Disable preemptive task switching. - * - * The scheduler maintains a per-process nesting counter. Task switching is - * effectively re-enabled only when the number of calls to proc_permit() - * matches the number of calls to proc_forbid(). - * - * Calling functions that could sleep while task switching is disabled - * is dangerous, although supported. Preemptive task switching is - * resumed while the process is sleeping and disabled again as soon as - * it wakes up again. - * - * \sa proc_permit() - */ -void proc_forbid(void) -{ - /* No need to protect against interrupts here. */ - ++CurrentProcess->forbid_cnt; -} - -/** - * Re-enable preemptive task switching. - * - * \sa proc_forbid() - */ -void proc_permit(void) -{ - /* No need to protect against interrupts here. */ - --CurrentProcess->forbid_cnt; -} - -#endif /* CONFIG_KERN_PREEMPTIVE */ diff --git a/bertos/kern/proc_p.h b/bertos/kern/proc_p.h index 24c02fe3..6a79eec3 100644 --- a/bertos/kern/proc_p.h +++ b/bertos/kern/proc_p.h @@ -34,7 +34,6 @@ * \brief Internal scheduler structures and definitions for processes. * * \version $Id$ - * * \author Bernie Innocenti */ @@ -132,4 +131,3 @@ void proc_schedule(void); #endif /* CONFIG_KERN_MONITOR */ #endif /* KERN_PROC_P_H */ - diff --git a/bertos/kern/proc_test.c b/bertos/kern/proc_test.c index 56f784d5..38a6b16c 100644 --- a/bertos/kern/proc_test.c +++ b/bertos/kern/proc_test.c @@ -117,6 +117,7 @@ int proc_testRun(void) * How can we fix this? */ #include TEST_ONLY(drv/kdebug.c) +#include TEST_ONLY(kern/coop.c) #include TEST_ONLY(kern/proc.c) #include TEST_ONLY(drv/timer.c) #include TEST_ONLY(mware/formatwr.c) -- 2.25.1