+/**
+ * Call the scheduler and eventually replace the current running process.
+ */
+static void proc_schedule(void)
+{
+ Process *old_process = current_process;
+
+ IRQ_ASSERT_DISABLED();
+
+ /* Poll on the ready queue for the first ready process */
+ LIST_ASSERT_VALID(&proc_ready_list);
+ 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;
+ }
+ if (CONTEXT_SWITCH_FROM_ISR())
+ proc_context_switch(current_process, old_process);
+ /* This RET resumes the execution on the new process */
+ LOG_INFO("resuming %p:%s\n", current_process, proc_currentName());
+}
+
+#if CONFIG_KERN_PREEMPT
+/* Global preemption nesting counter */
+cpu_atomic_t preempt_count;
+
+/*
+ * The time sharing interval: when a process is scheduled on a CPU it gets an
+ * amount of CONFIG_KERN_QUANTUM clock ticks. When these ticks expires and
+ * preemption is enabled a new process is selected to run.
+ */
+int _proc_quantum;
+
+/**
+ * Check if we need to schedule another task
+ */
+bool proc_needPreempt(void)
+{
+ if (UNLIKELY(current_process == NULL))
+ return false;
+ if (!proc_preemptAllowed())
+ return false;
+ if (LIST_EMPTY(&proc_ready_list))
+ return false;
+ return preempt_quantum() ? prio_next() > prio_curr() :
+ prio_next() >= prio_curr();
+}
+
+/**
+ * Preempt the current task.
+ */
+void proc_preempt(void)
+{
+ IRQ_ASSERT_DISABLED();
+ ASSERT(current_process);
+
+ /* Perform the kernel preemption */
+ LOG_INFO("preempting %p:%s\n", current_process, proc_currentName());
+ /* We are inside a IRQ context, so ATOMIC is not needed here */
+ SCHED_ENQUEUE(current_process);
+ preempt_reset_quantum();
+ proc_schedule();
+}
+#endif /* CONFIG_KERN_PREEMPT */
+
+/* Immediately switch to a particular process */
+static void proc_switchTo(Process *proc)
+{
+ Process *old_process = current_process;
+
+ SCHED_ENQUEUE(current_process);
+ preempt_reset_quantum();
+ current_process = proc;
+ proc_context_switch(current_process, old_process);
+}
+
+/**
+ * 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 proc_switch(void)
+{
+ ASSERT(proc_preemptAllowed());
+ ATOMIC(
+ preempt_reset_quantum();
+ proc_schedule();
+ );
+}
+
+/**
+ * Immediately wakeup a process, dispatching it to the CPU.
+ */
+void proc_wakeup(Process *proc)
+{
+ ASSERT(proc_preemptAllowed());
+ ASSERT(current_process);
+ IRQ_ASSERT_DISABLED();
+
+ if (prio_proc(proc) >= prio_curr())
+ proc_switchTo(proc);
+ else
+ SCHED_ENQUEUE_HEAD(proc);
+}