From: arighi Date: Mon, 14 Mar 2011 12:01:12 +0000 (+0000) Subject: event: generic event refactoring X-Git-Tag: 2.7.0~197 X-Git-Url: https://codewiz.org/gitweb?a=commitdiff_plain;h=a97e90d132f28738a92e709a543348e325ebe095;p=bertos.git event: generic event refactoring Fix the behaviour of the following primitives: - event_waitTimeout(): this is now implemented using a single sig_wait() on the event's embedded sigmask. The timeout is notified using a custom timer handler that sends the signal SIG_TIMEOUT to the same event's embedded sigmask. This also fixes the hard-fault bug reported by Nicolas Dandrimont: (http://lists.develer.com/pipermail/bertos/2011-March/003164.html) - event_select(): multiple event wait routine. Implement this functionality mapping multiple events to different signal bits of the process's sigmask and performing a single sig_wait() on that mask. Signal bits are "allocated" internally by the event_select() itself and must not be provided by the user. NOTE: event_select() should be still considered an experimental feature, so the API may change in the future (as reported in the documentation). git-svn-id: https://src.develer.com/svnoss/bertos/trunk@4769 38d2e660-2303-0410-9eaa-f027e97ec537 --- diff --git a/bertos/mware/event.c b/bertos/mware/event.c index 72663037..9dcb71a0 100644 --- a/bertos/mware/event.c +++ b/bertos/mware/event.c @@ -47,7 +47,18 @@ void event_hook_ignore(UNUSED_ARG(Event *, e)) { } -#if CONFIG_KERN_SIGNALS +void event_hook_softint(Event *e) +{ + e->Ev.Int.func(e->Ev.Int.user_data); +} + +void event_hook_generic(Event *e) +{ + e->Ev.Gen.completed = true; + MEMORY_BARRIER; +} + +#if CONFIG_KERN && CONFIG_KERN_SIGNALS void event_hook_signal(Event *e) { sig_post((e)->Ev.Sig.sig_proc, (e)->Ev.Sig.sig_bit); @@ -55,50 +66,130 @@ void event_hook_signal(Event *e) void event_hook_generic_signal(Event *e) { - sig_postSignal(&e->Ev.SigGen.sig, - e->Ev.SigGen.sig_proc, - EVENT_GENERIC_SIGNAL); + sig_postSignal(&e->Ev.Sig.sig, e->Ev.Sig.sig_proc, e->Ev.Sig.sig_bit); } -#endif -void event_hook_softint(Event *e) +/* + * Custom event hook to notify the completion of a event monitored via + * event_select(). + */ +static void event_hook_generic_multiple_signal(Event *e) { - e->Ev.Int.func(e->Ev.Int.user_data); + sig_post(e->Ev.Sig.sig_proc, e->Ev.Sig.sig_bit); } -void event_hook_generic(Event *e) +/* + * Custom timer hook to notify the timeout of a event_waitTimeout(). + */ +static void event_hook_generic_timeout_signal(void *arg) { - e->Ev.Gen.completed = true; - MEMORY_BARRIER; + Event *e = (Event *)arg; + + sig_postSignal(&e->Ev.Sig.sig, e->Ev.Sig.sig_proc, SIG_TIMEOUT); } -/** - * Wait for multiple events - * - * On success return the offset in the \a evs vector of the Event that - * happened, -1 if the timeout expires. +/* + * event_waitTimeout() slow path: this function put the current process to + * sleep until the event is notified. The timeout is managed using the custom + * timer hook event_hook_generic_timeout_signal(): if the timeout expires the + * signal SIG_TIMEOUT is notified via sig_post() to the sigmask embedded in the + * event. * - * NOTE: timeout == 0 means no timeout. + * The custom timer hook is required because the default timer's behaviour is + * to use the process's sigmask to notify the completion of an event, that is + * not suitable for this case, because we're sleeping on the event's sigmask + * instead. */ -#if defined(CONFIG_KERN_SIGNALS) && CONFIG_KERN_SIGNALS -int event_select(Event **evs, int n, ticks_t timeout) +static NOINLINE bool event_waitTimeoutSlowPath(Event *e, ticks_t timeout) +{ + bool ret; + + e->Ev.Sig.sig_proc = proc_current(); + ret = (sig_waitTimeoutSignal(&e->Ev.Sig.sig, + EVENT_GENERIC_SIGNAL, timeout, + event_hook_generic_timeout_signal, e) & SIG_TIMEOUT) ? + false : true; + return ret; +} + +bool event_waitTimeout(Event *e, ticks_t timeout) +{ + /* + * Fast path: check if the event already happened and return + * immediately in this case. + */ + if (sig_checkSignal(&e->Ev.Sig.sig, + EVENT_GENERIC_SIGNAL) == EVENT_GENERIC_SIGNAL) + return true; + return event_waitTimeoutSlowPath(e, timeout); +} + +/* + * event_select() slow path: this function handles the case when any event was + * not yet notified, so it takes care of making the current process to sleep on + * the list of events, mapping them to a different signal bit and issuing a + * call to sig_waitTimeout() using the process's sigmask. + */ +static NOINLINE int event_selectSlowPath(Event **evs, int n, ticks_t timeout) { sigmask_t mask = (1 << n) - 1; int i; - ASSERT(n <= SIG_USER_MAX); for (i = 0; i < n; i++) { Event *e = evs[i]; + /* Map each event to a distinct signal bit */ - event_initSignal(e, proc_current(), 1 << i); + e->Ev.Sig.sig_proc = proc_current(); + e->Ev.Sig.sig_bit = 1 << i; + e->action = event_hook_generic_multiple_signal; } + IRQ_ENABLE; + mask = timeout ? sig_waitTimeout(mask, timeout) : sig_wait(mask); - i = UINT8_LOG2(mask); + if (mask & SIG_TIMEOUT) + return -1; + return UINT8_LOG2(mask); +} + +int event_select(Event **evs, int n, ticks_t timeout) +{ + int i; - return i < n ? i : -1; + ASSERT(n <= SIG_USER_MAX); + + IRQ_DISABLE; + /* Fast path: check if one of the event already happened */ + for (i = 0; i < n; i++) + { + Event *e = evs[i]; + + if (__sig_checkSignal(&e->Ev.Sig.sig, + EVENT_GENERIC_SIGNAL) == EVENT_GENERIC_SIGNAL) + { + IRQ_ENABLE; + return i; + } + } + /* Otherwise, fallback to the slow path */ + return event_selectSlowPath(evs, n, timeout); } -#else +#else /* !(CONFIG_KERN && CONFIG_KERN_SIGNALS) */ +bool event_waitTimeout(Event *e, ticks_t timeout) +{ + ticks_t end = timer_clock() + timeout; + bool ret; + + while ((ACCESS_SAFE(e->Ev.Gen.completed) == false) || + TIMER_AFTER(timer_clock(), end)) + cpu_relax(); + ret = e->Ev.Gen.completed; + e->Ev.Gen.completed = false; + MEMORY_BARRIER; + + return ret; +} + int event_select(Event **evs, int n, ticks_t timeout) { ticks_t end = timer_clock() + timeout; @@ -122,4 +213,4 @@ int event_select(Event **evs, int n, ticks_t timeout) } return -1; } -#endif +#endif /* CONFIG_KERN && CONFIG_KERN_SIGNALS */ diff --git a/bertos/mware/event.h b/bertos/mware/event.h index 30c1c815..c8f38ee1 100644 --- a/bertos/mware/event.h +++ b/bertos/mware/event.h @@ -144,13 +144,10 @@ #include /* cpu_relax() */ -#if CONFIG_KERN - #if defined(CONFIG_KERN_SIGNALS) && CONFIG_KERN_SIGNALS - #include - #endif - - /* Forward decl */ - struct Process; +#if CONFIG_KERN && CONFIG_KERN_SIGNALS +#include +/* Forward decl */ +struct Process; #endif typedef struct Event @@ -158,18 +155,13 @@ typedef struct Event void (*action)(struct Event *); union { -#if defined(CONFIG_KERN_SIGNALS) && CONFIG_KERN_SIGNALS +#if CONFIG_KERN && CONFIG_KERN_SIGNALS struct { struct Process *sig_proc; /* Process to be signalled */ sigbit_t sig_bit; /* Signal to send */ + Signal sig; /* Local signal structure (used by generic event) */ } Sig; - - struct - { - struct Process *sig_proc; /* Process to be signalled */ - Signal sig; /* Signal structure */ - } SigGen; #endif struct { @@ -195,7 +187,6 @@ void event_hook_generic_signal(Event *event); ((e)->action = event_hook_ignore) /** Same as event_initNone(), but returns the initialized event */ -INLINE Event event_createNone(void); INLINE Event event_createNone(void) { Event e; @@ -217,8 +208,7 @@ INLINE Event event_createSoftint(Hook func, void *user_data) return e; } -#if defined(CONFIG_KERN_SIGNALS) && CONFIG_KERN_SIGNALS - +#if CONFIG_KERN && CONFIG_KERN_SIGNALS /** Initialize the event \a e with a signal (send signal \a s to process \a p) */ #define event_initSignal(e,p,s) \ ((e)->action = event_hook_signal,(e)->Ev.Sig.sig_proc = (p), (e)->Ev.Sig.sig_bit = (s)) @@ -233,24 +223,22 @@ INLINE Event event_createSignal(struct Process *proc, sigbit_t bit) return e; } -#endif +/** + * Signal used to implement generic events. + */ +#define EVENT_GENERIC_SIGNAL SIG_SYSTEM6 -#if defined(CONFIG_KERN_SIGNALS) && CONFIG_KERN_SIGNALS /** Initialize the generic sleepable event \a e */ #define event_initGeneric(e) \ ((e)->action = event_hook_generic_signal, \ - (e)->Ev.SigGen.sig_proc = proc_current(), \ - (e)->Ev.SigGen.sig.wait = 0, (e)->Ev.SigGen.sig.recv = 0) + (e)->Ev.Sig.sig_proc = proc_current(), \ + (e)->Ev.Sig.sig_bit = EVENT_GENERIC_SIGNAL, \ + (e)->Ev.Sig.sig.wait = 0, (e)->Ev.Sig.sig.recv = 0) #else #define event_initGeneric(e) \ ((e)->action = event_hook_generic, (e)->Ev.Gen.completed = false) #endif -/** - * Signal used to implement generic events. - */ -#define EVENT_GENERIC_SIGNAL SIG_SYSTEM5 - /** * Create a generic sleepable event. * @@ -272,9 +260,9 @@ INLINE Event event_createGeneric(void) */ INLINE void event_wait(Event *e) { -#if defined(CONFIG_KERN_SIGNALS) && CONFIG_KERN_SIGNALS +#if CONFIG_KERN_SIGNALS e->Ev.Sig.sig_proc = proc_current(); - sig_waitSignal(&e->Ev.SigGen.sig, EVENT_GENERIC_SIGNAL); + sig_waitSignal(&e->Ev.Sig.sig, EVENT_GENERIC_SIGNAL); #else while (ACCESS_SAFE(e->Ev.Gen.completed) == false) cpu_relax(); @@ -283,38 +271,24 @@ INLINE void event_wait(Event *e) #endif } +/** + * Wait for multiple events + * + * On success return the offset in the \a evs vector of the Event that + * happened, -1 if the timeout expires. + * + * NOTE: timeout == 0 means no timeout. + * + * \attention The API is work in progress and may change in future versions. + */ int event_select(Event **evs, int n, ticks_t timeout); -#if CONFIG_TIMER_EVENTS -#include /* timer_clock() */ - /** * Wait the completion of event \a e or \a timeout elapses. * * \note It's forbidden to use this function inside irq handling functions. */ -INLINE bool event_waitTimeout(Event *e, ticks_t timeout) -{ - bool ret; - -#if defined(CONFIG_KERN_SIGNALS) && CONFIG_KERN_SIGNALS - e->Ev.Sig.sig_proc = proc_current(); - ret = (sig_waitTimeoutSignal(&e->Ev.SigGen.sig, - EVENT_GENERIC_SIGNAL, timeout) & SIG_TIMEOUT) ? - false : true; -#else - ticks_t end = timer_clock() + timeout; - - while ((ACCESS_SAFE(e->Ev.Gen.completed) == false) || - TIMER_AFTER(timer_clock(), end)) - cpu_relax(); - ret = e->Ev.Gen.completed; - e->Ev.Gen.completed = false; -#endif - MEMORY_BARRIER; - return ret; -} -#endif /* CONFIG_TIMER_EVENTS */ +bool event_waitTimeout(Event *e, ticks_t timeout); /** * Trigger an event.