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 
00155 #include <cfg/os.h>
00156 
00157 #include <compiler.h>
00158 #include <sys/atom.h>
00159 #include <sys/heap.h>
00160 #include <sys/timer.h>
00161 #include <sys/thread.h>
00162 #include <sys/event.h>
00163 
00164 #ifdef NUTDEBUG
00165 #include <sys/osdebug.h>
00166 #include <stdio.h>
00167 #endif
00168 
00169 #ifdef NUTTRACER
00170 #include <sys/tracer.h>
00171 #endif
00172 
00177 
00178 
00189 void NutEventTimeout(HANDLE timer, void *arg)
00190 {
00191     NUTTHREADINFO *tqp;
00192     NUTTHREADINFO *volatile *tqpp = arg;
00193 
00194     /* Get the queue's root atomically. */
00195     NutEnterCritical();
00196     tqp = *tqpp;
00197     NutExitCritical();
00198 
00199     /*
00200      * A signaled queue is an empty queue. So our
00201      * thread already left this queue.
00202      */
00203     if (tqp != SIGNALED) {
00204 
00205         /*
00206          * Walk down the linked list and identify
00207          * the thread by the timer id it is waiting
00208          * for.
00209          */
00210         while (tqp) {
00211             if (tqp->td_timer == timer) {
00212                 /* Found the thread. Remove it from the event queue. */
00213                    
00214                 NutEnterCritical();
00215                 *tqpp = tqp->td_qnxt;
00216                 if (tqp->td_qpec) {
00217                     if (tqp->td_qnxt) {
00218                         tqp->td_qnxt->td_qpec = tqp->td_qpec;
00219                     }
00220                     else {
00221                         *tqpp = SIGNALED;
00222                     }
00223                     tqp->td_qpec = 0;
00224                 }
00225                 NutExitCritical();
00226 
00227                 /* Add it to the queue of threads, which are ready to run. */
00228                 tqp->td_state = TDS_READY;
00229                 NutThreadAddPriQueue(tqp, (NUTTHREADINFO **) & runQueue);
00230 
00231                 /* Signal the timer entry in the thread's info structure.
00232                    This will tell the waiting thread, that it has been
00233                    woken up by a timeout. */
00234                 tqp->td_timer = SIGNALED;
00235                 break;
00236             }
00237             tqpp = &tqp->td_qnxt;
00238             tqp = tqp->td_qnxt;
00239         }
00240     }
00241 }
00242 
00262 int NutEventWait(volatile HANDLE * qhp, u_long ms)
00263 {
00264     NUTTHREADINFO *tdp;
00265     
00266     /* Get the queue's root atomically. */
00267     NutEnterCritical();
00268     tdp = *qhp;
00269     NutExitCritical();
00270 
00271     /*
00272      * Check for posts on a previously empty queue. 
00273      */
00274     if (tdp == SIGNALED) {
00275         /* Clear the singaled state. */
00276         NutEnterCritical();
00277         *qhp = 0;
00278         NutExitCritical();
00279         
00280         /*
00281          * Even if already signaled, switch to any other thread, which 
00282          * is ready to run and has the same or higher priority.
00283          */
00284         NutThreadYield();
00285         return 0;
00286     }
00287 
00288     /*
00289      * Remove the current thread from the list of running threads 
00290      * and add it to the specified queue.
00291      */
00292     NutThreadRemoveQueue(runningThread, &runQueue);
00293     NutThreadAddPriQueue(runningThread, (NUTTHREADINFO **) qhp);
00294 
00295     /* Update our thread's state (sleeping + timer) */
00296     runningThread->td_state = TDS_SLEEP;
00297     if (ms) {
00298         runningThread->td_timer = NutTimerStart(ms, NutEventTimeout, (void *) qhp, TM_ONESHOT);
00299     }
00300     else {
00301         runningThread->td_timer = 0;
00302     }
00303 
00304     /*
00305      * Switch to the next thread, which is ready to run.
00306      */
00307 #ifdef NUTTRACER
00308     TRACE_ADD_ITEM(TRACE_TAG_THREAD_WAIT,(int)runningThread);
00309 #endif
00310     NutThreadResume();
00311 
00312     /* If our timer handle is signaled, we were woken up by a timeout. */
00313     if (runningThread->td_timer == SIGNALED) {
00314         runningThread->td_timer = 0;
00315         return -1;
00316     }
00317     return 0;
00318 }
00319 
00339 int NutEventWaitNext(volatile HANDLE * qhp, u_long ms)
00340 {
00341     /*
00342      * Check for posts on a previously empty queue. 
00343      */
00344     NutEnterCritical();
00345     if (*qhp == SIGNALED)
00346         *qhp = 0;
00347     NutExitCritical();
00348 
00349     return NutEventWait(qhp, ms);
00350 }
00351 
00371 int NutEventPostAsync(volatile HANDLE * qhp)
00372 {
00373     NUTTHREADINFO *td;
00374     
00375     NutEnterCritical();
00376     td = *qhp;
00377     NutExitCritical();
00378 
00379     /* Ignore signaled queues. */
00380     if (td != SIGNALED) {
00381         
00382         /* A thread is waiting. */
00383         if (td) {
00384             /* Remove the thread from the wait queue. */
00385             NutEnterCritical();
00386             *qhp = td->td_qnxt;
00387             if (td->td_qpec) {
00388                 if (td->td_qnxt) {
00389                     td->td_qnxt->td_qpec = td->td_qpec;
00390                 }
00391                 else {
00392                     *qhp = SIGNALED;
00393                 }
00394                 td->td_qpec = 0;
00395             }
00396             NutExitCritical();
00397 
00398             /* Stop any running timeout timer. */
00399             if (td->td_timer) {
00400                 NutTimerStop(td->td_timer);
00401                 td->td_timer = 0;
00402             }
00403             /* The thread is ready to run. */
00404             td->td_state = TDS_READY;
00405             NutThreadAddPriQueue(td, (NUTTHREADINFO **) & runQueue);
00406 
00407             return 1;
00408         }
00409         
00410         /* No thread is waiting. Mark the queue signaled. */
00411         else {
00412             NutEnterCritical();
00413             *qhp = SIGNALED;
00414             NutExitCritical();
00415         }
00416     }
00417     return 0;
00418 }
00419 
00439 int NutEventPost(volatile HANDLE * qhp)
00440 {
00441     int rc;
00442 
00443     rc = NutEventPostAsync(qhp);
00444 
00445     /*
00446      * If any thread with higher or equal priority is
00447      * ready to run, switch the context.
00448      */
00449     NutThreadYield();
00450 
00451     return rc;
00452 }
00453 
00471 int NutEventBroadcastAsync(volatile HANDLE * qhp)
00472 {
00473     int rc = 0;
00474     NUTTHREADINFO *tdp;
00475 
00476     /* Get the queue's root atomically. */
00477     NutEnterCritical();
00478     tdp = *qhp;
00479     NutExitCritical();
00480 
00481     if (tdp == SIGNALED) {
00482         NutEnterCritical();
00483         *qhp = 0;
00484         NutExitCritical();
00485     }
00486     else if (tdp) {
00487         do {
00488             rc += NutEventPostAsync(qhp);
00489             /* Get the queue's updated root atomically. */
00490             NutEnterCritical();
00491             tdp = *qhp;
00492             NutExitCritical();
00493         } while (tdp && tdp != SIGNALED);
00494     }
00495     return rc;
00496 }
00497 
00516 int NutEventBroadcast(volatile HANDLE * qhp)
00517 {
00518     int rc = NutEventBroadcastAsync(qhp);
00519 
00520     NutThreadYield();
00521 
00522     return rc;
00523 }
00524 

© 2000-2007 by egnite Software GmbH - visit http://www.ethernut.de/