Update benchmark projects.
[bertos.git] / bertos / kern / signal.c
index 948d68f1d814c687e8edb805b047da4596975242..2b3a191fe06b7a94194d45e9adc142b511cffcf2 100644 (file)
  * particular event has occurred, because the same signal may be
  * delivered twice before the process can notice.
  *
- * Any execution context, including an interrupt handler, can deliver
- * a signal to a process using sig_signal().  Multiple independent signals
- * may be delivered at once with a single invocation of sig_signal(),
- * although this is rarely useful.
+ * Signals can be delivered synchronously via sig_send() or asynchronously via
+ * sig_post().
+ *
+ * In the synchronous case the process is awakened if it was waiting for any
+ * signal and immediately dispatched for execution via a direct context switch,
+ * if its priority is greater than the running process.
+ *
+ * <pre>
+ * - Synchronous-signal delivery:
+ *
+ *     [P1]____sig_send()____proc_wakeup()____[P2]
+ * </pre>
+ *
+ * In the asynchronous case, the process is scheduled for execution as a
+ * consequence of the delivery, but it will be dispatched by the scheduler as
+ * usual, according to the scheduling policy.
+ *
+ * <pre>
+ * - Asynchronous-signal delivery:
+ *
+ *     [P1]____sig_post()____[P1]____proc_schedule()____[P2]
+ * </pre>
+ *
+ * In this way, any execution context, including an interrupt handler, can
+ * deliver a signal to a process. However, synchronous signal delivery from a
+ * non-sleepable context (like an interrupt handler) is forbidden in order to
+ * avoid potential deadlock conditions. Instead, sig_post() can be used from
+ * any context, expecially from interrupt context or when the preemption is
+ * disabled.
+ *
+ * Multiple independent signals may be delivered at once with a single
+ * invocation of sig_send() or sig_post(), although this is rarely useful.
  *
  * \section signal_allocation Signal Allocation
  *
  *  - Do not call system functions that may implicitly sleep, such as
  *    timer_delayTicks().
  *
- * \version $Id$
  * \author Bernie Innocenti <bernie@codewiz.org>
  */
 
 #include "signal.h"
 
-#include <cfg/cfg_timer.h>
+#include "cfg/cfg_timer.h"
 #include <cfg/debug.h>
+#include <cfg/depend.h>
+
 #include <cpu/irq.h>
 #include <kern/proc.h>
 #include <kern/proc_p.h>
 
 #if CONFIG_KERN_SIGNALS
 
-/**
- * Check if any of the signals in \a sigs has occurred and clear them.
- *
- * \return the signals that have occurred.
- */
-sigmask_t sig_check(sigmask_t sigs)
-{
-       sigmask_t result;
-       cpuflags_t flags;
-
-       IRQ_SAVE_DISABLE(flags);
-       result = CurrentProcess->sig_recv & sigs;
-       CurrentProcess->sig_recv &= ~sigs;
-       IRQ_RESTORE(flags);
+// Check config dependencies
+CONFIG_DEPEND(CONFIG_KERN_SIGNALS, CONFIG_KERN);
 
-       return result;
-}
-
-
-/**
- * Sleep until any of the signals in \a sigs occurs.
- * \return the signal(s) that have awoken the process.
- */
-sigmask_t sig_wait(sigmask_t sigs)
+sigmask_t sig_waitSignal(Signal *s, sigmask_t sigs)
 {
        sigmask_t result;
-       cpuflags_t flags;
 
        /* Sleeping with IRQs disabled or preemption forbidden is illegal */
        IRQ_ASSERT_ENABLED();
-
-       #if CONFIG_KERN_PREEMPT
-       ASSERT(preempt_forbid_cnt == 0);
-       #endif
+       ASSERT(proc_preemptAllowed());
 
        /*
-        * This is subtle: there's a race condition where a concurrent
-        * process or an interrupt may call sig_signal() to set a bit in
-        * Process.sig_recv just after we have checked for it, but before
-        * we've set Process.sig_wait to let them know we want to be awaken.
+        * This is subtle: there's a race condition where a concurrent process
+        * or an interrupt may call sig_send()/sig_post() to set a bit in
+        * Process.sig_recv just after we have checked for it, but before we've
+        * set Process.sig_wait to let them know we want to be awaken.
         *
-        * In this case, we'd deadlock with the signal bit already set
-        * and the process never being reinserted into the ready list.
+        * In this case, we'd deadlock with the signal bit already set and the
+        * process never being reinserted into the ready list.
         */
-       // FIXME: just use IRQ_DISABLE() here
-       IRQ_SAVE_DISABLE(flags);
+       IRQ_DISABLE;
 
        /* Loop until we get at least one of the signals */
-       while (!(result = CurrentProcess->sig_recv & sigs))
+       while (!(result = s->recv & sigs))
        {
                /*
                 * Tell "them" that we want to be awaken when any of these
                 * signals arrives.
                 */
-               CurrentProcess->sig_wait = sigs;
+               s->wait = sigs;
 
-               /*
-                * Go to sleep and proc_switch() to another process.
-                *
-                * We re-enable IRQs because proc_switch() does not
-                * guarantee to save and restore the interrupt mask.
-                */
-               IRQ_RESTORE(flags);
+               /* Go to sleep and proc_switch() to another process. */
                proc_switch();
-               IRQ_SAVE_DISABLE(flags);
-
                /*
                 * When we come back here, the wait mask must have been
-                * cleared by someone through sig_signal(), and at least
-                * one of the signals we were expecting must have been
+                * cleared by someone through sig_send()/sig_post(), and at
+                * least one of the signals we were expecting must have been
                 * delivered to us.
                 */
-               ASSERT(!CurrentProcess->sig_wait);
-               ASSERT(CurrentProcess->sig_recv & sigs);
+               ASSERT(!s->wait);
+               ASSERT(s->recv & sigs);
        }
 
        /* Signals found: clear them and return */
-       CurrentProcess->sig_recv &= ~sigs;
+       s->recv &= ~sigs;
 
-       IRQ_RESTORE(flags);
+       IRQ_ENABLE;
        return result;
 }
 
 #if CONFIG_TIMER_EVENTS
 
 #include <drv/timer.h>
