BeRTOS
event.c
Go to the documentation of this file.
00001 
00040 #include "event.h"
00041 #include "cfg/cfg_signal.h"
00042 #include "cfg/cfg_timer.h"
00043 
00044 #include <drv/timer.h> /* timer_clock() */
00045 
00046 void event_hook_ignore(UNUSED_ARG(Event *, e))
00047 {
00048 }
00049 
00050 void event_hook_softint(Event *e)
00051 {
00052     e->Ev.Int.func(e->Ev.Int.user_data);
00053 }
00054 
00055 void event_hook_generic(Event *e)
00056 {
00057     e->Ev.Gen.completed = true;
00058     MEMORY_BARRIER;
00059 }
00060 
00061 #if CONFIG_KERN && CONFIG_KERN_SIGNALS
00062 void event_hook_signal(Event *e)
00063 {
00064     sig_post((e)->Ev.Sig.sig_proc, (e)->Ev.Sig.sig_bit);
00065 }
00066 
00067 void event_hook_generic_signal(Event *e)
00068 {
00069     sig_postSignal(&e->Ev.Sig.sig, e->Ev.Sig.sig_proc, e->Ev.Sig.sig_bit);
00070 }
00071 
00072 /*
00073  * Custom event hook to notify the completion of a event monitored via
00074  * event_select().
00075  */
00076 static void event_hook_generic_multiple_signal(Event *e)
00077 {
00078     sig_post(e->Ev.Sig.sig_proc, e->Ev.Sig.sig_bit);
00079 }
00080 
00081 /*
00082  * Custom timer hook to notify the timeout of a event_waitTimeout().
00083  */
00084 static void event_hook_generic_timeout_signal(void *arg)
00085 {
00086     Event *e = (Event *)arg;
00087 
00088     sig_postSignal(&e->Ev.Sig.sig, e->Ev.Sig.sig_proc, SIG_TIMEOUT);
00089 }
00090 
00091 /*
00092  * event_waitTimeout() slow path: this function put the current process to
00093  * sleep until the event is notified. The timeout is managed using the custom
00094  * timer hook event_hook_generic_timeout_signal(): if the timeout expires the
00095  * signal SIG_TIMEOUT is notified via sig_post() to the sigmask embedded in the
00096  * event.
00097  *
00098  * The custom timer hook is required because the default timer's behaviour is
00099  * to use the process's sigmask to notify the completion of an event, that is
00100  * not suitable for this case, because we're sleeping on the event's sigmask
00101  * instead.
00102  */
00103 static NOINLINE bool event_waitTimeoutSlowPath(Event *e, ticks_t timeout)
00104 {
00105     bool ret;
00106 
00107     e->Ev.Sig.sig_proc = proc_current();
00108     ret = (sig_waitTimeoutSignal(&e->Ev.Sig.sig,
00109             EVENT_GENERIC_SIGNAL, timeout,
00110             event_hook_generic_timeout_signal, e) & SIG_TIMEOUT) ?
00111             false : true;
00112     return ret;
00113 }
00114 
00115 bool event_waitTimeout(Event *e, ticks_t timeout)
00116 {
00117     /*
00118      * Fast path: check if the event already happened and return
00119      * immediately in this case.
00120      */
00121     if (sig_checkSignal(&e->Ev.Sig.sig,
00122             EVENT_GENERIC_SIGNAL) == EVENT_GENERIC_SIGNAL)
00123         return true;
00124     return event_waitTimeoutSlowPath(e, timeout);
00125 }
00126 
00127 /*
00128  * event_select() slow path: this function handles the case when any event was
00129  * not yet notified, so it takes care of making the current process to sleep on
00130  * the list of events, mapping them to a different signal bit and issuing a
00131  * call to sig_waitTimeout() using the process's sigmask.
00132  */
00133 static NOINLINE int event_selectSlowPath(Event **evs, int n, ticks_t timeout)
00134 {
00135     sigmask_t mask = (1 << n) - 1;
00136     int i;
00137 
00138     for (i = 0; i < n; i++)
00139     {
00140         Event *e = evs[i];
00141 
00142         /* Map each event to a distinct signal bit */
00143         e->Ev.Sig.sig_proc = proc_current();
00144         e->Ev.Sig.sig_bit = 1 << i;
00145         e->action = event_hook_generic_multiple_signal;
00146     }
00147     IRQ_ENABLE;
00148 
00149     mask = timeout ? sig_waitTimeout(mask, timeout) : sig_wait(mask);
00150     if (mask & SIG_TIMEOUT)
00151         return -1;
00152     return UINT8_LOG2(mask);
00153 }
00154 
00155 int event_select(Event **evs, int n, ticks_t timeout)
00156 {
00157     int i;
00158 
00159     ASSERT(n <= SIG_USER_MAX);
00160 
00161     IRQ_DISABLE;
00162     /* Fast path: check if one of the event already happened */
00163     for (i = 0; i < n; i++)
00164     {
00165         Event *e = evs[i];
00166 
00167         if (__sig_checkSignal(&e->Ev.Sig.sig,
00168                 EVENT_GENERIC_SIGNAL) == EVENT_GENERIC_SIGNAL)
00169         {
00170             IRQ_ENABLE;
00171             return i;
00172         }
00173     }
00174     /* Otherwise, fallback to the slow path */
00175     return event_selectSlowPath(evs, n, timeout);
00176 }
00177 #else /* !(CONFIG_KERN && CONFIG_KERN_SIGNALS) */
00178 bool event_waitTimeout(Event *e, ticks_t timeout)
00179 {
00180     ticks_t end = timer_clock() + timeout;
00181     bool ret;
00182 
00183     while ((ACCESS_SAFE(e->Ev.Gen.completed) == false) ||
00184             TIMER_AFTER(timer_clock(), end))
00185         cpu_relax();
00186     ret = e->Ev.Gen.completed;
00187     e->Ev.Gen.completed = false;
00188     MEMORY_BARRIER;
00189 
00190     return ret;
00191 }
00192 
00193 int event_select(Event **evs, int n, ticks_t timeout)
00194 {
00195     ticks_t end = timer_clock() + timeout;
00196     int i;
00197 
00198     while (1)
00199     {
00200         for (i = 0; i < n; i++)
00201         {
00202             Event *e = evs[i];
00203             if (ACCESS_SAFE(e->Ev.Gen.completed) == true)
00204             {
00205                 e->Ev.Gen.completed = false;
00206                 MEMORY_BARRIER;
00207                 return i;
00208             }
00209         }
00210         if (timeout && TIMER_AFTER(timer_clock(), end))
00211             break;
00212         cpu_relax();
00213     }
00214     return -1;
00215 }
00216 #endif /* CONFIG_KERN && CONFIG_KERN_SIGNALS */