Nut/OS  4.10.3
API Reference
event.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2001-2006 by egnite Software GmbH. All rights reserved.
00003  *
00004  * Redistribution and use in source and binary forms, with or without
00005  * modification, are permitted provided that the following conditions
00006  * are met:
00007  *
00008  * 1. Redistributions of source code must retain the above copyright
00009  *    notice, this list of conditions and the following disclaimer.
00010  * 2. Redistributions in binary form must reproduce the above copyright
00011  *    notice, this list of conditions and the following disclaimer in the
00012  *    documentation and/or other materials provided with the distribution.
00013  * 3. Neither the name of the copyright holders nor the names of
00014  *    contributors may be used to endorse or promote products derived
00015  *    from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY EGNITE SOFTWARE GMBH AND CONTRIBUTORS
00018  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00019  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00020  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL EGNITE
00021  * SOFTWARE GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00022  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00023  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
00024  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
00025  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00026  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
00027  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00028  * SUCH DAMAGE.
00029  *
00030  * For additional information see http://www.ethernut.de/
00031  */
00032 
00161 #include <cfg/os.h>
00162 
00163 #include <compiler.h>
00164 #include <sys/atom.h>
00165 #include <sys/heap.h>
00166 #include <sys/timer.h>
00167 #include <sys/thread.h>
00168 #include <sys/event.h>
00169 #include <sys/nutdebug.h>
00170 
00171 #ifdef NUTDEBUG
00172 #include <sys/osdebug.h>
00173 #include <stdio.h>
00174 #endif
00175 
00176 #ifdef NUTTRACER
00177 #include <sys/tracer.h>
00178 #endif
00179 
00184 
00185 
00196 void NutEventTimeout(HANDLE timer, void *arg)
00197 {
00198     NUTTHREADINFO *tqp;
00199     NUTTHREADINFO *volatile *tqpp = arg;
00200 
00201     NUTASSERT(tqpp != NULL);
00202 
00203     /* Get the queue's root atomically. */
00204     NutEnterCritical();
00205     tqp = *tqpp;
00206     NutExitCritical();
00207 
00208     /*
00209      * A signaled queue is an empty queue. So our
00210      * thread already left this queue.
00211      */
00212     if (tqp != SIGNALED) {
00213 
00214         /*
00215          * Walk down the linked list and identify
00216          * the thread by the timer id it is waiting
00217          * for.
00218          */
00219         while (tqp) {
00220             if (tqp->td_timer == timer) {
00221                 /* Found the thread. Remove it from the event queue. */
00222                    
00223                 NutEnterCritical();
00224                 *tqpp = tqp->td_qnxt;
00225                 if (tqp->td_qpec) {
00226                     if (tqp->td_qnxt) {
00227                         tqp->td_qnxt->td_qpec = tqp->td_qpec;
00228                     }
00229                     else {
00230                         *tqpp = SIGNALED;
00231                     }
00232                     tqp->td_qpec = 0;
00233                 }
00234                 NutExitCritical();
00235 
00236                 /* Add it to the queue of threads, which are ready to run. */
00237                 tqp->td_state = TDS_READY;
00238                 NutThreadAddPriQueue(tqp, (NUTTHREADINFO **) & runQueue);
00239 
00240                 /* Signal the timer entry in the thread's info structure.
00241                    This will tell the waiting thread, that it has been
00242                    woken up by a timeout. */
00243                 tqp->td_timer = SIGNALED;
00244                 break;
00245             }
00246             tqpp = &tqp->td_qnxt;
00247             tqp = tqp->td_qnxt;
00248         }
00249     }
00250 }
00251 
00271 int NutEventWait(volatile HANDLE * qhp, uint32_t ms)
00272 {
00273     NUTTHREADINFO *tdp;
00274 
00275     NUTASSERT(qhp != NULL);
00276 
00277     /* Get the queue's root atomically. */
00278     NutEnterCritical();
00279     tdp = *qhp;
00280     NutExitCritical();
00281 
00282     /*
00283      * Check for posts on a previously empty queue. 
00284      */
00285     if (tdp == SIGNALED) {
00286         /* Clear the singaled state. */
00287         NutEnterCritical();
00288         *qhp = 0;
00289         NutExitCritical();
00290         
00291         /*
00292          * Even if already signaled, switch to any other thread, which 
00293          * is ready to run and has the same or higher priority.
00294          */
00295         NutThreadYield();
00296         return 0;
00297     }
00298 
00299     /*
00300      * Remove the current thread from the list of running threads 
00301      * and add it to the specified queue.
00302      */
00303     NutThreadRemoveQueue(runningThread, &runQueue);
00304     NutThreadAddPriQueue(runningThread, (NUTTHREADINFO **) qhp);
00305 
00306     /* Update our thread's state (sleeping + timer) */
00307     runningThread->td_state = TDS_SLEEP;
00308     if (ms) {
00309         runningThread->td_timer = NutTimerStart(ms, NutEventTimeout, (void *) qhp, TM_ONESHOT);
00310     }
00311     else {
00312         runningThread->td_timer = 0;
00313     }
00314 
00315     /*
00316      * Switch to the next thread, which is ready to run.
00317      */
00318 #ifdef NUTTRACER
00319     TRACE_ADD_ITEM(TRACE_TAG_THREAD_WAIT,(int)runningThread);
00320 #endif
00321     NutThreadResume();
00322 
00323     /* If our timer handle is signaled, we were woken up by a timeout. */
00324     if (runningThread->td_timer == SIGNALED) {
00325         runningThread->td_timer = 0;
00326         return -1;
00327     }
00328     return 0;
00329 }
00330 
00350 int NutEventWaitNext(volatile HANDLE * qhp, uint32_t ms)
00351 {
00352     NUTASSERT(qhp != NULL);
00353 
00354     /*
00355      * Check for posts on a previously empty queue. 
00356      */
00357     NutEnterCritical();
00358     if (*qhp == SIGNALED)
00359         *qhp = 0;
00360     NutExitCritical();
00361 
00362     return NutEventWait(qhp, ms);
00363 }
00364 
00384 int NutEventPostAsync(volatile HANDLE * qhp)
00385 {
00386     NUTTHREADINFO *td;
00387 
00388     NUTASSERT(qhp != NULL);
00389 
00390     NutEnterCritical();
00391     td = *qhp;
00392     NutExitCritical();
00393 
00394     /* Ignore signaled queues. */
00395     if (td != SIGNALED) {
00396         
00397         /* A thread is waiting. */
00398         if (td) {
00399             /* Remove the thread from the wait queue. */
00400             NutEnterCritical();
00401             *qhp = td->td_qnxt;
00402             if (td->td_qpec) {
00403                 if (td->td_qnxt) {
00404                     td->td_qnxt->td_qpec = td->td_qpec;
00405                 }
00406                 else {
00407                     *qhp = SIGNALED;
00408                 }
00409                 td->td_qpec = 0;
00410             }
00411             NutExitCritical();
00412 
00413             /* Stop any running timeout timer. */
00414             if (td->td_timer) {
00415                 NutTimerStop(td->td_timer);
00416                 td->td_timer = 0;
00417             }
00418             /* The thread is ready to run. */
00419             td->td_state = TDS_READY;
00420             NutThreadAddPriQueue(td, (NUTTHREADINFO **) & runQueue);
00421 
00422             return 1;
00423         }
00424         
00425         /* No thread is waiting. Mark the queue signaled. */
00426         else {
00427             NutEnterCritical();
00428             *qhp = SIGNALED;
00429             NutExitCritical();
00430         }
00431     }
00432     return 0;
00433 }
00434 
00454 int NutEventPost(volatile HANDLE * qhp)
00455 {
00456     int rc;
00457 
00458     rc = NutEventPostAsync(qhp);
00459 
00460     /*
00461      * If any thread with higher or equal priority is
00462      * ready to run, switch the context.
00463      */
00464     NutThreadYield();
00465 
00466     return rc;
00467 }
00468 
00486 int NutEventBroadcastAsync(volatile HANDLE * qhp)
00487 {
00488     int rc = 0;
00489     NUTTHREADINFO *tdp;
00490 
00491     NUTASSERT(qhp != NULL);
00492 
00493     /* Get the queue's root atomically. */
00494     NutEnterCritical();
00495     tdp = *qhp;
00496     NutExitCritical();
00497 
00498     if (tdp == SIGNALED) {
00499         NutEnterCritical();
00500         *qhp = 0;
00501         NutExitCritical();
00502     }
00503     else if (tdp) {
00504         do {
00505             rc += NutEventPostAsync(qhp);
00506             /* Get the queue's updated root atomically. */
00507             NutEnterCritical();
00508             tdp = *qhp;
00509             NutExitCritical();
00510         } while (tdp && tdp != SIGNALED);
00511     }
00512     return rc;
00513 }
00514 
00533 int NutEventBroadcast(volatile HANDLE * qhp)
00534 {
00535     int rc = NutEventBroadcastAsync(qhp);
00536 
00537     NutThreadYield();
00538 
00539     return rc;
00540 }
00541