www.pudn.com > MessageQueue.zip > TASKMGR.C
/* This module defines a task/thread scheduling routine and some thread management routines. Copyright (C) 2004 Liu Ge This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "memmgr.h" #include "thread.h" #includetypedef enum { THREAD_NONE, THREAD_SUSPENDED, THREAD_RUNNING }Eu_ThreadStatus; #define STACK_SIZE_PER_THREAD 512 typedef struct { Eu_ThreadStatus eStatus; unsigned pContext; int arySlots[THREAD_SHARED_SLOT_SPACE_LIMIT]; }St_ThreadDescriptor; typedef unsigned THREAD_STACK[STACK_SIZE_PER_THREAD]; unsigned SchedulerStack[512]; static int iNumberOfCurrentTask = 0; static int nMaxThreadNum; static THREAD_STACK * ThreadStacks; // unsigned SubTaskStack[512]; static St_ThreadDescriptor * ThreadDescriptors; static int * ReadyQueue; static int nNumberOfReadyThreads; // defines a structure decribing the register context of 8086 typedef struct { unsigned bp; unsigned di; unsigned si; unsigned dx; unsigned cx; unsigned bx; unsigned ax; unsigned flag; unsigned ip; }St_RegisterContext; typedef struct { St_RegisterContext stRegisters; unsigned ret; PFN_ThreadProc ThreadProc; void * Reserved; }St_ThreadContext; static int s_iIdleThread = -1; static int iStoppingThreadId = -1; static BOOL isStoppingThreadRemoved; static BOOL StopThread( int iThreadIdentifier, BOOL isRemoved ); // When TaskSwitch is called, the system will save current registers and call __TaskSchedule // The __TaskSchedule function will return the stack pointer of the next task unsigned __TaskSchedule(unsigned sp) { int iThreadIdentifier; int iCurrentThreadId = ReadyQueue[iNumberOfCurrentTask]; // Saving the thread context of the current thread ThreadDescriptors[iCurrentThreadId].pContext = sp; // Check if there is a deferred operation, which is usually generated by // a thread that attempt to suspend or destroy itself if( iStoppingThreadId == -1 ) { // If not, switch to the next ready thread iNumberOfCurrentTask = (iNumberOfCurrentTask + 1) % nNumberOfReadyThreads; } else { // If so, suspend or destroy the requested thread .. // First, switch to the next ready thread for destroying/suspending the requested // thread on the context of the next ready thread iNumberOfCurrentTask = (iNumberOfCurrentTask + 1) % nNumberOfReadyThreads; // Then, suspend or destroy the requested thread on the context of the next ready thread StopThread( iCurrentThreadId, isStoppingThreadRemoved ); iStoppingThreadId = -1; // With the suspending or destruction operation, // the current item in the ready queue is removed, // and the next item becomes the current item now. if( iNumberOfCurrentTask > 0 ) { iNumberOfCurrentTask = (iNumberOfCurrentTask - 1) % nNumberOfReadyThreads; } else { iNumberOfCurrentTask = nNumberOfReadyThreads - 1; } } iThreadIdentifier = ReadyQueue[iNumberOfCurrentTask]; return ThreadDescriptors[iThreadIdentifier].pContext; } static void IdleLoop(void) { for(;;); } void ThreadDispatcher(PFN_ThreadProc pfnThreadProc) { pfnThreadProc(); Thread_Destroy( Thread_GetCurrentThreadId() ); } void SubTaskInit(int iThreadNum, PFN_ThreadProc pfnThreadProc) { St_ThreadContext * pContext = (St_ThreadContext *)( ThreadStacks[iThreadNum] + STACK_SIZE_PER_THREAD) - 1; // Guess why the pContext pointer does not point to // the begin of the ThreadStacks array ? pContext->stRegisters.bp = 0; pContext->stRegisters.di = 0; pContext->stRegisters.si = 0; pContext->stRegisters.dx = 0; pContext->stRegisters.cx = 0; pContext->stRegisters.bx = 0; pContext->stRegisters.ax = 0; pContext->stRegisters.flag = 0x202; // Make sure the interrupt flag is set when the // __TaskSchedule routine switchs to the sub task. pContext->stRegisters.ip = (unsigned)ThreadDispatcher; pContext->ThreadProc = pfnThreadProc; ThreadDescriptors[iThreadNum].pContext = (unsigned)pContext; memset( ThreadDescriptors[iThreadNum].arySlots, 0, sizeof(ThreadDescriptors[iThreadNum].arySlots) ); } int Thread_GetCurrentThreadId(void) { return ReadyQueue[iNumberOfCurrentTask]; } // This function creates a thread associated with the thread procedure // specified by the pfnThreadProc parameter. The isSuspended // parameter indicates if the created thread automatically runs // after the creation. // Return value: // if success, return the thread identifier of the created thread // if fails, return -1 int Thread_Create( PFN_ThreadProc pfnThreadProc, BOOL isSuspended ) { int i; __SystemLock(); // Search the thread descriptor table for an unused descriptor for( i = 0; i < nMaxThreadNum; i ++ ) { if( ThreadDescriptors[i].eStatus == THREAD_NONE ) { break; } } if( i >= nMaxThreadNum ) { __SystemUnlock(); return -1; } // Create a thread context in the ThreadStacks array, using the index // of the unused descriptor as the thread identifier SubTaskInit(i, pfnThreadProc); ThreadDescriptors[i].eStatus = THREAD_SUSPENDED; // Mark this thread as suspended. __SystemUnlock(); if( !isSuspended ) { Thread_Resume( i ); } return i; } // This function destroys the thread specified by // the iThreadIdentifier parameter // Return value: // if success, return TRUE. // if fails, return FALSE // Note!!!: // It need a trick to destroy the current thread. BOOL Thread_Destroy(int iThreadIdentifier) { __SystemLock(); if( iThreadIdentifier < nMaxThreadNum && ThreadDescriptors[iThreadIdentifier].eStatus != THREAD_NONE ) { int iCurrentThreadId = Thread_GetCurrentThreadId(); if( iThreadIdentifier == iCurrentThreadId ) { StopThread( iCurrentThreadId, TRUE ); // Note: control will not return here } else { // Free the specified thread identifier ThreadDescriptors[iThreadIdentifier].eStatus = THREAD_NONE; __SystemUnlock(); return TRUE; } } __SystemUnlock(); return FALSE; } // Note: the Thread_Destory function has a logic bug, please locate and fix it. static void AddReadyThread( int iThreadIdentifier ) { // Add this thread indentifier into the ready queue ReadyQueue[nNumberOfReadyThreads] = iThreadIdentifier; nNumberOfReadyThreads ++; // Mark this thread as running ThreadDescriptors[iThreadIdentifier].eStatus = THREAD_RUNNING; } // This function resumes a suspended thread, specified by the iThreadIdentifier parameter // Return value: // if success, return TRUE. // if fails, return FALSE BOOL Thread_Resume(int iThreadIdentifier) { __SystemLock(); if( iThreadIdentifier < nMaxThreadNum && ThreadDescriptors[iThreadIdentifier].eStatus == THREAD_SUSPENDED ) { int iCurrentThreadId = Thread_GetCurrentThreadId(); AddReadyThread( iThreadIdentifier ); if( iCurrentThreadId == s_iIdleThread ) { StopThread( s_iIdleThread, FALSE ); } __SystemUnlock(); return TRUE; } __SystemUnlock(); return FALSE; } static BOOL StopThread( int iThreadIdentifier, BOOL isRemoved ) { int i; int iCurrentThreadId; if( iThreadIdentifier >= nMaxThreadNum || ThreadDescriptors[iThreadIdentifier].eStatus != THREAD_RUNNING ) { return FALSE; } // Search the ready queue for the item associated with the specified thread for( i = 0; i < nNumberOfReadyThreads; i ++ ) { if( ReadyQueue[i] == iThreadIdentifier ) { break; } } if( i >= nNumberOfReadyThreads ) { return FALSE; } iCurrentThreadId = Thread_GetCurrentThreadId(); // Check if this call is for stopping the current thread if( iThreadIdentifier != iCurrentThreadId ) { // If not, stop (suspend or destroy) the specified thread int j; // Remove the item of the suspended thread from // the ready queue, and shrink the queue for( j = i + 1; j < nNumberOfReadyThreads; j ++ ) { ReadyQueue[j-1] = ReadyQueue[j]; } nNumberOfReadyThreads --; if( isRemoved ) { // Destroy the current thread // Free the specified thread identifier ThreadDescriptors[iThreadIdentifier].eStatus = THREAD_NONE; } else { // Suspend the current thread // Mark this thread as suspended ThreadDescriptors[iThreadIdentifier].eStatus = THREAD_SUSPENDED; } return TRUE; } if( nNumberOfReadyThreads == 1 && iCurrentThreadId != s_iIdleThread ) { AddReadyThread( s_iIdleThread ); } // If this call is for stopping the current thread, // record the operation for performing it deferredly. iStoppingThreadId = iCurrentThreadId; isStoppingThreadRemoved = isRemoved; __TaskSwitch(); // Transfer control to the next ready thread // Note: __TaskSwitch will internally call __SystemUnlock() // so the following statement is unreachable return TRUE; } // This function suspends a running thread, specified by the // iThreadIdentifier parameter // Return value: // if success, return TRUE. // if fails, return FALSE // Note!!!: it need a trick to suspend the current thread. BOOL Thread_Suspend(int iThreadIdentifier) { BOOL isDone; __SystemLock(); isDone = StopThread( iThreadIdentifier, FALSE ); __SystemUnlock(); return isDone; } // This function starts the thread system and transfer control // to the root thread, specified by the pfnRootThreadProc parameter. // The root thread is the first thread in the system. // The root thread must not terminate, otherwise the system will crash void Thread_Init(PFN_ThreadProc pfnRootThreadProc, int nMaxNumOfThreads) { int iStartupThread; nMaxNumOfThreads ++; nMaxThreadNum = nMaxNumOfThreads; ThreadStacks = malloc( nMaxNumOfThreads * sizeof(THREAD_STACK) ); ThreadDescriptors = malloc( nMaxNumOfThreads * sizeof(St_ThreadDescriptor) ); ReadyQueue = malloc( nMaxNumOfThreads * sizeof(int) ); iStartupThread = Thread_Create( 0, FALSE ); iNumberOfCurrentTask = iStartupThread; s_iIdleThread = Thread_Create( IdleLoop, TRUE ); Thread_Create( pfnRootThreadProc, FALSE ); __TaskSwitch(); Thread_Destroy( iStartupThread ); } static BOOL s_arySlotSpace[THREAD_SHARED_SLOT_SPACE_LIMIT]; // This function allocates a property slot in the thread-shared slot space. // About the thread-shared slot space, please see the corresponding // description in the header file. // Return value: // if success, return the index of the allocated slot. // if fails, return -1; int Thread_AllocSharedSlot(void) { int i; __SystemLock(); for(i = 0; i < THREAD_SHARED_SLOT_SPACE_LIMIT; i ++ ) { if( s_arySlotSpace[i] == FALSE ) { s_arySlotSpace[i] = TRUE; __SystemUnlock(); return i; } } __SystemUnlock(); return -1; } // This function frees a property slot in the thread-shared slot space. // Parameters: // iSlot The slot index specifying the slot to be freed // Return value: // if success, return TRUE // if fails, return FALSE; BOOL Thread_FreeSharedSlot(int iSlot) { __SystemLock(); if( iSlot < THREAD_SHARED_SLOT_SPACE_LIMIT && s_arySlotSpace[iSlot] == TRUE ) { s_arySlotSpace[iSlot] = FALSE; __SystemUnlock(); return TRUE; } __SystemUnlock(); return FALSE; } BOOL Thread_GetSlot(int iThreadIdentifier, int iSlotIndex, int * pSlot) { __SystemLock(); if( iThreadIdentifier < nMaxThreadNum && iSlotIndex < THREAD_SHARED_SLOT_SPACE_LIMIT && s_arySlotSpace[iSlotIndex] == TRUE ) { *pSlot = ThreadDescriptors[iThreadIdentifier].arySlots[iSlotIndex]; __SystemUnlock(); return TRUE; } __SystemUnlock(); return FALSE; } BOOL Thread_SetSlot(int iThreadIdentifier, int iSlotIndex, int nSlotValue) { __SystemLock(); if( iThreadIdentifier < nMaxThreadNum && iSlotIndex < THREAD_SHARED_SLOT_SPACE_LIMIT && s_arySlotSpace[iSlotIndex] == TRUE ) { ThreadDescriptors[iThreadIdentifier].arySlots[iSlotIndex] = nSlotValue; __SystemUnlock(); return TRUE; } __SystemUnlock(); return FALSE; }