coop.c

Go to the documentation of this file.
00001 
00040 #include "proc_p.h"
00041 #include "proc.h"
00042 
00043 // Log settings for cfg/log.h.
00044 #define LOG_LEVEL   KERN_LOG_LEVEL
00045 #define LOG_FORMAT  KERN_LOG_FORMAT
00046 #include <cfg/log.h>
00047 
00048 #include <cpu/irq.h>
00049 #include <cpu/types.h>
00050 #include <cpu/attr.h>
00051 #include <cpu/frame.h>
00052 
00059 EXTERN_C void asm_switch_context(cpu_stack_t **new_sp, cpu_stack_t **save_sp);
00060 
00061 
00066 static void proc_schedule(void)
00067 {
00068     cpu_flags_t flags;
00069 
00070     ATOMIC(LIST_ASSERT_VALID(&ProcReadyList));
00071     ASSERT_USER_CONTEXT();
00072     IRQ_ASSERT_ENABLED();
00073 
00074     /* Poll on the ready queue for the first ready process */
00075     IRQ_SAVE_DISABLE(flags);
00076     while (!(CurrentProcess = (struct Process *)list_remHead(&ProcReadyList)))
00077     {
00078         /*
00079          * Make sure we physically reenable interrupts here, no matter what
00080          * the current task status is. This is important because if we
00081          * are idle-spinning, we must allow interrupts, otherwise no
00082          * process will ever wake up.
00083          *
00084          * During idle-spinning, an interrupt can occur and it may
00085          * modify \p ProcReadyList. To ensure that compiler reload this
00086          * variable every while cycle we call CPU_MEMORY_BARRIER.
00087          * The memory barrier ensure that all variables used in this context
00088          * are reloaded.
00089          * \todo If there was a way to write sig_wait() so that it does not
00090          * disable interrupts while waiting, there would not be any
00091          * reason to do this.
00092          */
00093         IRQ_ENABLE;
00094         CPU_IDLE;
00095         MEMORY_BARRIER;
00096         IRQ_DISABLE;
00097     }
00098     IRQ_RESTORE(flags);
00099 }
00100 
00101 void proc_switch(void)
00102 {
00103     /* Remember old process to save its context later */
00104     Process * const old_process = CurrentProcess;
00105 
00106     proc_schedule();
00107 
00108     /*
00109      * Optimization: don't switch contexts when the active
00110      * process has not changed.
00111      */
00112     if (CurrentProcess != old_process)
00113     {
00114         cpu_stack_t *dummy;
00115 
00116         #if CONFIG_KERN_MONITOR
00117             LOG_INFO("Switch from %p(%s) to %p(%s)\n",
00118                 old_process,    proc_name(old_process),
00119                 CurrentProcess, proc_currentName());
00120         #endif
00121 
00122         /* Save context of old process and switch to new process. If there is no
00123          * old process, we save the old stack pointer into a dummy variable that
00124          * we ignore. In fact, this happens only when the old process has just
00125          * exited.
00126          * TODO: Instead of physically clearing the process at exit time, a zombie
00127          * list should be created.
00128          */
00129         asm_switch_context(&CurrentProcess->stack, old_process ? &old_process->stack : &dummy);
00130     }
00131 
00132     /* This RET resumes the execution on the new process */
00133 }
00134 
00135 
00139 void proc_yield(void)
00140 {
00141     ATOMIC(SCHED_ENQUEUE(CurrentProcess));
00142     proc_switch();
00143 }