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

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