From f149112c04843df99b2c9e9fd2ef47d9cacae8a4 Mon Sep 17 00:00:00 2001 From: arighi Date: Thu, 25 Mar 2010 14:56:22 +0000 Subject: [PATCH] Introduce proc_wakeup(). Add a new primitive in each scheduler to wakeup a process and immediately dispatch it to the CPU. git-svn-id: https://src.develer.com/svnoss/bertos/trunk@3268 38d2e660-2303-0410-9eaa-f027e97ec537 --- bertos/kern/coop.c | 32 ++++++++++++++++++++++++++++++-- bertos/kern/preempt.c | 24 +++++++++++++++++++++++- bertos/kern/proc.c | 34 +--------------------------------- bertos/kern/proc.h | 9 ++++++++- bertos/kern/proc_p.h | 31 ++++++++++++++++++++++++++++++- 5 files changed, 92 insertions(+), 38 deletions(-) diff --git a/bertos/kern/coop.c b/bertos/kern/coop.c index 1b2e1bb9..542ed678 100644 --- a/bertos/kern/coop.c +++ b/bertos/kern/coop.c @@ -57,14 +57,42 @@ */ void coop_yield(void); void coop_switch(void); +void coop_wakeup(Process *proc); +/** + * Give the control of the CPU to another process. + * + * \note Assume the current process has been already added to a wait queue. + * + * \warning This should be considered an internal kernel function, even if it + * is allowed, usage from application code is strongly discouraged. + */ void coop_switch(void) { - IRQ_ASSERT_ENABLED(); - ATOMIC(proc_schedule()); } +/** + * Immediately wakeup a process, dispatching it to the CPU. + */ +void coop_wakeup(Process *proc) +{ + ASSERT(proc_preemptAllowed()); + ASSERT(current_process); + IRQ_ASSERT_DISABLED(); + + if (prio_proc(proc) >= prio_curr()) + { + Process *old_process = current_process; + + SCHED_ENQUEUE(current_process); + current_process = proc; + proc_switchTo(current_process, old_process); + } + else + SCHED_ENQUEUE_HEAD(proc); +} + /** * Co-operative context switch */ diff --git a/bertos/kern/preempt.c b/bertos/kern/preempt.c index f77dd85e..1cb5e5a0 100644 --- a/bertos/kern/preempt.c +++ b/bertos/kern/preempt.c @@ -121,6 +121,7 @@ void preempt_yield(void); int preempt_needPreempt(void); void preempt_preempt(void); void preempt_switch(void); +void preempt_wakeup(Process *proc); void preempt_init(void); /** @@ -171,11 +172,32 @@ void preempt_preempt(void) void preempt_switch(void) { ASSERT(proc_preemptAllowed()); - IRQ_ASSERT_ENABLED(); ATOMIC(preempt_schedule()); } +/** + * Immediately wakeup a process, dispatching it to the CPU. + */ +void preempt_wakeup(Process *proc) +{ + ASSERT(proc_preemptAllowed()); + ASSERT(current_process); + IRQ_ASSERT_DISABLED(); + + if (prio_proc(proc) >= prio_curr()) + { + Process *old_process = current_process; + + SCHED_ENQUEUE(current_process); + _proc_quantum = CONFIG_KERN_QUANTUM; + current_process = proc; + proc_switchTo(current_process, old_process); + } + else + SCHED_ENQUEUE_HEAD(proc); +} + /** * Voluntarily release the CPU. */ diff --git a/bertos/kern/proc.c b/bertos/kern/proc.c index 623c1d3f..acaf4220 100644 --- a/bertos/kern/proc.c +++ b/bertos/kern/proc.c @@ -62,14 +62,6 @@ #define PROC_SIZE_WORDS (ROUND_UP2(sizeof(Process), sizeof(cpu_stack_t)) / sizeof(cpu_stack_t)) -/** - * 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(cpu_stack_t **new_sp, cpu_stack_t **save_sp); - /* * The scheduer tracks ready processes by enqueuing them in the * ready list. @@ -428,15 +420,6 @@ void proc_exit(void) ASSERT(0); } - -/** - * Get the pointer to the user data of the current process - */ -iptr_t proc_currentUserData(void) -{ - return current_process->user_data; -} - /** * Call the scheduler and eventually replace the current running process. */ @@ -470,22 +453,7 @@ void proc_schedule(void) MEMORY_BARRIER; IRQ_DISABLE; } - /* - * Optimization: don't switch contexts when the active process has not - * changed. - */ - if (LIKELY(current_process != old_process)) { - cpu_stack_t *dummy; - - /* - * 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. - */ - asm_switch_context(¤t_process->stack, - old_process ? &old_process->stack : &dummy); - } + proc_switchTo(current_process, old_process); /* This RET resumes the execution on the new process */ LOG_INFO("resuming %p:%s\n", current_process, proc_currentName()); } diff --git a/bertos/kern/proc.h b/bertos/kern/proc.h index 390099da..bacd052b 100644 --- a/bertos/kern/proc.h +++ b/bertos/kern/proc.h @@ -148,6 +148,7 @@ void proc_exit(void); void proc_yield(void); void proc_preempt(void); int proc_needPreempt(void); +void proc_wakeup(Process *proc); /** * Dummy function that defines unimplemented scheduler class methods. @@ -167,6 +168,7 @@ INLINE void __proc_noop(void) * Preemptive scheduler: private methods. */ #define preempt_switch proc_switch + #define preempt_wakeup proc_wakeup #else /** * Co-operative scheduler: public methods. @@ -178,6 +180,7 @@ INLINE void __proc_noop(void) * Co-operative scheduler: private methods. */ #define coop_switch proc_switch + #define coop_wakeup proc_wakeup #endif void proc_rename(struct Process *proc, const char *name); @@ -191,7 +194,11 @@ const char *proc_currentName(void); * the returned pointer to the correct type. * \return Pointer to the user data of the current process. */ -iptr_t proc_currentUserData(void); +INLINE iptr_t proc_currentUserData(void) +{ + extern struct Process *current_process; + return current_process->user_data; +} int proc_testSetup(void); int proc_testRun(void); diff --git a/bertos/kern/proc_p.h b/bertos/kern/proc_p.h index 27d65fe2..85cb758f 100644 --- a/bertos/kern/proc_p.h +++ b/bertos/kern/proc_p.h @@ -50,6 +50,30 @@ #include // struct Process +/** + * 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(cpu_stack_t **new_sp, cpu_stack_t **save_sp); + +/* + * Save context of old process and switch to new process. + */ +INLINE void proc_switchTo(Process *next, Process *prev) +{ + cpu_stack_t *dummy; + + if (UNLIKELY(next == prev)) + return; + /* + * 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. + */ + asm_switch_context(&next->stack, prev ? &prev->stack : &dummy); +} /** * \name Flags for Process.flags. @@ -72,7 +96,8 @@ extern REGISTER List proc_ready_list; #if CONFIG_KERN_PRI #define prio_next() (LIST_EMPTY(&proc_ready_list) ? INT_MIN : \ ((PriNode *)LIST_HEAD(&proc_ready_list))->pri) - #define prio_curr() (current_process->link.pri) + #define prio_proc(proc) (proc->link.pri) + #define prio_curr() prio_proc(current_process) #define SCHED_ENQUEUE_INTERNAL(proc) \ LIST_ENQUEUE(&proc_ready_list, &(proc)->link) @@ -80,6 +105,7 @@ extern REGISTER List proc_ready_list; LIST_ENQUEUE_HEAD(&proc_ready_list, &(proc)->link) #else #define prio_next() 0 + #define prio_proc(proc) 0 #define prio_curr() 0 #define SCHED_ENQUEUE_INTERNAL(proc) ADDTAIL(&proc_ready_list, &(proc)->link) @@ -153,6 +179,9 @@ void proc_switch(void); /* Low level scheduling routine. */ void proc_schedule(void); +/* Low level context switch routine. */ +void proc_switchTo(Process *next, Process *prev); + /* Initialize a scheduler class. */ void proc_schedInit(void); -- 2.25.1