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 
00204 #include <cfg/os.h>
00205 #include <cfg/memory.h>
00206 
00207 #include <string.h>
00208 
00209 #include <sys/types.h>
00210 #include <sys/heap.h>
00211 #include <sys/atom.h>
00212 #include <sys/timer.h>
00213 #include <sys/event.h>
00214 #include <sys/thread.h>
00215 #include <sys/nutdebug.h>
00216 
00217 #ifdef NUTDEBUG
00218 #include <sys/osdebug.h>
00219 #endif
00220 
00221 #ifdef NUTTRACER
00222 #include <sys/tracer.h>
00223 #endif
00224 
00229 
00230 #if defined(NUT_CRITICAL_NESTING) && !defined(NUT_CRITICAL_NESTING_STACK)
00231 unsigned int critical_nesting_level;
00232 #endif
00233 
00234 #if defined(__linux__) || defined(__APPLE__) || defined(__CYGWIN__)
00235 // prototype
00236 extern void NutUnixThreadYieldHook(void);  // from unix_nutinit.c
00237 #endif
00238 
00245 NUTTHREADINFO * runningThread;
00246 
00253 NUTTHREADINFO * killedThread;
00254 
00263 NUTTHREADINFO * nutThreadList;
00264 
00272 NUTTHREADINFO * runQueue;
00273 
00274 
00275 
00286 void NutThreadAddPriQueue(NUTTHREADINFO * td, NUTTHREADINFO * volatile *tqpp)
00287 {
00288     NUTTHREADINFO *tqp;
00289 
00290     NUTASSERT(td != NULL);
00291 
00292     td->td_queue = (HANDLE) tqpp;
00293     td->td_qpec = 0;            // start with clean event count
00294 
00295     /*
00296      * Be most careful not to override an intermediate event from interrupt 
00297      * context, which may change a queue from empty to signaled state. Many 
00298      * thanks to Michael Jones, who detected and corrected this bug.
00299      */
00300     NutEnterCritical();
00301     tqp = *tqpp;
00302 
00303     if (tqp == SIGNALED) {
00304         tqp = 0;
00305         td->td_qpec++;          // transfer the signaled state 
00306     } else if (tqp) {
00307         NutExitCritical();      // there are other threads in queue
00308                         // so its save to leave critical.          
00309 
00310         while (tqp && tqp->td_priority <= td->td_priority) {
00311             tqpp = &tqp->td_qnxt;
00312             tqp = tqp->td_qnxt;
00313         }
00314 
00315         NutEnterCritical();     // back into critical
00316     }
00317 
00318     td->td_qnxt = tqp;
00319 
00320     *tqpp = td;
00321     if (td->td_qnxt && td->td_qnxt->td_qpec) {
00322         td->td_qpec += td->td_qnxt->td_qpec; // don't overwrite count
00323         td->td_qnxt->td_qpec = 0;
00324     }
00325     NutExitCritical();
00326 }
00327 
00338 void NutThreadRemoveQueue(NUTTHREADINFO * td, NUTTHREADINFO * volatile *tqpp)
00339 {
00340     NUTTHREADINFO *tqp;
00341 
00342     NutEnterCritical();
00343     tqp = *tqpp;
00344     NutExitCritical();
00345 
00346     if (tqp != SIGNALED) {
00347         while (tqp) {
00348             if (tqp == td) {
00349                 NutEnterCritical();
00350                 *tqpp = td->td_qnxt;
00351                 if (td->td_qpec) {
00352                     if (td->td_qnxt) {
00353                         td->td_qnxt->td_qpec = td->td_qpec;
00354                     }
00355                     td->td_qpec = 0;
00356                 }
00357                 NutExitCritical();
00358 
00359                 td->td_qnxt = 0;
00360                 td->td_queue = 0;
00361                 break;
00362             }
00363             tqpp = &tqp->td_qnxt;
00364             tqp = tqp->td_qnxt;
00365         }
00366     }
00367 }
00368 
00379 void NutThreadResume(void)
00380 {
00381     NUTTHREADINFO *td;
00382     NUTTHREADINFO **qhp;
00383     NUTTHREADINFO *tqp;
00384     unsigned int cnt;
00385 
00386     /*
00387      * Process events that have been posted from interrupt context.
00388      */
00389     td = nutThreadList;
00390     while (td) {
00391         NutEnterCritical();
00392         cnt = td->td_qpec;
00393         NutExitCritical();
00394         if (cnt) {
00395             /* In order to reduce context switching time, it is sufficient 
00396              * to remove the thread on top of the priority ordered list. */
00397             qhp = (NUTTHREADINFO **)(td->td_queue);
00398             NutEnterCritical();
00399             td->td_qpec--;
00400             tqp = *qhp;
00401             NutExitCritical();
00402             if (tqp != SIGNALED) {
00403                 NutEventPostAsync((HANDLE *)qhp);
00404             }
00405         }
00406         td = td->td_next;
00407     }
00408 
00409     /*
00410      * Process elapsed timers. Must be done after processing the
00411      * events from interupt routines.
00412      */
00413     NutTimerProcessElapsed();
00414 
00415     /* Check for context switch. */
00416     if (runningThread != runQueue) {
00417 #ifdef NUTTRACER
00418         TRACE_ADD_ITEM(TRACE_TAG_THREAD_YIELD,(int)runningThread);
00419 #endif
00420 
00421         if (runningThread->td_state == TDS_RUNNING) {
00422             runningThread->td_state = TDS_READY;
00423         }
00424         NutEnterCritical();
00425         NutThreadSwitch();
00426         NutExitCritical();
00427     }
00428 }
00429 
00447 void NutThreadWake(HANDLE timer, HANDLE th)
00448 {
00449     NUTASSERT(th != NULL);
00450 
00451     /* clear pointer on timer and waiting queue */
00452     ((NUTTHREADINFO *) th)->td_timer = 0;
00453     ((NUTTHREADINFO *) th)->td_state = TDS_READY;
00454     NutThreadAddPriQueue(th, (NUTTHREADINFO **) & runQueue);
00455 }
00456 
00465 void NutThreadYield(void)
00466 {
00467 
00468 #if defined(__linux__) || defined(__APPLE__) || defined(__CYGWIN__)
00469     NutEnterCritical();
00470     NutUnixThreadYieldHook();
00471     NutExitCritical();
00472 #endif
00473 
00474     /*
00475      * Remove current thread from runQueue and reinsert it.
00476      * The idle thread is the last one in the queue and will
00477      * never be removed.
00478      */
00479     if (runningThread->td_qnxt) {
00480         NutThreadRemoveQueue(runningThread, (NUTTHREADINFO **) & runQueue);
00481         NutThreadAddPriQueue(runningThread, (NUTTHREADINFO **) & runQueue);
00482     }
00483 
00484     /* Continue with the highest priority thread, which is ready to run. */
00485     NutThreadResume();
00486 }
00487 
00515 uint8_t NutThreadSetPriority(uint8_t level)
00516 {
00517     uint8_t last = runningThread->td_priority;
00518 
00519     /*
00520      * Remove the thread from the run queue and re-insert it with a new
00521      * priority, if this new priority level is below 255. A priotity of
00522      * 255 will kill the thread.
00523      */
00524     NutThreadRemoveQueue(runningThread, &runQueue);
00525     runningThread->td_priority = level;
00526     if (level < 255) {
00527         NutThreadAddPriQueue(runningThread, (NUTTHREADINFO **) & runQueue);
00528     } else {
00529         NutThreadKill();
00530     }
00531 
00532     /*
00533      * Are we still on top of the queue? If yes, then change our status
00534      * back to running, otherwise do a context switch.
00535      */
00536     if (runningThread == runQueue) {
00537         runningThread->td_state = TDS_RUNNING;
00538     } else {
00539         runningThread->td_state = TDS_READY;
00540 #ifdef NUTTRACER
00541         TRACE_ADD_ITEM(TRACE_TAG_THREAD_SETPRIO,(int)runningThread);
00542 #endif
00543 
00544         NutEnterCritical();
00545         NutThreadSwitch();
00546         NutExitCritical();
00547     }
00548 
00549     return last;
00550 }
00551 
00562 void NutThreadExit(void)
00563 {
00564     NutThreadSetPriority(255);
00565 }
00566 
00576 void NutThreadDestroy(void)
00577 {
00578     if (killedThread) {
00579         NutStackFree(killedThread->td_memory);
00580         killedThread = 0;
00581     }
00582 }
00583 
00591 void NutThreadKill(void)
00592 {
00593 
00594     NUTTHREADINFO *pCur = nutThreadList;
00595     NUTTHREADINFO **pp = (NUTTHREADINFO **) & nutThreadList;
00596 
00597     /* Free up any unfinished already killed threads. */
00598     NutThreadDestroy();
00599 
00600     /* Remove from the thread list. */
00601     while (pCur) {
00602         if (pCur == runningThread) {
00603             *pp = pCur->td_next;
00604             break;
00605         }
00606 
00607         pp = (NUTTHREADINFO **) & pCur->td_next;
00608         pCur = pCur->td_next;
00609     }
00610 
00611     /* Schedule for cleanup. */
00612     killedThread = runningThread;
00613 }
00614 
00624 HANDLE GetThreadByName(char * name)
00625 {
00626     NUTTHREADINFO *tdp;
00627 
00628     if (name) {
00629         for (tdp = nutThreadList; tdp; tdp = tdp->td_next) {
00630             if (strcmp(tdp->td_name, name) == 0)
00631                 return tdp;
00632         }
00633     } else {
00634         return runningThread;
00635     }
00636     return NULL;
00637 }
00638 
00639 #if defined(NUTDEBUG_CHECK_STACKMIN) || defined(NUTDEBUG_CHECK_STACK)
00640 /* Calculate the size if untouched stack space. */
00641 static size_t StackAvail(NUTTHREADINFO *td)
00642 {
00643     uint32_t *sp = (uint32_t *)td->td_memory;
00644 
00645     while(*sp++ == DEADBEEF);
00646 
00647     return (size_t)((uintptr_t)sp - (uintptr_t)td->td_memory);
00648 }
00649 
00671 size_t NutThreadStackAvailable(char *name)
00672 {
00673     NUTTHREADINFO *tdp = (NUTTHREADINFO *)GetThreadByName(name);
00674 
00675     return tdp ? StackAvail(tdp) : 0;
00676 }
00677 
00689 NUTTHREADINFO *NutThreadStackCheck(size_t minsiz)
00690 {
00691     NUTTHREADINFO *tdp;
00692 
00693     for (tdp = nutThreadList; tdp; tdp = tdp->td_next) {
00694         if (StackAvail(tdp) < minsiz) {
00695             break;
00696         }
00697     }
00698     return tdp;
00699 }
00700 #endif
00701 

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