thread.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  * -
00033  * Portions Copyright (C) 2000 David J. Hudson <dave@humbug.demon.co.uk>
00034  *
00035  * This file is distributed in the hope that it will be useful, but WITHOUT
00036  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00037  * FITNESS FOR A PARTICULAR PURPOSE.
00038  *
00039  * You can redistribute this file and/or modify it under the terms of the GNU
00040  * General Public License (GPL) as published by the Free Software Foundation;
00041  * either version 2 of the License, or (at your discretion) any later version.
00042  * See the accompanying file "copying-gpl.txt" for more details.
00043  *
00044  * As a special exception to the GPL, permission is granted for additional
00045  * uses of the text contained in this file.  See the accompanying file
00046  * "copying-liquorice.txt" for details.
00047  */
00048 
00187 #include <cfg/os.h>
00188 #include <cfg/memory.h>
00189 
00190 #include <string.h>
00191 
00192 #include <sys/types.h>
00193 #include <sys/heap.h>
00194 #include <sys/atom.h>
00195 #include <sys/timer.h>
00196 #include <sys/event.h>
00197 #include <sys/thread.h>
00198 
00199 #ifdef NUTDEBUG
00200 #include <sys/osdebug.h>
00201 #endif
00202 
00203 #ifdef NUTTRACER
00204 #include <sys/tracer.h>
00205 #endif
00206 
00211 
00212 #if defined(NUT_CRITICAL_NESTING) && !defined(NUT_CRITICAL_NESTING_STACK)
00213 u_int critical_nesting_level;
00214 #endif
00215 
00216 #if defined(__linux__) || defined(__APPLE__) || defined(__CYGWIN__)
00217 // prototype
00218 extern void NutUnixThreadYieldHook(void);  // from unix_nutinit.c
00219 #endif
00220 
00227 NUTTHREADINFO * runningThread;
00228 
00235 NUTTHREADINFO * killedThread;
00236 
00245 NUTTHREADINFO * nutThreadList;
00246 
00254 NUTTHREADINFO * runQueue;
00255 
00256 
00257 
00268 void NutThreadAddPriQueue(NUTTHREADINFO * td, NUTTHREADINFO * volatile *tqpp)
00269 {
00270     NUTTHREADINFO *tqp;
00271 
00272     td->td_queue = (HANDLE) tqpp;
00273     td->td_qpec = 0;            // start with clean event count
00274 
00275     /*
00276      * Be most careful not to override an intermediate event from interrupt 
00277      * context, which may change a queue from empty to signaled state. Many 
00278      * thanks to Michael Jones, who detected and corrected this bug.
00279      */
00280     NutEnterCritical();
00281     tqp = *tqpp;
00282 
00283     if (tqp == SIGNALED) {
00284         tqp = 0;
00285         td->td_qpec++;          // transfer the signaled state 
00286     } else if (tqp) {
00287         NutExitCritical();      // there are other threads in queue
00288                         // so its save to leave critical.          
00289 
00290         while (tqp && tqp->td_priority <= td->td_priority) {
00291             tqpp = &tqp->td_qnxt;
00292             tqp = tqp->td_qnxt;
00293         }
00294 
00295         NutEnterCritical();     // back into critical
00296     }
00297 
00298     td->td_qnxt = tqp;
00299 
00300     *tqpp = td;
00301     if (td->td_qnxt && td->td_qnxt->td_qpec) {
00302         td->td_qpec += td->td_qnxt->td_qpec; // don't overwrite count
00303         td->td_qnxt->td_qpec = 0;
00304     }
00305     NutExitCritical();
00306 }
00307 
00318 void NutThreadRemoveQueue(NUTTHREADINFO * td, NUTTHREADINFO * volatile *tqpp)
00319 {
00320     NUTTHREADINFO *tqp;
00321 
00322     NutEnterCritical();
00323     tqp = *tqpp;
00324     NutExitCritical();
00325 
00326     if (tqp != SIGNALED) {
00327         while (tqp) {
00328             if (tqp == td) {
00329                 NutEnterCritical();
00330                 *tqpp = td->td_qnxt;
00331                 if (td->td_qpec) {
00332                     if (td->td_qnxt) {
00333                         td->td_qnxt->td_qpec = td->td_qpec;
00334                     }
00335                     td->td_qpec = 0;
00336                 }
00337                 NutExitCritical();
00338 
00339                 td->td_qnxt = 0;
00340                 td->td_queue = 0;
00341                 break;
00342             }
00343             tqpp = &tqp->td_qnxt;
00344             tqp = tqp->td_qnxt;
00345         }
00346     }
00347 }
00348 
00359 void NutThreadResume(void)
00360 {
00361     NUTTHREADINFO *td;
00362     NUTTHREADINFO **qhp;
00363     NUTTHREADINFO *tqp;
00364     u_int cnt;
00365 
00366     /*
00367      * Process events that have been posted from interrupt context.
00368      */
00369     td = nutThreadList;
00370     while (td) {
00371         NutEnterCritical();
00372         cnt = td->td_qpec;
00373         NutExitCritical();
00374         if (cnt) {
00375             /* In order to reduce context switching time, it is sufficient 
00376              * to remove the thread on top of the priority ordered list. */
00377             qhp = (NUTTHREADINFO **)(td->td_queue);
00378             NutEnterCritical();
00379             td->td_qpec--;
00380             tqp = *qhp;
00381             NutExitCritical();
00382             if (tqp != SIGNALED) {
00383                 NutEventPostAsync((HANDLE *)qhp);
00384             }
00385         }
00386         td = td->td_next;
00387     }
00388 
00389     /*
00390      * Process elapsed timers. Must be done after processing the
00391      * events from interupt routines.
00392      */
00393     NutTimerProcessElapsed();
00394 
00395     /* Check for context switch. */
00396     if (runningThread != runQueue) {
00397 #ifdef NUTTRACER
00398         TRACE_ADD_ITEM(TRACE_TAG_THREAD_YIELD,(int)runningThread);
00399 #endif
00400 
00401         if (runningThread->td_state == TDS_RUNNING) {
00402             runningThread->td_state = TDS_READY;
00403         }
00404         NutEnterCritical();
00405         NutThreadSwitch();
00406         NutExitCritical();
00407     }
00408 }
00409 
00427 void NutThreadWake(HANDLE timer, HANDLE th)
00428 {
00429     /* clear pointer on timer and waiting queue */
00430     ((NUTTHREADINFO *) th)->td_timer = 0;
00431     ((NUTTHREADINFO *) th)->td_state = TDS_READY;
00432     NutThreadAddPriQueue(th, (NUTTHREADINFO **) & runQueue);
00433 }
00434 
00443 void NutThreadYield(void)
00444 {
00445 
00446 #if defined(__linux__) || defined(__APPLE__) || defined(__CYGWIN__)
00447     NutEnterCritical();
00448     NutUnixThreadYieldHook();
00449     NutExitCritical();
00450 #endif
00451 
00452     /*
00453      * Remove current thread from runQueue and reinsert it.
00454      * The idle thread is the last one in the queue and will
00455      * never be removed.
00456      */
00457     if (runningThread->td_qnxt) {
00458         NutThreadRemoveQueue(runningThread, (NUTTHREADINFO **) & runQueue);
00459         NutThreadAddPriQueue(runningThread, (NUTTHREADINFO **) & runQueue);
00460     }
00461 
00462     /* Continue with the highest priority thread, which is ready to run. */
00463     NutThreadResume();
00464 }
00465 
00493 u_char NutThreadSetPriority(u_char level)
00494 {
00495     u_char last = runningThread->td_priority;
00496 
00497     /*
00498      * Remove the thread from the run queue and re-insert it with a new
00499      * priority, if this new priority level is below 255. A priotity of
00500      * 255 will kill the thread.
00501      */
00502     NutThreadRemoveQueue(runningThread, &runQueue);
00503     runningThread->td_priority = level;
00504     if (level < 255) {
00505         NutThreadAddPriQueue(runningThread, (NUTTHREADINFO **) & runQueue);
00506     } else {
00507         NutThreadKill();
00508     }
00509 
00510     /*
00511      * Are we still on top of the queue? If yes, then change our status
00512      * back to running, otherwise do a context switch.
00513      */
00514     if (runningThread == runQueue) {
00515         runningThread->td_state = TDS_RUNNING;
00516     } else {
00517         runningThread->td_state = TDS_READY;
00518 #ifdef NUTTRACER
00519         TRACE_ADD_ITEM(TRACE_TAG_THREAD_SETPRIO,(int)runningThread);
00520 #endif
00521 
00522         NutEnterCritical();
00523         NutThreadSwitch();
00524         NutExitCritical();
00525     }
00526 
00527     return last;
00528 }
00529 
00540 void NutThreadExit(void)
00541 {
00542     NutThreadSetPriority(255);
00543 }
00544 
00554 void NutThreadDestroy(void)
00555 {
00556     if (killedThread) {
00557         NutStackFree(killedThread->td_memory);
00558         killedThread = 0;
00559     }
00560 }
00561 
00569 void NutThreadKill(void)
00570 {
00571 
00572     NUTTHREADINFO *pCur = nutThreadList;
00573     NUTTHREADINFO **pp = (NUTTHREADINFO **) & nutThreadList;
00574 
00575     /* Free up any unfinished already killed threads. */
00576     NutThreadDestroy();
00577 
00578     /* Remove from the thread list. */
00579     while (pCur) {
00580         if (pCur == runningThread) {
00581             *pp = pCur->td_next;
00582             break;
00583         }
00584 
00585         pp = (NUTTHREADINFO **) & pCur->td_next;
00586         pCur = pCur->td_next;
00587     }
00588 
00589     /* Schedule for cleanup. */
00590     killedThread = runningThread;
00591 }
00592 
00602 HANDLE GetThreadByName(char * name)
00603 {
00604     NUTTHREADINFO *tdp;
00605 
00606     for (tdp = nutThreadList; tdp; tdp = tdp->td_next) {
00607         if (strcmp(tdp->td_name, name) == 0)
00608             return tdp;
00609     }
00610     return NULL;
00611 }
00612 

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