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