X-Git-Url: https://codewiz.org/gitweb?a=blobdiff_plain;ds=inline;f=bertos%2Fkern%2Fsignal.c;h=2f72c47906d6bf74899a55a03bedc63bd9912064;hb=0f60d6b24cf8ec1c9bd8dca82fed6325932c5aca;hp=a22249429d885ed5dd62119773e3dd94e79c2fff;hpb=9b714f49120ffe7674765dc5e1248430b22ae593;p=bertos.git
diff --git a/bertos/kern/signal.c b/bertos/kern/signal.c
index a2224942..2f72c479 100644
--- a/bertos/kern/signal.c
+++ b/bertos/kern/signal.c
@@ -65,10 +65,38 @@
* 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.
+ *
+ *
+ * - Synchronous-signal delivery:
+ *
+ * [P1]____sig_send()____proc_wakeup()____[P2]
+ *
+ *
+ * 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.
+ *
+ *
+ * - Asynchronous-signal delivery:
+ *
+ * [P1]____sig_post()____[P1]____proc_schedule()____[P2]
+ *
+ *
+ * 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
*
@@ -94,14 +122,14 @@
* - Do not call system functions that may implicitly sleep, such as
* timer_delayTicks().
*
- * \version $Id$
* \author Bernie Innocenti
*/
#include "signal.h"
-#include
+#include "cfg/cfg_timer.h"
#include
+#include
#include
#include
@@ -110,6 +138,9 @@
#if CONFIG_KERN_SIGNALS
+// Check config dependencies
+CONFIG_DEPEND(CONFIG_KERN_SIGNALS, CONFIG_KERN);
+
/**
* Check if any of the signals in \a sigs has occurred and clear them.
*
@@ -121,8 +152,8 @@ sigmask_t sig_check(sigmask_t sigs)
cpu_flags_t flags;
IRQ_SAVE_DISABLE(flags);
- result = CurrentProcess->sig_recv & sigs;
- CurrentProcess->sig_recv &= ~sigs;
+ result = current_process->sig_recv & sigs;
+ current_process->sig_recv &= ~sigs;
IRQ_RESTORE(flags);
return result;
@@ -136,57 +167,47 @@ sigmask_t sig_check(sigmask_t sigs)
sigmask_t sig_wait(sigmask_t sigs)
{
sigmask_t result;
- cpu_flags_t flags;
/* Sleeping with IRQs disabled or preemption forbidden is illegal */
IRQ_ASSERT_ENABLED();
- ASSERT(proc_allowed());
+ 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 = current_process->sig_recv & sigs))
{
/*
* Tell "them" that we want to be awaken when any of these
* signals arrives.
*/
- CurrentProcess->sig_wait = sigs;
+ current_process->sig_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(!current_process->sig_wait);
+ ASSERT(current_process->sig_recv & sigs);
}
/* Signals found: clear them and return */
- CurrentProcess->sig_recv &= ~sigs;
+ current_process->sig_recv &= ~sigs;
- IRQ_RESTORE(flags);
+ IRQ_ENABLE;
return result;
}
@@ -225,18 +246,10 @@ 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(Process *proc, sigmask_t sigs, bool wakeup)
{
cpu_flags_t flags;
- /* See comment in sig_wait() for why this protection is necessary */
IRQ_SAVE_DISABLE(flags);
/* Set the signals */
@@ -245,12 +258,45 @@ void sig_signal(Process *proc, sigmask_t sigs)
/* Check if process needs to be awoken */
if (proc->sig_recv & proc->sig_wait)
{
- /* Wake up process and enqueue in ready list */
+ ASSERT(proc != current_process);
+
proc->sig_wait = 0;
- SCHED_ENQUEUE(proc);
+ if (wakeup)
+ proc_wakeup(proc);
+ else
+ SCHED_ENQUEUE_HEAD(proc);
}
-
IRQ_RESTORE(flags);
}
+/**
+ * Send the signals \a sigs to the process \a proc and immeditaly dispatch it
+ * for execution.
+ *
+ * The process will be awoken if it was waiting for any of them and immediately
+ * dispatched for execution.
+ *
+ * \note This function can't be called from IRQ context, use sig_post()
+ * instead.
+ */
+void sig_send(Process *proc, sigmask_t sigs)
+{
+ ASSERT_USER_CONTEXT();
+ IRQ_ASSERT_ENABLED();
+ ASSERT(proc_preemptAllowed());
+
+ __sig_signal(proc, sigs, true);
+}
+
+/**
+ * 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_post(Process *proc, sigmask_t sigs)
+{
+ __sig_signal(proc, sigs, false);
+}
+
#endif /* CONFIG_KERN_SIGNALS */