-/**
- * Sleep until any of the signals in \a sigs or \a timeout ticks elapse.
- * If the timeout elapse a SIG_TIMEOUT is added to the received signal(s).
- * \return the signal(s) that have awoken the process.
- * \note Caller must check return value to check which signal awoke the process.
- */
-sigmask_t sig_waitTimeout(sigmask_t sigs, ticks_t timeout)
+
+sigmask_t sig_waitTimeoutSignal(Signal *s, sigmask_t sigs, ticks_t timeout,
+                               Hook func, iptr_t data)
 {
        Timer t;
        sigmask_t res;
-       cpuflags_t flags;
+       cpu_flags_t flags;
 
-       ASSERT(!sig_check(SIG_TIMEOUT));
+       ASSERT(!sig_checkSignal(s, SIG_TIMEOUT));
        ASSERT(!(sigs & SIG_TIMEOUT));
        /* IRQ are needed to run timer */
        ASSERT(IRQ_ENABLED());
 
-       timer_set_event_signal(&t, proc_current(), SIG_TIMEOUT);
+       if (func)
+               timer_setSoftint(&t, func, data);
+       else
+               timer_set_event_signal(&t, proc_current(), SIG_TIMEOUT);
        timer_setDelay(&t, timeout);
        timer_add(&t);
-       res = sig_wait(SIG_TIMEOUT | sigs);
+       res = sig_waitSignal(s, SIG_TIMEOUT | sigs);
 
        IRQ_SAVE_DISABLE(flags);
        /* Remove timer if sigs occur before timer signal */
-       if (!(res & SIG_TIMEOUT) && !sig_check(SIG_TIMEOUT))
+       if (!(res & SIG_TIMEOUT) && !sig_checkSignal(s, SIG_TIMEOUT))
                timer_abort(&t);
        IRQ_RESTORE(flags);
        return res;
@@ -227,32 +222,41 @@ sigmask_t sig_waitTimeout(sigmask_t sigs, ticks_t timeout)
 
 #endif // CONFIG_TIMER_EVENTS
 
-
-/**
- * Send the signals \a sigs to the process \a proc.
- * The process will be awoken if it was waiting for any of them.
- *
- * \note This call is interrupt safe.
- */
-void sig_signal(Process *proc, sigmask_t sigs)
+INLINE void __sig_signal(Signal *s, Process *proc, sigmask_t sigs, bool wakeup)
 {
-       cpuflags_t flags;
+       cpu_flags_t flags;
 
-       /* See comment in sig_wait() for why this protection is necessary */
        IRQ_SAVE_DISABLE(flags);
 
        /* Set the signals */
-       proc->sig_recv |= sigs;
+       s->recv |= sigs;
 
        /* Check if process needs to be awoken */
-       if (proc->sig_recv & proc->sig_wait)
+       if (s->recv & s->wait)
        {
-               /* Wake up process and enqueue in ready list */
-               proc->sig_wait = 0;
-               SCHED_ENQUEUE(proc);
-       }
+               ASSERT(proc != current_process);
 
+               s->wait = 0;
+               if (wakeup)
+                       proc_wakeup(proc);
+               else
+                       SCHED_ENQUEUE_HEAD(proc);
+       }
        IRQ_RESTORE(flags);
 }
 
+void sig_sendSignal(Signal *s, Process *proc, sigmask_t sigs)
+{
+       ASSERT_USER_CONTEXT();
+       IRQ_ASSERT_ENABLED();
+       ASSERT(proc_preemptAllowed());
+
+       __sig_signal(s, proc, sigs, true);
+}
+
+void sig_postSignal(Signal *s, Process *proc, sigmask_t sigs)
+{
+       __sig_signal(s, proc, sigs, false);
+}
+
 #endif /* CONFIG_KERN_SIGNALS */