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 
00181 #include <cfg/os.h>
00182 #include <cfg/memory.h>
00183 
00184 #include <string.h>
00185 
00186 #include <sys/types.h>
00187 #include <sys/heap.h>
00188 #include <sys/atom.h>
00189 #include <sys/timer.h>
00190 #include <sys/event.h>
00191 #include <sys/thread.h>
00192 
00193 #ifdef NUTDEBUG
00194 #include <sys/osdebug.h>
00195 #endif
00196 
00197 #ifdef NUTTRACER
00198 #include <sys/tracer.h>
00199 #endif
00200 
00205 
00206 #if defined(__linux__) || defined(__APPLE__) || defined(__CYGWIN__)
00207 // prototype
00208 extern void NutUnixThreadYieldHook(void);  // from unix_nutinit.c
00209 #endif
00210 
00217 NUTTHREADINFO * runningThread;
00218 
00225 NUTTHREADINFO * killedThread;
00226 
00235 NUTTHREADINFO * nutThreadList;
00236 
00244 NUTTHREADINFO * runQueue;
00245 
00246 
00247 
00258 void NutThreadAddPriQueue(NUTTHREADINFO * td, NUTTHREADINFO * volatile *tqpp)
00259 {
00260     NUTTHREADINFO *tqp;
00261 
00262     td->td_queue = (HANDLE) tqpp;
00263     td->td_qpec = 0;            // start with clean event count
00264 
00265     /*
00266      * Be most careful not to override an intermediate event from interrupt 
00267      * context, which may change a queue from empty to signaled state. Many 
00268      * thanks to Michael Jones, who detected and corrected this bug.
00269      */
00270     NutEnterCritical();
00271     tqp = *tqpp;
00272 
00273     if (tqp == SIGNALED) {
00274         tqp = 0;
00275         td->td_qpec++;          // transfer the signaled state 
00276     } else if (tqp) {
00277         NutExitCritical();      // there are other threads in queue
00278                         // so its save to leave critical.          
00279 
00280         while (tqp && tqp->td_priority <= td->td_priority) {
00281             tqpp = &tqp->td_qnxt;
00282             tqp = tqp->td_qnxt;
00283         }
00284 
00285         NutEnterCritical();     // back into critical
00286     }
00287 
00288     td->td_qnxt = tqp;
00289 
00290     *tqpp = td;
00291     if (td->td_qnxt && td->td_qnxt->td_qpec) {
00292         td->td_qpec += td->td_qnxt->td_qpec; // don't overwrite count
00293         td->td_qnxt->td_qpec = 0;
00294     }
00295     NutExitCritical();
00296 }
00297 
00308 void NutThreadRemoveQueue(NUTTHREADINFO * td, NUTTHREADINFO * volatile *tqpp)
00309 {
00310     NUTTHREADINFO *tqp;
00311 
00312     NutEnterCritical();
00313     tqp = *tqpp;
00314     NutExitCritical();
00315 
00316     if (tqp != SIGNALED) {
00317         while (tqp) {
00318             if (tqp == td) {
00319                 NutEnterCritical();
00320                 *tqpp = td->td_qnxt;
00321                 if (td->td_qpec) {
00322                     if (td->td_qnxt) {
00323                         td->td_qnxt->td_qpec = td->td_qpec;
00324                     }
00325                     td->td_qpec = 0;
00326                 }
00327                 NutExitCritical();
00328 
00329                 td->td_qnxt = 0;
00330                 td->td_queue = 0;
00331                 break;
00332             }
00333             tqpp = &tqp->td_qnxt;
00334             tqp = tqp->td_qnxt;
00335         }
00336     }
00337 }
00338 
00349 void NutThreadResume(void)
00350 {
00351     NUTTHREADINFO *td;
00352     NUTTHREADINFO **qhp;
00353     NUTTHREADINFO *tqp;
00354     u_int cnt;
00355 
00356     /*
00357      * Process events that have been posted from interrupt context.
00358      */
00359     td = nutThreadList;
00360     while (td) {
00361         NutEnterCritical();
00362         cnt = td->td_qpec;
00363         NutExitCritical();
00364         if (cnt) {
00365             /* In order to reduce context switching time, it is sufficient 
00366              * to remove the thread on top of the priority ordered list. */
00367             qhp = (NUTTHREADINFO **)(td->td_queue);
00368             NutEnterCritical();
00369             td->td_qpec--;
00370             tqp = *qhp;
00371             NutExitCritical();
00372             if (tqp != SIGNALED) {
00373                 NutEventPostAsync((HANDLE *)qhp);
00374             }
00375         }
00376         td = td->td_next;
00377     }
00378 
00379     /*
00380      * Process elapsed timers. Must be done after processing the
00381      * events from interupt routines.
00382      */
00383     NutTimerProcessElapsed();
00384 
00385     /* Check for context switch. */
00386     if (runningThread != runQueue) {
00387 #ifdef NUTTRACER
00388         TRACE_ADD_ITEM(TRACE_TAG_THREAD_YIELD,(int)runningThread);
00389 #endif
00390 
00391         if (runningThread->td_state == TDS_RUNNING) {
00392             runningThread->td_state = TDS_READY;
00393         }
00394         NutEnterCritical();
00395         NutThreadSwitch();
00396         NutExitCritical();
00397     }
00398 }
00399 
00417 void NutThreadWake(HANDLE timer, HANDLE th)
00418 {
00419     /* clear pointer on timer and waiting queue */
00420     ((NUTTHREADINFO *) th)->td_timer = 0;
00421     ((NUTTHREADINFO *) th)->td_state = TDS_READY;
00422     NutThreadAddPriQueue(th, (NUTTHREADINFO **) & runQueue);
00423 }
00424 
00433 void NutThreadYield(void)
00434 {
00435 
00436 #if defined(__linux__) || defined(__APPLE__) || defined(__CYGWIN__)
00437     NutEnterCritical();
00438     NutUnixThreadYieldHook();
00439     NutExitCritical();
00440 #endif
00441 
00442     /*
00443      * Remove current thread from runQueue and reinsert it.
00444      * The idle thread is the last one in the queue and will
00445      * never be removed.
00446      */
00447     if (runningThread->td_qnxt) {
00448         NutThreadRemoveQueue(runningThread, (NUTTHREADINFO **) & runQueue);
00449         NutThreadAddPriQueue(runningThread, (NUTTHREADINFO **) & runQueue);
00450     }
00451 
00452     /* Continue with the highest priority thread, which is ready to run. */
00453     NutThreadResume();
00454 }
00455 
00483 u_char NutThreadSetPriority(u_char level)
00484 {
00485     u_char last = runningThread->td_priority;
00486 
00487     /*
00488      * Remove the thread from the run queue and re-insert it with a new
00489      * priority, if this new priority level is below 255. A priotity of
00490      * 255 will kill the thread.
00491      */
00492     NutThreadRemoveQueue(runningThread, &runQueue);
00493     runningThread->td_priority = level;
00494     if (level < 255) {
00495         NutThreadAddPriQueue(runningThread, (NUTTHREADINFO **) & runQueue);
00496     } else {
00497         NutThreadKill();
00498     }
00499 
00500     /*
00501      * Are we still on top of the queue? If yes, then change our status
00502      * back to running, otherwise do a context switch.
00503      */
00504     if (runningThread == runQueue) {
00505         runningThread->td_state = TDS_RUNNING;
00506     } else {
00507         runningThread->td_state = TDS_READY;
00508 #ifdef NUTTRACER
00509         TRACE_ADD_ITEM(TRACE_TAG_THREAD_SETPRIO,(int)runningThread);
00510 #endif
00511 
00512         NutEnterCritical();
00513         NutThreadSwitch();
00514         NutExitCritical();
00515     }
00516 
00517     return last;
00518 }
00519 
00530 void NutThreadExit(void)
00531 {
00532     NutThreadSetPriority(255);
00533 }
00534 
00544 void NutThreadDestroy(void)
00545 {
00546     if (killedThread) {
00547         NutStackFree(killedThread->td_memory);
00548         killedThread = 0;
00549     }
00550 }
00551 
00559 void NutThreadKill(void)
00560 {
00561 
00562     NUTTHREADINFO *pCur = nutThreadList;
00563     NUTTHREADINFO **pp = (NUTTHREADINFO **) & nutThreadList;
00564 
00565     /* Free up any unfinished already killed threads. */
00566     NutThreadDestroy();
00567 
00568     /* Remove from the thread list. */
00569     while (pCur) {
00570         if (pCur == runningThread) {
00571             *pp = pCur->td_next;
00572             break;
00573         }
00574 
00575         pp = (NUTTHREADINFO **) & pCur->td_next;
00576         pCur = pCur->td_next;
00577     }
00578 
00579     /* Schedule for cleanup. */
00580     killedThread = runningThread;
00581 }
00582 
00592 HANDLE GetThreadByName(char * name)
00593 {
00594     NUTTHREADINFO *tdp;
00595 
00596     for (tdp = nutThreadList; tdp; tdp = tdp->td_next) {
00597         if (strcmp(tdp->td_name, name) == 0)
00598             return tdp;
00599     }
00600     return NULL;
00601 }
00602 

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