•Mac_Classes/TThread.cp

//  TThread.cp - Macintosh Thread class object
// 
// Apple Macintosh Developer Technical Support
// Written by:  Vinne Moscaritolo
//
//  Copyright (work in progress)  Apple Computer, Inc All rights reserved.
//
// You may incorporate this sample code into your applications without
// restriction, though the sample code has been provided "AS IS" and the
// responsibility for its operation is 100% yours.  However, what you are
// not permitted to do is to redistribute the source as "DSC Sample Code"
// after having made changes. If you're going to re-distribute the source,
// we require that you make it clear in the source that the code was
// descended from Apple Sample Code, but that you've made changes.
// 
 
 
#include "TMacException.h"
#include "TThread.h"
 
#include "TNetworkException.h"
#include <strstream.h>
 
// ===========================================================================
//  Static member variables
// ===========================================================================
 
Boolean             TThread::fgInited   = false;    // is Thread class library inited
ProcessSerialNumber TThread::fgPSN;                 // application's PSN
ThreadTaskRef       TThread::fgThreadTaskRef;       // thread task ref
 
// ---------------------------------------------------------------------------
//   DoEntry (pascal wrapper function)
// ---------------------------------------------------------------------------
//  Thread manager Callback for thread entry
 
pascal void* TThread::DoEntry(void *arg)
{
    TThread *theThread = (TThread*) arg;
    void* theResult;
        
//  try
    {
        theResult = theThread->Run();
    }
    
//  catch(...)      
    {
    // stop error prop
    } 
 
// Recycle thread
    theThread->Stop(theResult);
 
// cant get here... 
    ThrowMsg("TThread :DoEntry returned from dispose");
    return nil;
}
 
// ---------------------------------------------------------------------------
//   DoTermination (pascal wrapper function)
// ---------------------------------------------------------------------------
//  Thread manager Callback for thread termination
 
pascal void  TThread::DoTermination(ThreadID theID, void *arg)
{
    TThread *theThread = (TThread*) arg;
    
    theThread->fTID = kNoThreadID;
    
    try
    {
        theThread->Done();
    }
    catch(...)
    {       
        // all we want is to stop error propagation
    } 
}
 
// ---------------------------------------------------------------------------
//   DoSwapIn (pascal wrapper function)
// ---------------------------------------------------------------------------
//  Thread manager Callback for thread Swap in
 
pascal void TThread::DoSwapIn(ThreadID, void *arg)
{
    TThread* theThread = (TThread*) arg;
        
    theThread->fContext.Restore();  
}
 
 
// ---------------------------------------------------------------------------
//   DoSwapOut (pascal wrapper function)
// ---------------------------------------------------------------------------
//  Thread manager Callback for thread Swap Out
 
pascal void TThread::DoSwapOut(ThreadID, void *arg)
{
    TThread  *theThread = (TThread*) arg;
    
    theThread->fContext.Save(); 
}
 
// ---------------------------------------------------------------------------
//   TThread
// ---------------------------------------------------------------------------
//  Default Constructor
 
TThread::TThread()
{
// Check if first time to use thread package
    if (!fgInited) Initialize();
 
// Set state
    fTID =  kNoThreadID;
}
 
 
// ---------------------------------------------------------------------------
//   ~TThread
// ---------------------------------------------------------------------------
//  Destructor
 
TThread::~TThread()
{
// Kill thread if it's running
    Stop(0);
    fTID = kNoThreadID;
}
 
// ---------------------------------------------------------------------------
//   Start
// ---------------------------------------------------------------------------
//   Setup thread for execution
 
void TThread::Start()
{
    OSErr ErrNo;
    
    try {
 
// Create thread
        ThrowIfOSErr (::NewThread( kCooperativeThread,          // kCooperativeThread,kPreemptiveThread
                                    DoEntry,                    // thread entry proc
                                    this,                       // param to entry proc 
                                    0,                          // Stack space
                                    (kReadyThreadState          // starting state (ready)
                                    | kCreateIfNeeded),         // try to get from pool
                                    (void**) &fResult,          // place to put quit result
                                    &fTID));                    // place to put task ID
 
// set terminator and thread switch procs
        ThrowIfOSErr(::SetThreadSwitcher(fTID, DoSwapIn, this, true));
        ThrowIfOSErr(::SetThreadSwitcher(fTID, DoSwapOut, this, false));
        ThrowIfOSErr(::SetThreadTerminator(fTID, DoTermination, this));
    }
    catch(...){
        if (fTID != kNoThreadID) {
            ::SetThreadTerminator(fTID, nil, nil);
            ::DisposeThread(fTID, nil, true);
            fTID = kNoThreadID;
            }
        throw;
    }
 }
 
// ---------------------------------------------------------------------------
//  Stop
// ---------------------------------------------------------------------------
//   Stop thread execution
 
void TThread::Stop(void * theResult)
{
    
    if(fTID != kNoThreadID){
        ThreadID  ourThread = fTID;
        fTID = kNoThreadID;                             // this might be the last thing this thread does
        ::DisposeThread(ourThread,theResult, true);     // so juggle the Tid
        };
}
 
// ---------------------------------------------------------------------------
//  Sleep
// ---------------------------------------------------------------------------
//  Sleep Thread
 
void TThread::Sleep()
{
    if(fTID != kNoThreadID)
        ThrowIfOSErr(::SetThreadState(fTID,kStoppedThreadState,kCurrentThreadID));
}
 
 
// ---------------------------------------------------------------------------
//  WakeUp
// ---------------------------------------------------------------------------
//  Wake up Thread
 
void TThread::WakeUp()
{
    ThreadState threadState;
    
    if( (fTID != kNoThreadID) 
        && (::GetThreadStateGivenTaskRef(fgThreadTaskRef, fTID,&threadState) != noErr)
        && (threadState == kStoppedThreadState))
            ::SetThreadReadyGivenTaskRef(fgThreadTaskRef, fTID);
}
 
 
// ---------------------------------------------------------------------------
//  Yield
// ---------------------------------------------------------------------------
//  Yield  time to next thread
 
void TThread::Yield()
{
    ::YieldToAnyThread();
}
 
 
// ---------------------------------------------------------------------------
//  Run
// ---------------------------------------------------------------------------
//  default thread Run
 
void* TThread::Run()
{
     while(true) {
        Yield();
        }
     return nil;
}
 
// ---------------------------------------------------------------------------
//  Done
// ---------------------------------------------------------------------------
//  default thread Done
 
void TThread::Done()
{
}
 
 
 
// ---------------------------------------------------------------------------
//   Allocate (static public)
// ---------------------------------------------------------------------------
//  Allocate memory for Thread manager
 
void TThread::Allocate(short numToCreate, Size stackSize)
{
    ThrowIfOSErr(::CreateThreadPool(kCooperativeThread, numToCreate, stackSize));
}
 
 
// ---------------------------------------------------------------------------
//   Initialize 
// ---------------------------------------------------------------------------
//  Will  run when first thread is started
 
void TThread::Initialize()
{
// is the Thread Manager running ?
/*      if (!UEnvironment::HasFeature(env_HasThreadsManager))
        {
            ThrowOSErr_(threadProtocolErr);
        }
*/      
// low-level initialisation
    ThrowIfOSErr( ::GetCurrentProcess(&fgPSN));
    ThrowIfOSErr( ::GetThreadCurrentTaskRef(&fgThreadTaskRef));
 
    fgInited = true;
}