ChibiOS/RT
2.6.0
chevents.c
Go to the documentation of this file.
00001 /*
00002     ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
00003                  2011,2012,2013 Giovanni Di Sirio.
00004 
00005     This file is part of ChibiOS/RT.
00006 
00007     ChibiOS/RT is free software; you can redistribute it and/or modify
00008     it under the terms of the GNU General Public License as published by
00009     the Free Software Foundation; either version 3 of the License, or
00010     (at your option) any later version.
00011 
00012     ChibiOS/RT is distributed in the hope that it will be useful,
00013     but WITHOUT ANY WARRANTY; without even the implied warranty of
00014     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015     GNU General Public License for more details.
00016 
00017     You should have received a copy of the GNU General Public License
00018     along with this program.  If not, see <http://www.gnu.org/licenses/>.
00019 
00020                                       ---
00021 
00022     A special exception to the GPL can be applied should you wish to distribute
00023     a combined work that includes ChibiOS/RT, without being obliged to provide
00024     the source code for any proprietary components. See the file exception.txt
00025     for full details of how and when the exception can be applied.
00026 */
00027 /*
00028    Concepts and parts of this file have been contributed by Scott (skute).
00029  */
00030 
00031 /**
00032  * @file    chevents.c
00033  * @brief   Events code.
00034  *
00035  * @addtogroup events
00036  * @details Event Flags, Event Sources and Event Listeners.
00037  *          <h2>Operation mode</h2>
00038  *          Each thread has a mask of pending event flags inside its @p Thread
00039  *          structure.
00040  *          Operations defined for event flags:
00041  *          - <b>Wait</b>, the invoking thread goes to sleep until a certain
00042  *            AND/OR combination of event flags becomes pending.
00043  *          - <b>Clear</b>, a mask of event flags is cleared from the pending
00044  *            events mask, the cleared event flags mask is returned (only the
00045  *            flags that were actually pending and then cleared).
00046  *          - <b>Signal</b>, an event mask is directly ORed to the mask of the
00047  *            signaled thread.
00048  *          - <b>Broadcast</b>, each thread registered on an Event Source is
00049  *            signaled with the event flags specified in its Event Listener.
00050  *          - <b>Dispatch</b>, an events mask is scanned and for each bit set
00051  *            to one an associated handler function is invoked. Bit masks are
00052  *            scanned from bit zero upward.
00053  *          .
00054  *          An Event Source is a special object that can be "broadcasted" by
00055  *          a thread or an interrupt service routine. Broadcasting an Event
00056  *          Source has the effect that all the threads registered on the
00057  *          Event Source will be signaled with an events mask.<br>
00058  *          An unlimited number of Event Sources can exists in a system and
00059  *          each thread can be listening on an unlimited number of
00060  *          them.
00061  * @pre     In order to use the Events APIs the @p CH_USE_EVENTS option must be
00062  *          enabled in @p chconf.h.
00063  * @post    Enabling events requires 1-4 (depending on the architecture)
00064  *          extra bytes in the @p Thread structure.
00065  * @{
00066  */
00067 
00068 #include "ch.h"
00069 
00070 #if CH_USE_EVENTS || defined(__DOXYGEN__)
00071 /**
00072  * @brief   Registers an Event Listener on an Event Source.
00073  * @details Once a thread has registered as listener on an event source it
00074  *          will be notified of all events broadcasted there.
00075  * @note    Multiple Event Listeners can specify the same bits to be ORed to
00076  *          different threads.
00077  *
00078  * @param[in] esp       pointer to the  @p EventSource structure
00079  * @param[in] elp       pointer to the @p EventListener structure
00080  * @param[in] mask      the mask of event flags to be ORed to the thread when
00081  *                      the event source is broadcasted
00082  *
00083  * @api
00084  */
00085 void chEvtRegisterMask(EventSource *esp, EventListener *elp, eventmask_t mask) {
00086 
00087   chDbgCheck((esp != NULL) && (elp != NULL), "chEvtRegisterMask");
00088 
00089   chSysLock();
00090   elp->el_next     = esp->es_next;
00091   esp->es_next     = elp;
00092   elp->el_listener = currp;
00093   elp->el_mask     = mask;
00094   elp->el_flags    = 0;
00095   chSysUnlock();
00096 }
00097 
00098 /**
00099  * @brief   Unregisters an Event Listener from its Event Source.
00100  * @note    If the event listener is not registered on the specified event
00101  *          source then the function does nothing.
00102  * @note    For optimal performance it is better to perform the unregister
00103  *          operations in inverse order of the register operations (elements
00104  *          are found on top of the list).
00105  *
00106  * @param[in] esp       pointer to the  @p EventSource structure
00107  * @param[in] elp       pointer to the @p EventListener structure
00108  *
00109  * @api
00110  */
00111 void chEvtUnregister(EventSource *esp, EventListener *elp) {
00112   EventListener *p;
00113 
00114   chDbgCheck((esp != NULL) && (elp != NULL), "chEvtUnregister");
00115 
00116   p = (EventListener *)esp;
00117   chSysLock();
00118   while (p->el_next != (EventListener *)esp) {
00119     if (p->el_next == elp) {
00120       p->el_next = elp->el_next;
00121       break;
00122     }
00123     p = p->el_next;
00124   }
00125   chSysUnlock();
00126 }
00127 
00128 /**
00129  * @brief   Clears the pending events specified in the mask.
00130  *
00131  * @param[in] mask      the events to be cleared
00132  * @return              The pending events that were cleared.
00133  *
00134  * @api
00135  */
00136 eventmask_t chEvtGetAndClearEvents(eventmask_t mask) {
00137   eventmask_t m;
00138 
00139   chSysLock();
00140 
00141   m = currp->p_epending & mask;
00142   currp->p_epending &= ~mask;
00143 
00144   chSysUnlock();
00145   return m;
00146 }
00147 
00148 /**
00149  * @brief   Adds (OR) a set of event flags on the current thread, this is
00150  *          @b much faster than using @p chEvtBroadcast() or @p chEvtSignal().
00151  *
00152  * @param[in] mask      the event flags to be added
00153  * @return              The current pending events mask.
00154  *
00155  * @api
00156  */
00157 eventmask_t chEvtAddEvents(eventmask_t mask) {
00158 
00159   chSysLock();
00160 
00161   mask = (currp->p_epending |= mask);
00162 
00163   chSysUnlock();
00164   return mask;
00165 }
00166 
00167 /**
00168  * @brief   Signals all the Event Listeners registered on the specified Event
00169  *          Source.
00170  * @details This function variants ORs the specified event flags to all the
00171  *          threads registered on the @p EventSource in addition to the event
00172  *          flags specified by the threads themselves in the
00173  *          @p EventListener objects.
00174  * @post    This function does not reschedule so a call to a rescheduling
00175  *          function must be performed before unlocking the kernel. Note that
00176  *          interrupt handlers always reschedule on exit so an explicit
00177  *          reschedule must not be performed in ISRs.
00178  *
00179  * @param[in] esp       pointer to the @p EventSource structure
00180  * @param[in] flags     the flags set to be added to the listener flags mask
00181  *
00182  * @iclass
00183  */
00184 void chEvtBroadcastFlagsI(EventSource *esp, flagsmask_t flags) {
00185   EventListener *elp;
00186 
00187   chDbgCheckClassI();
00188   chDbgCheck(esp != NULL, "chEvtBroadcastMaskI");
00189 
00190   elp = esp->es_next;
00191   while (elp != (EventListener *)esp) {
00192     elp->el_flags |= flags;
00193     chEvtSignalI(elp->el_listener, elp->el_mask);
00194     elp = elp->el_next;
00195   }
00196 }
00197 
00198 /**
00199  * @brief   Returns the flags associated to an @p EventListener.
00200  * @details The flags are returned and the @p EventListener flags mask is
00201  *          cleared.
00202  *
00203  * @param[in] elp       pointer to the @p EventListener structure
00204  * @return              The flags added to the listener by the associated
00205  *                      event source.
00206  *
00207  * @iclass
00208  */
00209 flagsmask_t chEvtGetAndClearFlags(EventListener *elp) {
00210   flagsmask_t flags;
00211 
00212   chSysLock();
00213 
00214   flags = elp->el_flags;
00215   elp->el_flags = 0;
00216 
00217   chSysUnlock();
00218   return flags;
00219 }
00220 
00221 /**
00222  * @brief   Adds a set of event flags directly to specified @p Thread.
00223  *
00224  * @param[in] tp        the thread to be signaled
00225  * @param[in] mask      the event flags set to be ORed
00226  *
00227  * @api
00228  */
00229 void chEvtSignal(Thread *tp, eventmask_t mask) {
00230 
00231   chDbgCheck(tp != NULL, "chEvtSignal");
00232 
00233   chSysLock();
00234   chEvtSignalI(tp, mask);
00235   chSchRescheduleS();
00236   chSysUnlock();
00237 }
00238 
00239 /**
00240  * @brief   Adds a set of event flags directly to specified @p Thread.
00241  * @post    This function does not reschedule so a call to a rescheduling
00242  *          function must be performed before unlocking the kernel. Note that
00243  *          interrupt handlers always reschedule on exit so an explicit
00244  *          reschedule must not be performed in ISRs.
00245  *
00246  * @param[in] tp        the thread to be signaled
00247  * @param[in] mask      the event flags set to be ORed
00248  *
00249  * @iclass
00250  */
00251 void chEvtSignalI(Thread *tp, eventmask_t mask) {
00252 
00253   chDbgCheckClassI();
00254   chDbgCheck(tp != NULL, "chEvtSignalI");
00255 
00256   tp->p_epending |= mask;
00257   /* Test on the AND/OR conditions wait states.*/
00258   if (((tp->p_state == THD_STATE_WTOREVT) &&
00259        ((tp->p_epending & tp->p_u.ewmask) != 0)) ||
00260       ((tp->p_state == THD_STATE_WTANDEVT) &&
00261        ((tp->p_epending & tp->p_u.ewmask) == tp->p_u.ewmask)))
00262     chSchReadyI(tp)->p_u.rdymsg = RDY_OK;
00263 }
00264 
00265 /**
00266  * @brief   Signals all the Event Listeners registered on the specified Event
00267  *          Source.
00268  * @details This function variants ORs the specified event flags to all the
00269  *          threads registered on the @p EventSource in addition to the event
00270  *          flags specified by the threads themselves in the
00271  *          @p EventListener objects.
00272  *
00273  * @param[in] esp       pointer to the @p EventSource structure
00274  * @param[in] flags     the flags set to be added to the listener flags mask
00275  *
00276  * @api
00277  */
00278 void chEvtBroadcastFlags(EventSource *esp, flagsmask_t flags) {
00279 
00280   chSysLock();
00281   chEvtBroadcastFlagsI(esp, flags);
00282   chSchRescheduleS();
00283   chSysUnlock();
00284 }
00285 
00286 /**
00287  * @brief   Returns the flags associated to an @p EventListener.
00288  * @details The flags are returned and the @p EventListener flags mask is
00289  *          cleared.
00290  *
00291  * @param[in] elp       pointer to the @p EventListener structure
00292  * @return              The flags added to the listener by the associated
00293  *                      event source.
00294  *
00295  * @iclass
00296  */
00297 flagsmask_t chEvtGetAndClearFlagsI(EventListener *elp) {
00298   flagsmask_t flags;
00299 
00300   flags = elp->el_flags;
00301   elp->el_flags = 0;
00302 
00303   return flags;
00304 }
00305 
00306 /**
00307  * @brief   Invokes the event handlers associated to an event flags mask.
00308  *
00309  * @param[in] mask      mask of the event flags to be dispatched
00310  * @param[in] handlers  an array of @p evhandler_t. The array must have size
00311  *                      equal to the number of bits in eventmask_t.
00312  *
00313  * @api
00314  */
00315 void chEvtDispatch(const evhandler_t *handlers, eventmask_t mask) {
00316   eventid_t eid;
00317 
00318   chDbgCheck(handlers != NULL, "chEvtDispatch");
00319 
00320   eid = 0;
00321   while (mask) {
00322     if (mask & EVENT_MASK(eid)) {
00323       chDbgAssert(handlers[eid] != NULL,
00324                   "chEvtDispatch(), #1",
00325                   "null handler");
00326       mask &= ~EVENT_MASK(eid);
00327       handlers[eid](eid);
00328     }
00329     eid++;
00330   }
00331 }
00332 
00333 #if CH_OPTIMIZE_SPEED || !CH_USE_EVENTS_TIMEOUT || defined(__DOXYGEN__)
00334 /**
00335  * @brief   Waits for exactly one of the specified events.
00336  * @details The function waits for one event among those specified in
00337  *          @p mask to become pending then the event is cleared and returned.
00338  * @note    One and only one event is served in the function, the one with the
00339  *          lowest event id. The function is meant to be invoked into a loop in
00340  *          order to serve all the pending events.<br>
00341  *          This means that Event Listeners with a lower event identifier have
00342  *          an higher priority.
00343  *
00344  * @param[in] mask      mask of the event flags that the function should wait
00345  *                      for, @p ALL_EVENTS enables all the events
00346  * @return              The mask of the lowest id served and cleared event.
00347  *
00348  * @api
00349  */
00350 eventmask_t chEvtWaitOne(eventmask_t mask) {
00351   Thread *ctp = currp;
00352   eventmask_t m;
00353 
00354   chSysLock();
00355 
00356   if ((m = (ctp->p_epending & mask)) == 0) {
00357     ctp->p_u.ewmask = mask;
00358     chSchGoSleepS(THD_STATE_WTOREVT);
00359     m = ctp->p_epending & mask;
00360   }
00361   m &= -m;
00362   ctp->p_epending &= ~m;
00363 
00364   chSysUnlock();
00365   return m;
00366 }
00367 
00368 /**
00369  * @brief   Waits for any of the specified events.
00370  * @details The function waits for any event among those specified in
00371  *          @p mask to become pending then the events are cleared and returned.
00372  *
00373  * @param[in] mask      mask of the event flags that the function should wait
00374  *                      for, @p ALL_EVENTS enables all the events
00375  * @return              The mask of the served and cleared events.
00376  *
00377  * @api
00378  */
00379 eventmask_t chEvtWaitAny(eventmask_t mask) {
00380   Thread *ctp = currp;
00381   eventmask_t m;
00382 
00383   chSysLock();
00384 
00385   if ((m = (ctp->p_epending & mask)) == 0) {
00386     ctp->p_u.ewmask = mask;
00387     chSchGoSleepS(THD_STATE_WTOREVT);
00388     m = ctp->p_epending & mask;
00389   }
00390   ctp->p_epending &= ~m;
00391 
00392   chSysUnlock();
00393   return m;
00394 }
00395 
00396 /**
00397  * @brief   Waits for all the specified events.
00398  * @details The function waits for all the events specified in @p mask to
00399  *          become pending then the events are cleared and returned.
00400  *
00401  * @param[in] mask      mask of the event flags that the function should wait
00402  *                      for, @p ALL_EVENTS requires all the events
00403  * @return              The mask of the served and cleared events.
00404  *
00405  * @api
00406  */
00407 eventmask_t chEvtWaitAll(eventmask_t mask) {
00408   Thread *ctp = currp;
00409 
00410   chSysLock();
00411 
00412   if ((ctp->p_epending & mask) != mask) {
00413     ctp->p_u.ewmask = mask;
00414     chSchGoSleepS(THD_STATE_WTANDEVT);
00415   }
00416   ctp->p_epending &= ~mask;
00417 
00418   chSysUnlock();
00419   return mask;
00420 }
00421 #endif /* CH_OPTIMIZE_SPEED || !CH_USE_EVENTS_TIMEOUT */
00422 
00423 #if CH_USE_EVENTS_TIMEOUT || defined(__DOXYGEN__)
00424 /**
00425  * @brief   Waits for exactly one of the specified events.
00426  * @details The function waits for one event among those specified in
00427  *          @p mask to become pending then the event is cleared and returned.
00428  * @note    One and only one event is served in the function, the one with the
00429  *          lowest event id. The function is meant to be invoked into a loop in
00430  *          order to serve all the pending events.<br>
00431  *          This means that Event Listeners with a lower event identifier have
00432  *          an higher priority.
00433  *
00434  * @param[in] mask      mask of the event flags that the function should wait
00435  *                      for, @p ALL_EVENTS enables all the events
00436  * @param[in] time      the number of ticks before the operation timeouts,
00437  *                      the following special values are allowed:
00438  *                      - @a TIME_IMMEDIATE immediate timeout.
00439  *                      - @a TIME_INFINITE no timeout.
00440  *                      .
00441  * @return              The mask of the lowest id served and cleared event.
00442  * @retval 0            if the operation has timed out.
00443  *
00444  * @api
00445  */
00446 eventmask_t chEvtWaitOneTimeout(eventmask_t mask, systime_t time) {
00447   Thread *ctp = currp;
00448   eventmask_t m;
00449 
00450   chSysLock();
00451 
00452   if ((m = (ctp->p_epending & mask)) == 0) {
00453     if (TIME_IMMEDIATE == time) {
00454       chSysUnlock();
00455       return (eventmask_t)0;
00456     }
00457     ctp->p_u.ewmask = mask;
00458     if (chSchGoSleepTimeoutS(THD_STATE_WTOREVT, time) < RDY_OK) {
00459       chSysUnlock();
00460       return (eventmask_t)0;
00461     }
00462     m = ctp->p_epending & mask;
00463   }
00464   m &= -m;
00465   ctp->p_epending &= ~m;
00466 
00467   chSysUnlock();
00468   return m;
00469 }
00470 
00471 /**
00472  * @brief   Waits for any of the specified events.
00473  * @details The function waits for any event among those specified in
00474  *          @p mask to become pending then the events are cleared and
00475  *          returned.
00476  *
00477  * @param[in] mask      mask of the event flags that the function should wait
00478  *                      for, @p ALL_EVENTS enables all the events
00479  * @param[in] time      the number of ticks before the operation timeouts,
00480  *                      the following special values are allowed:
00481  *                      - @a TIME_IMMEDIATE immediate timeout.
00482  *                      - @a TIME_INFINITE no timeout.
00483  *                      .
00484  * @return              The mask of the served and cleared events.
00485  * @retval 0            if the operation has timed out.
00486  *
00487  * @api
00488  */
00489 eventmask_t chEvtWaitAnyTimeout(eventmask_t mask, systime_t time) {
00490   Thread *ctp = currp;
00491   eventmask_t m;
00492 
00493   chSysLock();
00494 
00495   if ((m = (ctp->p_epending & mask)) == 0) {
00496     if (TIME_IMMEDIATE == time) {
00497       chSysUnlock();
00498       return (eventmask_t)0;
00499     }
00500     ctp->p_u.ewmask = mask;
00501     if (chSchGoSleepTimeoutS(THD_STATE_WTOREVT, time) < RDY_OK) {
00502       chSysUnlock();
00503       return (eventmask_t)0;
00504     }
00505     m = ctp->p_epending & mask;
00506   }
00507   ctp->p_epending &= ~m;
00508 
00509   chSysUnlock();
00510   return m;
00511 }
00512 
00513 /**
00514  * @brief   Waits for all the specified events.
00515  * @details The function waits for all the events specified in @p mask to
00516  *          become pending then the events are cleared and returned.
00517  *
00518  * @param[in] mask      mask of the event flags that the function should wait
00519  *                      for, @p ALL_EVENTS requires all the events
00520  * @param[in] time      the number of ticks before the operation timeouts,
00521  *                      the following special values are allowed:
00522  *                      - @a TIME_IMMEDIATE immediate timeout.
00523  *                      - @a TIME_INFINITE no timeout.
00524  *                      .
00525  * @return              The mask of the served and cleared events.
00526  * @retval 0            if the operation has timed out.
00527  *
00528  * @api
00529  */
00530 eventmask_t chEvtWaitAllTimeout(eventmask_t mask, systime_t time) {
00531   Thread *ctp = currp;
00532 
00533   chSysLock();
00534 
00535   if ((ctp->p_epending & mask) != mask) {
00536     if (TIME_IMMEDIATE == time) {
00537       chSysUnlock();
00538       return (eventmask_t)0;
00539     }
00540     ctp->p_u.ewmask = mask;
00541     if (chSchGoSleepTimeoutS(THD_STATE_WTANDEVT, time) < RDY_OK) {
00542       chSysUnlock();
00543       return (eventmask_t)0;
00544     }
00545   }
00546   ctp->p_epending &= ~mask;
00547 
00548   chSysUnlock();
00549   return mask;
00550 }
00551 #endif /* CH_USE_EVENTS_TIMEOUT */
00552 
00553 #endif /* CH_USE_EVENTS */
00554 
00555 /** @} */