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

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