signal.c

Go to the documentation of this file.
00001 
00101 #include "signal.h"
00102 
00103 #include "cfg/cfg_timer.h"
00104 #include <cfg/debug.h>
00105 #include <cfg/depend.h>
00106 
00107 #include <cpu/irq.h>
00108 #include <kern/proc.h>
00109 #include <kern/proc_p.h>
00110 
00111 
00112 #if CONFIG_KERN_SIGNALS
00113 
00114 // Check config dependencies
00115 CONFIG_DEPEND(CONFIG_KERN_SIGNALS, CONFIG_KERN);
00116 
00122 sigmask_t sig_check(sigmask_t sigs)
00123 {
00124     sigmask_t result;
00125     cpu_flags_t flags;
00126 
00127     IRQ_SAVE_DISABLE(flags);
00128     result = CurrentProcess->sig_recv & sigs;
00129     CurrentProcess->sig_recv &= ~sigs;
00130     IRQ_RESTORE(flags);
00131 
00132     return result;
00133 }
00134 
00135 
00140 sigmask_t sig_wait(sigmask_t sigs)
00141 {
00142     sigmask_t result;
00143     cpu_flags_t flags;
00144 
00145     /* Sleeping with IRQs disabled or preemption forbidden is illegal */
00146     IRQ_ASSERT_ENABLED();
00147     ASSERT(proc_allowed());
00148 
00149     /*
00150      * This is subtle: there's a race condition where a concurrent
00151      * process or an interrupt may call sig_signal() to set a bit in
00152      * Process.sig_recv just after we have checked for it, but before
00153      * we've set Process.sig_wait to let them know we want to be awaken.
00154      *
00155      * In this case, we'd deadlock with the signal bit already set
00156      * and the process never being reinserted into the ready list.
00157      */
00158     // FIXME: just use IRQ_DISABLE() here
00159     IRQ_SAVE_DISABLE(flags);
00160 
00161     /* Loop until we get at least one of the signals */
00162     while (!(result = CurrentProcess->sig_recv & sigs))
00163     {
00164         /*
00165          * Tell "them" that we want to be awaken when any of these
00166          * signals arrives.
00167          */
00168         CurrentProcess->sig_wait = sigs;
00169 
00170         /*
00171          * Go to sleep and proc_switch() to another process.
00172          *
00173          * We re-enable IRQs because proc_switch() does not
00174          * guarantee to save and restore the interrupt mask.
00175          */
00176         IRQ_RESTORE(flags);
00177         proc_switch();
00178         IRQ_SAVE_DISABLE(flags);
00179 
00180         /*
00181          * When we come back here, the wait mask must have been
00182          * cleared by someone through sig_signal(), and at least
00183          * one of the signals we were expecting must have been
00184          * delivered to us.
00185          */
00186         ASSERT(!CurrentProcess->sig_wait);
00187         ASSERT(CurrentProcess->sig_recv & sigs);
00188     }
00189 
00190     /* Signals found: clear them and return */
00191     CurrentProcess->sig_recv &= ~sigs;
00192 
00193     IRQ_RESTORE(flags);
00194     return result;
00195 }
00196 
00197 #if CONFIG_TIMER_EVENTS
00198 
00199 #include <drv/timer.h>
00206 sigmask_t sig_waitTimeout(sigmask_t sigs, ticks_t timeout)
00207 {
00208     Timer t;
00209     sigmask_t res;
00210     cpu_flags_t flags;
00211 
00212     ASSERT(!sig_check(SIG_TIMEOUT));
00213     ASSERT(!(sigs & SIG_TIMEOUT));
00214     /* IRQ are needed to run timer */
00215     ASSERT(IRQ_ENABLED());
00216 
00217     timer_set_event_signal(&t, proc_current(), SIG_TIMEOUT);
00218     timer_setDelay(&t, timeout);
00219     timer_add(&t);
00220     res = sig_wait(SIG_TIMEOUT | sigs);
00221 
00222     IRQ_SAVE_DISABLE(flags);
00223     /* Remove timer if sigs occur before timer signal */
00224     if (!(res & SIG_TIMEOUT) && !sig_check(SIG_TIMEOUT))
00225         timer_abort(&t);
00226     IRQ_RESTORE(flags);
00227     return res;
00228 }
00229 
00230 #endif // CONFIG_TIMER_EVENTS
00231 
00232 
00239 void sig_signal(Process *proc, sigmask_t sigs)
00240 {
00241     cpu_flags_t flags;
00242 
00243     /* See comment in sig_wait() for why this protection is necessary */
00244     IRQ_SAVE_DISABLE(flags);
00245 
00246     /* Set the signals */
00247     proc->sig_recv |= sigs;
00248 
00249     /* Check if process needs to be awoken */
00250     if (proc->sig_recv & proc->sig_wait)
00251     {
00252         /* Wake up process and enqueue in ready list */
00253         proc->sig_wait = 0;
00254         SCHED_ENQUEUE(proc);
00255     }
00256 
00257     IRQ_RESTORE(flags);
00258 }
00259 
00260 #endif /* CONFIG_KERN_SIGNALS */