#include <cpu/frame.h>
/**
- * CPU dependent context switching routines.
+ * Define function prototypes exported outside.
*
- * Saving and restoring the context on the stack is done by a CPU-dependent
- * support routine which usually needs to be written in assembly.
+ * Required to silent gcc "no previous prototype" warnings.
*/
-EXTERN_C void asm_switch_context(cpu_stack_t **new_sp, cpu_stack_t **save_sp);
-
+void coop_yield(void);
+void coop_switch(void);
+void coop_wakeup(Process *proc);
/**
- * System scheduler: pass CPU control to the next process in
- * the ready queue.
+ * 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.
*/
-static void coop_schedule(void)
+void coop_switch(void)
{
- cpu_flags_t flags;
-
- ATOMIC(LIST_ASSERT_VALID(&proc_ready_list));
- ASSERT_USER_CONTEXT();
- IRQ_ASSERT_ENABLED();
-
- /* Poll on the ready queue for the first ready process */
- IRQ_SAVE_DISABLE(flags);
- while (!(current_process = (struct Process *)list_remHead(&proc_ready_list)))
- {
- /*
- * 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 proc_ready_list. 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);
+ ATOMIC(proc_schedule());
}
-void coop_switch(void)
+/**
+ * Immediately wakeup a process, dispatching it to the CPU.
+ */
+void coop_wakeup(Process *proc)
{
- /* Remember old process to save its context later */
- Process * const old_process = current_process;
+ ASSERT(proc_preemptAllowed());
+ ASSERT(current_process);
+ IRQ_ASSERT_DISABLED();
- coop_schedule();
-
- /*
- * Optimization: don't switch contexts when the active
- * process has not changed.
- */
- if (current_process != old_process)
+ if (prio_proc(proc) >= prio_curr())
{
- cpu_stack_t *dummy;
-
- #if CONFIG_KERN_MONITOR
- LOG_INFO("Switch from %p(%s) to %p(%s)\n",
- old_process, proc_name(old_process),
- current_process, proc_currentName());
- #endif
+ Process *old_process = current_process;
- /* 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(¤t_process->stack, old_process ? &old_process->stack : &dummy);
+ SCHED_ENQUEUE(current_process);
+ current_process = proc;
+ proc_switchTo(current_process, old_process);
}
-
- /* This RET resumes the execution on the new process */
+ else
+ SCHED_ENQUEUE_HEAD(proc);
}
/**
void coop_yield(void)
{
ATOMIC(SCHED_ENQUEUE(current_process));
- proc_switch();
+ coop_switch();
}