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