* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
- * Copyright 2004 Develer S.r.l. (http://www.develer.com/)
- * Copyright 1999, 2000, 2001 Bernardo Innocenti <bernie@develer.com>
- *
+ * Copyright 2004, 2008 Develer S.r.l. (http://www.develer.com/)
+ * Copyright 1999, 2000, 2001 Bernie Innocenti <bernie@codewiz.org>
* -->
*
* \brief IPC signals implementation.
* - Do not sleep between starting the asynchronous task that will fire
* SIG_SINGLE, and the call to sig_wait().
* - Do not call system functions that may implicitly sleep, such as
- * timer_delayTickes().
+ * timer_delayTicks().
*
* \version $Id$
- *
- * \author Bernardo Innocenti <bernie@develer.com>
+ * \author Bernie Innocenti <bernie@codewiz.org>
*/
#include "signal.h"
+#include "cfg/cfg_timer.h"
#include <cfg/debug.h>
-#include <drv/timer.h>
+#include <cfg/depend.h>
+
+#include <cpu/irq.h>
#include <kern/proc.h>
#include <kern/proc_p.h>
-
+
#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.
+ *
* \return the signals that have occurred.
*/
sigmask_t sig_check(sigmask_t sigs)
{
sigmask_t result;
- cpuflags_t flags;
+ cpu_flags_t flags;
IRQ_SAVE_DISABLE(flags);
result = CurrentProcess->sig_recv & sigs;
sigmask_t sig_wait(sigmask_t sigs)
{
sigmask_t result;
- cpuflags_t flags;
+ cpu_flags_t flags;
+ /* Sleeping with IRQs disabled or preemption forbidden is illegal */
+ IRQ_ASSERT_ENABLED();
+ 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.
+ *
+ * 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);
/* Loop until we get at least one of the signals */
while (!(result = CurrentProcess->sig_recv & sigs))
{
- /* go to sleep and proc_schedule() another process */
+ /*
+ * Tell "them" that we want to be awaken when any of these
+ * signals arrives.
+ */
CurrentProcess->sig_wait = sigs;
- proc_schedule();
- /* When we come back here, a signal must be arrived */
+ /*
+ * 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);
+ 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
+ * delivered to us.
+ */
ASSERT(!CurrentProcess->sig_wait);
- ASSERT(CurrentProcess->sig_recv);
+ ASSERT(CurrentProcess->sig_recv & sigs);
}
/* Signals found: clear them and return */
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).
{
Timer t;
sigmask_t res;
- cpuflags_t flags;
+ cpu_flags_t flags;
ASSERT(!sig_check(SIG_TIMEOUT));
ASSERT(!(sigs & SIG_TIMEOUT));
return res;
}
+#endif // CONFIG_TIMER_EVENTS
+
/**
* Send the signals \a sigs to the process \a proc.
*/
void sig_signal(Process *proc, sigmask_t sigs)
{
- 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 */
}
#endif /* CONFIG_KERN_SIGNALS */
-