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" 
#include  
 
typedef 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; 
}