home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
deans.zip
/
PROCESS.CPP
< prev
next >
Wrap
C/C++ Source or Header
|
1994-09-14
|
62KB
|
1,996 lines
//
// NAME: CIDLib_Process.Cpp
//
// DESCRIPTION:
//
// This module implements the THREAD class, which provides process control
// for all CIDLib based applications.
//
//
// AUTHOR: Dean Roddey
//
// CREATE DATE: 04/16/93
//
// COPYRIGHT: 1992..1994, 'CIDCorp
//
// CAVEATS/GOTCHAS:
//
// 1) The bInitProcess() method that is called during facility init, has to
// bypass the normal THREAD constructor and so calls the default
// constructor and fills in the thread object (which stands for the main
// thread). So be sure to check it if any new THREAD data members are
// added.
//
// 2) The local thread list and per-thread object manipulation functions
// assume that the methods calling them have locked the access semaphore.
//
// 3) When a message is logged by any code on a thread (with a severity of
// APIFAILED or greater), it is stored in the thead object as the
// thread's last error. This can be queried later by the client code
// to see what happened. If it is fatal, then an exception is thrown.
//
// MODIFICATION LOG:
//
// -----------------------------------------------------------------------------
// Local defines
// -----------------------------------------------------------------------------
#define CIDLIB_PROCESS_CPP
#define INCL_DOSEXCEPTIONS
#define INCL_DOSSEMAPHORES
#define INCL_CIDLIB_ERROBJS
#define INCL_CIDLIB_ERRORIDS
#define INCL_CIDLIB_FACOBJECT
#define INCL_CIDLIB_METRICS
#define INCL_CIDLIB_PROCESS
// -----------------------------------------------------------------------------
// C runtime support includes
// -----------------------------------------------------------------------------
#include <process.h>
#include <setjmp.h>
#include <string.h>
// -----------------------------------------------------------------------------
// Facility specific includes
//
// We bring a special header CIDLib_Exception_.Hpp here, which is shared
// only by this module and CIDLib_Exception.Cpp. It provides us a call for
// a function to log exception information.
// -----------------------------------------------------------------------------
#include "CIDLib_.Hpp"
#include "CIDLib_Exception_.Hpp"
// -----------------------------------------------------------------------------
// Static, constant members of tCIDLib that we are responsible for
// -----------------------------------------------------------------------------
const tCIDLib::CARD4 tCIDLib::c4MaxPrioLev = 31;
// -----------------------------------------------------------------------------
// Constant data for local use
//
// __c4MaxThreads
// The maximum number of threads supported per-process.
// -----------------------------------------------------------------------------
const tCIDLib::CARD4 __c4MaxThreads = 512;
// -----------------------------------------------------------------------------
// Local data types
//
// PERTHREADINFO
// This is used to allow per-thread objects to be stored. Other subsystems
// can register the need to have per-thread objects. When each thread
// is created, an array of per-thread object pointers are created for the
// thread. An array of these structures is used to store the master list
// of objects to create. It stores the name of the object (for logging
// error messages and insuring non-duplication) and a call out function
// that the subsystem provides to create the objects.
//
// STARTDATA
// This structure is used to pass startup data to the __ThreadStart()
// function. This function is used to start all threads. It does a
// regular call to the actual thread function.
// -----------------------------------------------------------------------------
struct PERTHREADINFO
{
tCIDLib::ZSTR64 szObjName;
tCIDLib::pfnPTHRFUNC pfnPerThread;
};
struct STARTDATA
{
THREAD* pThread;
tCIDLib::VOID* pData;
};
// -----------------------------------------------------------------------------
// Variables for local use
//
// __aPTObjs
// The list of registered per-thread objects.
//
// __apThreads
// A list of the thread objects that currently exist. We can use the
// _bRunning member to know whether the thread is currently running or
// not.
//
// __bInited
// This is set when the _bInitProcess method has been called. So, if an
// error occurs during startup, we won't flip out by trying to access
// bad data.
//
// __c4PTObjCount
// The count of items in the __aPTObjs list, which holds entries for the
// registered per-thread objects.
//
// __c4ThreadCount
// The count of thread objects currently defined. They are not all
// necessarily running.
//
// __hmtxListAccess
// A semaphore to coordinate access to the __apThreads and __aPTObjs
// arrays, to insure that multiple threads don't step on each other's
// toes.
//
// __pthrMain
// This is a dummy thread object manually set up in the _bInitProcess()
// function for the startup thread.
// -----------------------------------------------------------------------------
PERTHREADINFO __aPTObjs[c4MaxPTObjs];
THREAD* __apThreads[__c4MaxThreads];
tCIDLib::eBOOL __bInited;
tCIDLib::CARD4 __c4PTObjCount;
tCIDLib::CARD4 __c4ThreadCount;
HMTX __hmtxListAccess;
THREAD* __pthrMain;
// -----------------------------------------------------------------------------
// CLASS: LSTLOCK
// PREFIX: lck
//
// Define a local tiny class that will make it safer to lock/unlock the
// thread list access semaphore (__hmtxListAccess above).
// -----------------------------------------------------------------------------
class LSTLOCK
{
public :
// --------------------------------------------------------------------
// Constructors and destructors
// --------------------------------------------------------------------
LSTLOCK(tCIDLib::CH* pszFunc)
{
tCIDLib::CARD4 c4Err;
// Get access to the thread list
c4Err = DosRequestMutexSem( __hmtxListAccess
, SEM_INDEFINITE_WAIT);
if (c4Err)
{
facCIDLib.LogSysErr(__FILE__
, pszFunc
, __LINE__
, c4Err
, "Could not lock thread list access semaphore"
, tCIDLib::eSEV_PROCESS_FATAL);
}
strcpy(__szFunc, pszFunc);
}
~LSTLOCK()
{
tCIDLib::CARD4 c4Err;
// Get access to the thread list
c4Err = DosReleaseMutexSem(__hmtxListAccess);
if (c4Err)
{
facCIDLib.LogSysErr(__FILE__
, __szFunc
, __LINE__
, c4Err
, "Could not release thread list access semaphore"
, tCIDLib::eSEV_PROCESS_FATAL);
}
}
private :
// --------------------------------------------------------------------
// Private data members
//
// __szFunc
// The name of the function with the lock
// --------------------------------------------------------------------
tCIDLib::CH __szFunc[128];
};
// -----------------------------------------------------------------------------
// Functions for local use only
// -----------------------------------------------------------------------------
//
// FUNCTION/METHOD NAME: __AddThread(pThead)
//
// DESCRIPTION:
//
// This method is called when a new thread is to be added to the list. It
// will abort if the thread count exceeds the maximum.
// ---------------------------------------
// INPUT: pThread is the new thread object to add.
//
// OUTPUT: None
//
// RETURN: None
//
static tCIDLib::VOID __AddThread(THREAD* pThread)
{
if (__c4ThreadCount == __c4MaxThreads)
{
facCIDLib.LogErr( __FILE__
, "__AddThread"
, __LINE__
, LIBERR_PRC_TOO_MANY_THREADS
, tCIDLib::eSEV_PROCESS_FATAL
, tCIDLib::eCLASS_OUTRESOURCE
, CARDINAL(__c4MaxThreads));
}
//
// Scan for an empty entry in the list. If we don't find one, then
// there is a major screw up because the thread count is wrong. Also,
// count how many are taken while we look. If it exceeds
// __c4ThreadCount, then that is an error too.
//
tCIDLib::CARD4 c4Ind, c4Count = 0;
for (c4Ind = 0; c4Ind < __c4MaxThreads; c4Ind++)
{
if (!__apThreads[c4Ind])
break;
c4Count++;
}
if ((c4Count > __c4ThreadCount)
|| (c4Ind == __c4MaxThreads))
{
facCIDLib.LogErr( __FILE__
, "__AddThread"
, __LINE__
, LIBERR_PRC_THREAD_COUNT
, tCIDLib::eSEV_PROCESS_FATAL
, tCIDLib::eCLASS_INTERNAL
, CARDINAL(__c4ThreadCount)
, CARDINAL(c4Count));
}
// Store the thread and bump up the count
__apThreads[c4Ind] = pThread;
__c4ThreadCount++;
}
//
// FUNCTION/METHOD NAME: __bRemoveId(tidThread)
//
// DESCRIPTION:
//
// This method will find the thread in the list with the passed id and
// then remove the entry, setting it to 0 and bumping down the thread
// count.
// ---------------------------------------
// INPUT: tidThread is the thread id to look for
//
// OUTPUT: None
//
// RETURN: A pointer to the thread or 0 if not found
//
static tCIDLib::eBOOL __bRemoveId(TID tidThread)
{
if (!__c4ThreadCount)
{
facCIDLib.LogErr( __FILE__
, "__bRemoveId"
, __LINE__
, LIBERR_PRC_THREAD_COUNT_0
, tCIDLib::eSEV_PROCESS_FATAL
, tCIDLib::eCLASS_INTERNAL);
}
for (tCIDLib::CARD4 c4Ind = 0; c4Ind < __c4MaxThreads; c4Ind++)
{
if (__apThreads[c4Ind])
{
if (__apThreads[c4Ind]->tidThread() == tidThread)
{
__c4ThreadCount--;
__apThreads[c4Ind] = (THREAD*)0;
return tCIDLib::eTRUE;
}
}
}
return tCIDLib::eFALSE;
}
//
// FUNCTION/METHOD NAME: __c4AddPTObj(strObjName, pfnPerThread)
//
// DESCRIPTION:
//
// This method will add a per-thread object to the master list. It is assumed
// that the caller has locked the thread access semaphore, so we don't worry
// about manipulating locally global data.
// ---------------------------------------
// INPUT: strObjName is a descriptive name for the object.
// pfnPerThread is the callout function that will be called to create
// the object.
//
// OUTPUT: None
//
// RETURN: The index at which the new item was added.
//
static tCIDLib::CARD4
__c4AddPTObj( const STRG64& strObjName
, tCIDLib::pfnPTHRFUNC pfnPerThread)
{
if (__c4PTObjCount >= c4MaxPTObjs)
{
facCIDLib.LogMsg( __FILE__
, "__c4AddPTObj"
, __LINE__
, "Too many per thread objects registered"
, tCIDLib::eSEV_PROCESS_FATAL);
}
// Look for an object with this name already in the list
for (tCIDLib::CARD4 c4Ind = 0; c4Ind < __c4PTObjCount; c4Ind++)
{
if (!stricmp(__aPTObjs[c4Ind].szObjName, strObjName.pSZ()))
{
facCIDLib.LogMsg( __FILE__
, "__c4AddPTObj"
, __LINE__
, "Per-thread object %(1) already exists"
, tCIDLib::eSEV_PROCESS_FATAL
, tCIDLib::eCLASS_ALREADY
, strObjName);
}
}
// Bump up the index
__c4PTObjCount++;
// Add the new guy
strcpy(__aPTObjs[__c4PTObjCount].szObjName, strObjName.pSZ());
__aPTObjs[__c4PTObjCount].pfnPerThread = pfnPerThread;
// Return the new index
return __c4PTObjCount;
}
//
// FUNCTION/METHOD NAME: __pthrFindId(tidThread)
//
// DESCRIPTION:
//
// This method will find the thread in the list with the passed id.
// ---------------------------------------
// INPUT: tidThread is the thread id to look for
//
// OUTPUT: None
//
// RETURN: A pointer to the thread or 0 if not found
//
static THREAD* __pthrFindId(TID tidThread)
{
for (tCIDLib::CARD4 c4Ind = 0; c4Ind < __c4MaxThreads; c4Ind++)
{
if (__apThreads[c4Ind])
{
if (__apThreads[c4Ind]->tidThread() == tidThread)
return __apThreads[c4Ind];
}
}
return (THREAD*)0;
}
//
// FUNCTION/METHOD NAME: __pthrFindName(strName)
//
// DESCRIPTION:
//
// This method will find the named thread in the list.
// ---------------------------------------
// INPUT: strName is the name of the thread to look for
//
// OUTPUT: None
//
// RETURN: A pointer to the thread or 0 if not found
//
static THREAD* __pthrFindName(const STRGBUF& strName)
{
for (tCIDLib::CARD4 c4Ind = 0; c4Ind < __c4MaxThreads; c4Ind++)
{
if (__apThreads[c4Ind])
{
if (__apThreads[c4Ind]->strName() == strName)
return __apThreads[c4Ind];
}
}
return 0;
}
//
// FUNCTION/METHOD NAME: __ThreadException(pRep, pReg, pContext, pDummy)
//
// DESCRIPTION:
//
// This is an exception handler that is installed for every thread. It
// watches for exceptions that indicate the thread is dying. If so, it
// clears the __bRunning flag in the thread's thread object. It is installed
// on every thread started below in __ThreadStart.
//
// This guy is a friend of THREAD so it can call the protected method
// required to clear the thread info.
// ---------------------------------------
// INPUT: pRep is the exception report record
// pReg is the exception registration record
// pContext contains context information for this exception
// pDummy is a pointer to magic info which we ignore.
//
// OUTPUT: None
//
// RETURN: We return the correct code according to whether we handle the
// exception or ignore it.
//
APIRET APIENTRY
__ThreadException( struct _EXCEPTIONREPORTRECORD* pRep
, struct _EXCEPTIONREGISTRATIONRECORD* pReg
, struct _CONTEXT* pContext
, tCIDLib::VOID* pDummy)
{
// Make the compiler happy
pDummy; pContext;
// See if we are exiting
if ((pRep->fHandlerFlags & EH_EXIT_UNWIND)
|| (pRep->fHandlerFlags & EH_UNWINDING)
|| (pRep->fHandlerFlags & EH_NESTED_CALL))
{
return XCPT_CONTINUE_SEARCH;
}
//
// If a terminate then clean up the thread object by calling _Exiting. If
// a fatal exception, then log to file. Don't do guard page exceptions
// because they are expected and will be handled for us.
//
if ((pRep->ExceptionNum == XCPT_PROCESS_TERMINATE)
|| (pRep->ExceptionNum == XCPT_ASYNC_PROCESS_TERMINATE))
{
// Call the internal exiting method to clean up thread flags
((CIDTHREADEXP*)pReg)->pthrThis->_Exiting();
}
else if (((pRep->ExceptionNum & 0xC0000000) == 0xC0000000)
&& (pRep->ExceptionNum != XCPT_GUARD_PAGE_VIOLATION)
&& (pRep->ExceptionNum != XCPT_FLOAT_UNDERFLOW)
&& (pRep->ExceptionNum != XCPT_FLOAT_OVERFLOW))
{
// Log the exception info
_DumpExceptInfo(pRep, pReg, pContext);
}
return XCPT_CONTINUE_SEARCH;
}
//
// FUNCTION/METHOD NAME: __ThreadStart(pStart)
//
// DESCRIPTION:
//
// This method will call the thread's function and pass it a pointer to the
// thread object and to the user passed data buffer. When the thread function
// returns, control will come back here and we will turn off the _bRunning
// flag.
//
// On entry, we block on the thread's sync semaphore until the thread that
// started us is ready. This is done in the bStart() method, below.
// ---------------------------------------
// INPUT: pStart is a pointer to a STARTDATA data structure.
//
// OUTPUT: None
//
// RETURN: None
//
tCIDLib::VOID THREADFUNC __ThreadStart(tCIDLib::VOID* pData)
{
tCIDLib::CARD4 c4Err;
CIDTHREADEXP ThreadExp;
THREAD* pThread;
tCIDLib::VOID* pUserData;
// Look at the passed pointer as a start data structure
STARTDATA* pStart = (STARTDATA*)(pData);
//
// Get the thread pointer and data pointer out of the passed data buffer
// then delete it. It had to be allocated because of the async nature
// of the thread startup.
//
pThread = pStart->pThread;
pUserData = pStart->pData;
// Delete the start data buffer
delete pStart;
//
// Make sure that main() has completed, which also means that all of the
// global data is created
//
_ThreadSyncStart();
// Wait on the sync semaphore
DosWaitEventSem(pThread->__hevSync, SEM_INDEFINITE_WAIT);
// Register the standard thread exception
ThreadExp.pthrThis = pThread;
ThreadExp.pfnHandler = (PFN)__ThreadException;
c4Err = DosSetExceptionHandler((PEXCEPTIONREGISTRATIONRECORD)&ThreadExp);
if (c4Err)
{
facCIDLib.LogSysErr(__FILE__
, "__ThreadStart"
, __LINE__
, c4Err
, "Could not register standard exception handler"
, tCIDLib::eSEV_PROCESS_FATAL);
}
// Call the init method
pThread->Init();
// Call the thread function
tCIDLib::eBOOL bFatalExit = tCIDLib::eFALSE;
try
{
pThread->__pfnFunc(*pThread, pUserData);
}
catch (CIDERROR errRT)
{
//
// DO NOT log a fatal error here or we trigger another throw
//
facCIDLib.LogMsg( __FILE__
, "__ThreadStart"
, __LINE__
, "Unhandled runtime error. See ErrorInf.CIDLib"
, tCIDLib::eSEV_INFORMATION);
bFatalExit = tCIDLib::eTRUE;
}
// Remove the exception handler
c4Err = DosUnsetExceptionHandler((PEXCEPTIONREGISTRATIONRECORD)&ThreadExp);
if (c4Err)
{
// !!! Don't log a fatal error here because it would throw again
facCIDLib.LogSysErr(__FILE__
, "__ThreadStart"
, __LINE__
, c4Err
, "Could not deregister standard exception handler"
, tCIDLib::eSEV_WARNING);
bFatalExit = tCIDLib::eTRUE;
}
//
// If the __bRunning flag is still set, then we need to call _Exiting()
// to clean up. Otherwise, the exception handler has already done it
// for us.
//
if (pThread->__bRunning)
pThread->_Exiting();
//
// If this is not thread 0 exiting and bFatalExit is eTRUE, then shut
// down the program.
//
if (pThread->__tidThread && bFatalExit)
facCIDLib.ExitProcess(tCIDLib::eEXIT_FATAL);
}
// -----------------------------------------------------------------------------
// Functions for intrafacility use
// -----------------------------------------------------------------------------
//
// FUNCTION/METHOD NAME: _bInitProcess()
//
// DESCRIPTION:
//
// This is the init function for this module. It is called from the Facility's
// main module when the Facility is loaded. This guy needs to create some local
// data, mainly the synchronization semphore. It also adds an entry in the
// thread list for this start up thread.
// ---------------------------------------
// INPUT: None
//
// OUTPUT: None
//
// RETURN: eTRUE if successful, else eFALSE.
//
tCIDLib::eBOOL _bInitProcess()
{
tCIDLib::CARD4 c4Err;
// Create the linked list access semaphore
c4Err = DosCreateMutexSem(0, &__hmtxListAccess, 0, 0);
if (c4Err)
{
// Do a text popup and return eFALSE
_ERRPOPUP_( "_bInitProcess"
, "Could not create list access semaphore"
, c4Err);
return tCIDLib::eFALSE;
}
//
// We need to create a dummy node for the main thread, which is the one
// that we are running under now. This function is a friend of THREAD, so
// it can call the default constructor and then fill it in.
//
__pthrMain = new THREAD();
PTIB pTBlk;
PPIB pPBlk;
DosGetInfoBlocks(&pTBlk, &pPBlk);
__pthrMain->__strName = "_Thread1_";
__pthrMain->__tidThread = pTBlk->tib_ptib2->tib2_ultid;
__pthrMain->__bRunning = tCIDLib::eTRUE;
// Add the new thread to the list
__AddThread(__pthrMain);
// Set the local inited flag
__bInited = tCIDLib::eTRUE;
return tCIDLib::eTRUE;
}
//
// FUNCTION/METHOD NAME: _pthrCurrent()
//
// DESCRIPTION:
//
// This function will get the thread id of the current thread, lock the
// thread list, look up the thread, and return a pointer to the thread
// object.
//
// NOTE: If the thread is not found, then this is a major error, so we log
// an error and abort. Note also that we cannot log from this call
// because we will get into a big circle jerk with the logging
// mechanism and overflow the stack.
// ---------------------------------------
// INPUT: None
//
// OUTPUT: None
//
// RETURN: A pointer to the thread object of the calling thread.
//
THREAD* _pthrCurrent()
{
tCIDLib::CARD4 c4Err;
PTIB pTBlk;
PPIB pPBlk;
THREAD* pThread;
TID tidTmp;
if (!__bInited)
{
//
// Return a bogus thread object, since we know that this is happening
// due to a start up error and he just wants a thread name. He is
// going to abort anyway so this memory will not get lost.
//
return new THREAD("_Thread1_", 0);
}
// Get access to the thread list
c4Err = DosRequestMutexSem(__hmtxListAccess, 2000);
if (c4Err)
{
_ERRPOPUP_( "_pthrCurrent"
, "An error occured while locking thread list access sem"
, c4Err);
facCIDLib.ExitProcess(tCIDLib::eEXIT_FATAL);
}
//
// Query the info blocks and store away some information from them.
//
c4Err = DosGetInfoBlocks(&pTBlk, &pPBlk);
if (c4Err)
{
_ERRPOPUP_( "_pthrCurrent"
, "An error occured while getting thread information"
, c4Err);
facCIDLib.ExitProcess(tCIDLib::eEXIT_FATAL);
}
tidTmp = pTBlk->tib_ptib2->tib2_ultid;
// Look for this thread id
pThread = __pthrFindId(tidTmp);
// Release the list access semaphore
DosReleaseMutexSem(__hmtxListAccess);
//
// If we did get a nul node back, then a thread with that id does not
// exist. This is a major error, so we need to abort.
//
if (!pThread)
{
_ERRPOPUP_( "_pthrCurrent"
, "The calling thread was not found in the thread list"
, 0);
facCIDLib.ExitProcess(tCIDLib::eEXIT_FATAL);
}
// We did find the node, so return the thread object in the node
return pThread;
}
// -----------------------------------------------------------------------------
// CLASS: THREAD
// PREFIX: thr
//
// This class provides thread support.
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// THREAD: Constructors and Destructors
// -----------------------------------------------------------------------------
//
// FUNCTION/METHOD NAME: THREAD(strName, pfnFunc, c4StackSz, pData, bAutoStart)
//
// DESCRIPTION:
//
// This is the only public constructor for thread objects. The calls must pass
// a thread name, a pointer to a thread function, a pointer to a data buffer
// which will be passed to the thread function, and an optional autostart flag.
// The thread object is placed into the linked list of current threads, after
// checking to make sure that a thread object with the same name does not
// already exist.
// ---------------------------------------
// INPUT: strName is the name to give to the thread
// pfnFunc is the address of the thread's function
// c4StackSz is the size of the stack to give to the thread. If less
// than 8K, it will be bumped up to that amount.
// pData is an optional pointer to a data buffer that is passed to the
// thread function.
// bAutoStart is an optional autostart flag. If eTRUE, then thread will
// be started immediately. It defaults eFALSE, so that the client
// app needs to call bStart() to start it.
// bSelfPrio indicates whether this thread should prevent any other
// thread from setting his priority.
//
// OUTPUT: None
//
// RETURN: None
//
PROCEXP THREAD::THREAD( const STRG32& strName
, tCIDLib::pfnTHREADFUNC pfnFunc
, tCIDLib::CARD4 c4StackSz
, tCIDLib::VOID* pData
, tCIDLib::eBOOL bAutoStart
, tCIDLib::eBOOL bSelfPrio) :
__bRunning(tCIDLib::eFALSE)
, __bSelfPrio(bSelfPrio)
, __bSyncRequest(tCIDLib::eFALSE)
, __c4StackSz(c4StackSz)
, __perrLast(0)
, __pfnFunc(pfnFunc)
, __pfnOnExit(0)
, __pData(pData)
, __strName(strName)
{
tCIDLib::CARD4 c4Err;
// Make sure that the stack size is not less than 8K
if (__c4StackSz < 8192)
{
__c4StackSz = 8192;
}
// Bump up the count of threads
_pmtrLIBCore->OffsetCard(tCIDLib_::eCOREMETRIC_THREADCNT, 1);
// Get access to the thread list
LSTLOCK Lock("THREAD::THREAD");
//
// Check the thread list and see if there is already a thread with
// this name.
//
THREAD* pThread = __pthrFindName(__strName);
// If we get a thread back, then a thread with that name already exists
if (pThread)
{
facCIDLib.LogErr(__FILE__
, "THREAD"
, __LINE__
, LIBERR_PRC_THREAD_EXISTS
, tCIDLib::eSEV_PROCESS_FATAL
, tCIDLib::eCLASS_BADPARMS
, strName);
}
// Add a new node to the list.
__AddThread(this);
// Create this thread's sync semaphore
c4Err = DosCreateEventSem(0, &__hevSync, 0, 0);
if (c4Err)
{
// Fill in the last error member with the error info and log it
facCIDLib.LogSysErr(__FILE__
, "THREAD"
, __LINE__
, c4Err
, "Could not create the sync semaphore"
, tCIDLib::eSEV_PROCESS_FATAL);
}
// Create this thread's sync response semaphore.
c4Err = DosCreateEventSem(0, &__hevResponse, 0, 0);
if (c4Err)
{
// Fill in the last error member with the error info and log it
facCIDLib.LogSysErr(__FILE__
, "THREAD"
, __LINE__
, c4Err
, "Could not create the sync response semaphore"
, tCIDLib::eSEV_PROCESS_FATAL);
}
//
// Create all of the per-thread objects for this thread. If there are not
// any then nothing will happen. Zero out the array first so we can do a
// sanity check later when we delete all of the objects.
//
memset(__apobjList, 0, sizeof(__apobjList));
__CreatePerThreadObjs();
// If the auto start flag is set, then call the start method
if (bAutoStart)
bStart();
}
//
// FUNCTION/METHOD NAME: THREAD()
//
// DESCRIPTION:
//
// This default constructor is protected so it can't be called by the general
// public.
// ---------------------------------------
// INPUT: None
//
// OUTPUT: None
//
// RETURN: None
//
PROCEXP THREAD::THREAD() :
__bRunning(tCIDLib::eTRUE)
, __bSelfPrio(tCIDLib::eFALSE)
, __bSyncRequest(tCIDLib::eFALSE)
, __c4StackSz(0)
, __pData(0)
, __perrLast(0)
, __pfnFunc(0)
, __tidThread(0)
{
_pmtrLIBCore->OffsetCard(tCIDLib_::eCOREMETRIC_THREADCNT, 1);
memset(__apobjList, 0, sizeof(__apobjList));
__CreatePerThreadObjs();
DosCreateEventSem(0, &__hevSync, 0, 0);
DosCreateEventSem(0, &__hevResponse, 0, 0);
}
THREAD::THREAD(const THREAD& thrSrc)
{
facCIDLib.LogMsg( __FILE__
, "THREAD"
, __LINE__
, "Copy constructor cannot be called"
, tCIDLib::eSEV_PROCESS_FATAL);
}
//
// FUNCTION/METHOD NAME: ~THREAD()
//
// DESCRIPTION:
//
// This is the destructor for threads. It cleans up the sync semaphore and
// removes this thread object from the linked list of running threads.
// ---------------------------------------
// INPUT: None
//
// OUTPUT: None
//
// RETURN: None
//
PROCEXP THREAD::~THREAD()
{
tCIDLib::CARD4 c4Err;
// Bump down the thread count
_pmtrLIBCore->OffsetCard(tCIDLib_::eCOREMETRIC_THREADCNT, -1);
// Get access to the thread list
LSTLOCK Lock("~THREAD");
// Remove this thread from the running list
if (!__bRemoveId(__tidThread))
{
facCIDLib.LogErr( __FILE__
, "~THREAD"
, __LINE__
, LIBERR_PRC_THREAD_NOT_FOUND
, tCIDLib::eSEV_PROCESS_FATAL
, tCIDLib::eCLASS_INTERNAL
, __strName);
}
// If the thread is running, then kill it
if (__bRunning)
{
c4Err = DosKillThread(__tidThread);
if (c4Err)
{
STRG128 strTmp("Could not kill thread ");
strTmp << __strName;
facCIDLib.LogSysErr(__FILE__
, "~THREAD"
, __LINE__
, c4Err
, strTmp
, tCIDLib::eSEV_PROCESS_FATAL);
}
}
// Close the sync semaphore
c4Err = DosCloseEventSem(__hevSync);
if (c4Err)
{
facCIDLib.LogSysErr(__FILE__
, "~THREAD"
, __LINE__
, c4Err
, "Could not close the sync semaphore"
, tCIDLib::eSEV_PROCESS_FATAL);
}
// Close the sync response semaphore
c4Err = DosCloseEventSem(__hevResponse);
if (c4Err)
{
facCIDLib.LogSysErr(__FILE__
, "~THREAD"
, __LINE__
, c4Err
, "Could not close the sync response semaphore"
, tCIDLib::eSEV_PROCESS_FATAL);
}
// Destroy the per-thread objects if any
__DestroyPerThreadObjs();
// Delete the last error object if any
if (__perrLast)
delete __perrLast;
}
// -----------------------------------------------------------------------------
// THREAD: Public, static methods
// -----------------------------------------------------------------------------
//
// FUNCTION/METHOD NAME: c4RegisterPerThreadObj(strObjName, pfnPerThread)
//
// DESCRIPTION:
//
// This method will register a per-thread object. The information is placed
// into the master per-thread object list, above. When thread objects are
// created, the callout for each entry in the list is called to allow the
// subsystems to create the object. This guy will also retofit this object to
// any existing threads.
// ---------------------------------------
// INPUT: strObjName is the name of the object create. It is just a
// descriptive string for logging messages and to insure that the
// clients do not accidentally register the same object twice.
// pfnPerThread is a pointer to a callout that will be made to let
// the caller create the object as desired.
//
// OUTPUT: None
//
// RETURN: A handle that the subsystem can use to get access to the object
// later.
//
tCIDLib::CARD4 THREAD::c4RegisterPerThreadObj
( const STRG64& strObjName
, tCIDLib::pfnPTHRFUNC pfnPerThread)
{
// Get access to the thread list
LSTLOCK Lock("THREAD::_c4RegisterPerThreadObj");
//
// Add a new record. It will make sure the name is unique. If so it will
// add it to the master list.
//
tCIDLib::CARD4 c4Handle = __c4AddPTObj(strObjName, pfnPerThread);
//
// Loop through any existing threads and retrofit this object to them
// all so we don't have to worry about different lists for every thread.
//
for (tCIDLib::CARD4 c4Ind = 0; c4Ind < __c4MaxThreads; c4Ind++)
{
if (__apThreads[c4Ind])
{
__apThreads[c4Ind]->__apobjList[c4Handle]
= __aPTObjs[c4Handle].pfnPerThread(c4Handle);
}
}
return c4Handle;
}
// -----------------------------------------------------------------------------
// THREAD: Public, virtual methods
// -----------------------------------------------------------------------------
//
// FUNCTION/METHOD NAME: operator=(thrSrc)
//
// DESCRIPTION:
//
// This method will handle assignment of one thread object to another.
// ---------------------------------------
// INPUT: thrSrc is the source thread to copy.
//
// OUTPUT: None
//
// RETURN: A reference to this object.
//
THREAD& THREAD::operator=(const THREAD& thrSrc)
{
facCIDLib.LogMsg( __FILE__
, "THREAD::operator="
, __LINE__
, "Assignment operator cannot be called"
, tCIDLib::eSEV_PROCESS_FATAL);
// Make compiler happy
return *this;
}
// -----------------------------------------------------------------------------
// THREAD: Public, inherited methods
// -----------------------------------------------------------------------------
//
// FUNCTION/METHOD NAME: FormatToStr(strbDest) const
//
// DESCRIPTION:
//
// This method will format this object into the destination string. For this
// class, we just format some debugging info into the string.
// ---------------------------------------
// INPUT: None
//
// OUTPUT: strbDest is the destination string to format into
//
// RETURN: None
//
tCIDLib::VOID PROCEXP THREAD::FormatToStr(STRGBUF& strbDest) const
{
strbDest << "Thread=" << __strName << ",Running=" << __bRunning;
}
// -----------------------------------------------------------------------------
// THREAD: Public, non-virtual methods
// -----------------------------------------------------------------------------
//
// FUNCTION/METHOD NAME: bProbeBuffer(pBuffer, c4Len)
//
// DESCRIPTION:
//
// This method will probe the passed buffer to see if it will cause an
// exception.
// ---------------------------------------
// INPUT: pBuffer is the buffer to probe
// c4Len is the length of the buffer
//
// OUTPUT: None
//
// RETURN: eTRUE if the buffer is ok, else eFALSE if it caused an exception
//
struct BUFPROBEEXP
{
PVOID pNext;
PFN pfnHandler;
jmp_buf JmpInfo;
};
APIRET APIENTRY __ProbeException( EXCEPTIONREPORTRECORD* pRep
, EXCEPTIONREGISTRATIONRECORD* pReg
, CONTEXTRECORD* pContext
, tCIDLib::VOID* pDummy)
{
// Make the compiler happy
pDummy; pContext;
// See if we are exiting
if ((pRep->fHandlerFlags & EH_EXIT_UNWIND)
|| (pRep->fHandlerFlags & EH_UNWINDING)
|| (pRep->fHandlerFlags & EH_NESTED_CALL))
{
return XCPT_CONTINUE_SEARCH;
}
// If an access violation, then do the long jump to stored state
if (pRep->ExceptionNum == XCPT_ACCESS_VIOLATION)
longjmp(((BUFPROBEEXP*)pReg)->JmpInfo, pRep->ExceptionNum);
// Not an access violation so let it go
return XCPT_CONTINUE_SEARCH;
}
tCIDLib::eBOOL PROCEXP THREAD::bProbeBuffer(tCIDLib::VOID* pBuffer
, tCIDLib::CARD4 c4Len)
{
BUFPROBEEXP BufProbe;
tCIDLib::CARD4 c4Err, c4Except;
BufProbe.pfnHandler = (PFN)__ProbeException;
c4Err = DosSetExceptionHandler((PEXCEPTIONREGISTRATIONRECORD)&BufProbe);
if (c4Err)
{
facCIDLib.LogSysErr(__FILE__
, "bProbeBuffer"
, __LINE__
, c4Err
, "Could not register buffer probe exception"
, tCIDLib::eSEV_PROCESS_FATAL);
}
// Store the jump info
c4Except = setjmp(BufProbe.JmpInfo);
if (c4Except)
{
c4Err =
DosUnsetExceptionHandler((PEXCEPTIONREGISTRATIONRECORD)&BufProbe);
if (c4Except == XCPT_ACCESS_VIOLATION)
return tCIDLib::eFALSE;
}
// Do the buffer probing
tCIDLib::CARD1 c1Tmp;
tCIDLib::CARD1* pc1Buf = (tCIDLib::CARD1*)pBuffer;
c1Tmp = pc1Buf[0];
c1Tmp = pc1Buf[c4Len-1];
// Make the compiler happy
c1Tmp;
c4Err = DosUnsetExceptionHandler((PEXCEPTIONREGISTRATIONRECORD)&BufProbe);
if (c4Err)
{
facCIDLib.LogSysErr(__FILE__
, "bProbeBuffer"
, __LINE__
, c4Err
, "Could not deregister buffer probe exception"
, tCIDLib::eSEV_PROCESS_FATAL);
}
// Went ok, so return eTRUE
return tCIDLib::eTRUE;
}
//
// FUNCTION/METHOD NAME: bRelease()
//
// DESCRIPTION:
//
// This method is called by another thread that has sync'd with this thread.
// Calling this method will release this thread again.
// ---------------------------------------
// INPUT: None
//
// OUTPUT: None
//
// RETURN: eTRUE if successful, else eFALSE.
//
tCIDLib::eBOOL PROCEXP THREAD::bRelease()
{
LSTLOCK Lock("THREAD::bRelease");
// If there is not an outstanding sync request, then this call is bogus
if (!__bSyncRequest)
{
facCIDLib.LogMsg( __FILE__
, "THREAD::bRelease"
, __LINE__
, "There was no outstanding thread sync to release"
, tCIDLib::eSEV_PROCESS_FATAL);
}
// Make sure the caller is the one that did the sync
#if DEBUGLOW
THREAD* pthrCaller = facCIDLib.pthrCaller();
if (pthrCaller->tidThread() != __tidSyncReq)
{
facCIDLib.LogErr( __FILE__
, "THREAD::bRelease"
, __LINE__
, LIBERR_PRC_NOT_SYNCREQ
, tCIDLib::eSEV_PROCESS_FATAL
, tCIDLib::eCLASS_APPERROR
, pthrCaller->strName()
, __strName);
}
#endif
// Clear the sync request flag
__bSyncRequest = tCIDLib::eFALSE;
// Post the sync semaphore to let the thread go
tCIDLib::CARD4 c4Err = DosPostEventSem(__hevSync);
if (c4Err)
{
facCIDLib.LogSysErr(__FILE__
, "THREAD::bRelease"
, __LINE__
, c4Err
, "Could not post the sync semaphore"
, tCIDLib::eSEV_PROCESS_FATAL);
}
return tCIDLib::eTRUE;
}
//
// FUNCTION/METHOD NAME: bStart()
//
// DESCRIPTION:
//
// This method will start the thread running if it is not already running.
// Note that we want to exit here with the __hevSync semaphore posted so
// that the thread will not block prematurely when it calls bSync().
// ---------------------------------------
// INPUT: None
//
// OUTPUT: None
//
// RETURN: eTRUE if successful, else eFALSE.
//
tCIDLib::eBOOL PROCEXP THREAD::bStart()
{
if (__bRunning)
{
facCIDLib.LogErr( __FILE__
, "THREAD::bStart"
, __LINE__
, LIBERR_PRC_THREAD_RUNNING
, tCIDLib::eSEV_WARNING
, tCIDLib::eCLASS_ALREADY
, __strName);
return tCIDLib::eFALSE;
}
// Try to start up the thread
tCIDLib::CARD4 c4Err, c4PostCount;
// Reset the sync semaphore so it won't go until we tell it to
c4Err = DosResetEventSem(__hevSync, &c4PostCount);
if ((c4Err) && (c4Err != LIBERR_EVSEM_ALREADY_RESET))
{
facCIDLib.LogSysErr(__FILE__
, "THREAD::bStart"
, __LINE__
, c4Err
, "Could not reset the sync semaphore"
, tCIDLib::eSEV_PROCESS_FATAL);
}
// Start the thread and pass the startup data
STARTDATA* pStartData = new STARTDATA;
pStartData->pThread = this;
pStartData->pData = __pData;
//
// Note that the stack address is not used in 32 bit world, so we pass
// 0.
//
int id = _beginthread(__ThreadStart, 0, __c4StackSz, pStartData);
if (id < 0)
{
facCIDLib.LogErr( __FILE__
, "THREAD::bStart"
, __LINE__
, LIBERR_PRC_THREAD_START_FAILED
, tCIDLib::eSEV_WARNING
, tCIDLib::eCLASS_ALREADY
, __strName);
facCIDLib.ExitProcess(tCIDLib::eEXIT_FATAL);
}
//
// It went ok, so set the running flag, store the id, and post to the
// sync semaphore to let it go.
//
__tidThread = TID(id);
__bRunning = tCIDLib::eTRUE;
c4Err = DosPostEventSem(__hevSync);
if (c4Err)
{
facCIDLib.LogSysErr(__FILE__
, "THREAD::bStart"
, __LINE__
, c4Err
, "Could not post the sync semaphore"
, tCIDLib::eSEV_PROCESS_FATAL);
}
return tCIDLib::eTRUE;
}
//
// FUNCTION/METHOD NAME: bWaitSync()
//
// DESCRIPTION:
//
// Other threads call this method to sync up with this thread. On successful
// return, the thread is waiting for the calling thread to call its bRelease
// method to let it go again.
// ---------------------------------------
// INPUT: None
//
// OUTPUT: None
//
// RETURN: None
//
tCIDLib::eBOOL PROCEXP THREAD::bWaitSync()
{
tCIDLib::CARD4 c4Count;
// Make sure that thread does not sync with himself
#if DEBUGLOW
if (this == facCIDLib.pthrCaller())
{
facCIDLib.LogErr( __FILE__
, "THREAD::bWaitSync"
, __LINE__
, LIBERR_PRC_SYNC_WITH_SELF
, tCIDLib::eSEV_PROCESS_FATAL
, tCIDLib::eCLASS_APPERROR
, __strName);
}
#endif
// If there is already a request, then we can't do it
DosEnterCritSec();
if (__bSyncRequest)
{
DosExitCritSec();
return tCIDLib::eFALSE;
}
// Set the flag so we won't be interrupted
__bSyncRequest = tCIDLib::eTRUE;
DosExitCritSec();
// Remember the id of the requester
PTIB pTBlk;
PPIB pPBlk;
DosGetInfoBlocks(&pTBlk, &pPBlk);
__tidSyncReq = pTBlk->tib_ptib2->tib2_ultid;
//
// Reset the sync semaphore to cause the thread to block the next time
// it calls Sync().
//
DosResetEventSem(__hevSync, &c4Count);
//
// Block on the response semaphore, which should already be reset. When
// we awake, the thread is blocked on the sync semaphore. So we can
// return.
//
DosWaitEventSem(__hevResponse, SEM_INDEFINITE_WAIT);
return tCIDLib::eTRUE;
}
//
// FUNCTION/METHOD NAME: ClrLastErr()
//
// DESCRIPTION:
//
// Clears the last error on this thread.
// ---------------------------------------
// INPUT: None
//
// OUTPUT: None
//
// RETURN: None
//
tCIDLib::VOID PROCEXP THREAD::ClrLastErr()
{
// Abort if the caller is not this thread
_CheckCallerIsSelf("ClrLastErr");
if (__perrLast)
{
delete (__perrLast);
__perrLast = 0;
}
}
//
// FUNCTION/METHOD NAME: c4LastErrCode() const
//
// DESCRIPTION:
//
// This method will return the error code of the current error on this
// thread. It then deletes the error info. If no info is available the
// return value is 0.
// ---------------------------------------
// INPUT: None
//
// OUTPUT: None
//
// RETURN: The last error code
//
tCIDLib::CARD4 PROCEXP THREAD::c4LastErrCode()
{
// Abort if the caller is not this thread
_CheckCallerIsSelf("c4LastErrCode");
// Lock the thread list
LSTLOCK Lock("THREAD::cLastErrCode");
tCIDLib::CARD4 c4Code;
if (__perrLast)
{
c4Code = __perrLast->c4ErrCode();
delete __perrLast;
__perrLast = 0;
}
return c4Code;
}
//
// FUNCTION/METHOD NAME: GetPriority(ePrtyClass, c4PrioLev)
//
// DESCRIPTION:
//
// This method will return the priority of this thread.
// ---------------------------------------
// INPUT: NOne
//
// OUTPUT: ePrtyClass is filled in with the thread's class
// c4PrioLev is filled in with the thread's priority level.
//
// RETURN: None
//
tCIDLib::VOID PROCEXP THREAD::GetPriority( tCIDLib::ePRTYCLASS& ePrtyClass
, tCIDLib::CARD4& c4PrioLev)
{
// Abort if the caller is not this thread
_CheckCallerIsSelf( "GetPriority"
,"Only a thread can get its own priority");
tCIDLib::CARD4 c4Err;
PTIB pTBlk;
PPIB pPBlk;
c4Err = DosGetInfoBlocks(&pTBlk, &pPBlk);
if (c4Err)
{
facCIDLib.LogSysErr(__FILE__
, "THREAD::GetPriority"
, __LINE__
, c4Err
, "DosGetInfoBlocks failed"
, tCIDLib::eSEV_PROCESS_FATAL);
}
//
// The values is encoded with the class in the 2nd byte and the level
// in the 1st byte.
//
ePrtyClass = tCIDLib::ePRTYCLASS(pTBlk->tib_ptib2->tib2_ulpri >> 8);
c4PrioLev = pTBlk->tib_ptib2->tib2_ulpri & 0xFF;
}
//
// FUNCTION/METHOD NAME: pfnSetOnExit(pfnNew)
//
// DESCRIPTION:
//
// This method will set the on-exit function and return the current one. It
// can only be called by this thread.
// ---------------------------------------
// INPUT: pfnNew is the new on-exit function to call when the thread shuts
// down.
//
// OUTPUT: None
//
// RETURN: The current on-exit function
//
tCIDLib::pfnEXITFUNC
PROCEXP THREAD::pfnSetOnExit(tCIDLib::pfnEXITFUNC pfnNew)
{
tCIDLib::pfnEXITFUNC pfnTmp = __pfnOnExit;
__pfnOnExit = pfnNew;
return pfnTmp;
}
//
// FUNCTION/METHOD NAME: perrLast()
//
// DESCRIPTION:
//
// This method will return the last error that occured. The caller is now
// responsible for deleting the error object!!!
// ---------------------------------------
// INPUT: None
//
// OUTPUT: None
//
// RETURN: The address of the last error object, 0 if there was no error
// info.
//
CIDERROR* PROCEXP THREAD::perrLast()
{
// Abort if the caller is not this thread
_CheckCallerIsSelf("perrLast");
CIDERROR* pErr;
// Lock the thread list
LSTLOCK Lock("THREAD::perrLast");
if (__perrLast)
{
pErr = __perrLast;
__perrLast = 0;
}
else
{
pErr = 0;
}
return pErr;
}
//
// FUNCTION/METHOD NAME: SetPriority(ePrtyClass, c4PrioLev)
//
// DESCRIPTION:
//
// The first version of this method will set the priority class of the
// thread and it's absolute level within that class. The second version
// does a relative adjustment of the level without changing the class.
// ---------------------------------------
// INPUT: ePrtyClass is the class to set
// c4PrioLev is the priority level with the class, which must be
// from 0 to tCIDLib::c4MaxPrioLev.
//
// OUTPUT: None
//
// RETURN: None
//
tCIDLib::VOID PROCEXP THREAD::SetPriority( tCIDLib::ePRTYCLASS ePrtyClass
, tCIDLib::CARD4 c4PrioLev)
{
tCIDLib::CARD4 c4Err;
#if DEBUGMED
if (c4PrioLev > tCIDLib::c4MaxPrioLev)
{
facCIDLib.LogErr( __FILE__
, "THREAD::SetPriority"
, __LINE__
, LIBERR_PRC_BAD_PRIO_LEV
, "The value was clipped to valid range"
, tCIDLib::eSEV_WARNING
, tCIDLib::eCLASS_BADPARMS
, CARDINAL(c4PrioLev)
, CARDINAL(tCIDLib::c4MaxPrioLev));
c4PrioLev = tCIDLib::c4MaxPrioLev;
}
#endif
//
// If this thread is in SELFPRIO mode, then only it can set its own
// priority.
//
if (__bSelfPrio)
{
// Abort if the caller is not this thread
_CheckCallerIsSelf("SetPriority", "This thread is in SELFPRIO mode");
}
c4Err = DosSetPriority(PRTYS_THREAD, ePrtyClass, c4PrioLev, __tidThread);
if (c4Err)
{
facCIDLib.LogSysErr(__FILE__
, "THREAD::SetPriority"
, __LINE__
, c4Err
, "DosSetPriority failed"
, tCIDLib::eSEV_PROCESS_FATAL);
}
}
tCIDLib::VOID PROCEXP THREAD::SetPriority(tCIDLib::INT4 i4PrioLev)
{
tCIDLib::CARD4 c4Err;
//
// If this thread is in SELFPRIO mode, then only it can set its own
// priority.
//
if (__bSelfPrio)
{
// Abort if the caller is not this thread
_CheckCallerIsSelf("SetPriority", "This thread is in SELFPRIO mode");
}
c4Err = DosSetPriority( PRTYS_THREAD
, PRTYC_NOCHANGE
, i4PrioLev
, __tidThread);
if (c4Err)
{
facCIDLib.LogSysErr(__FILE__
, "THREAD::SetPriority"
, __LINE__
, c4Err
, "DosSetPriority failed"
, tCIDLib::eSEV_PROCESS_FATAL);
}
}
//
// FUNCTION/METHOD NAME: StoreNewError(perrNew)
//
// DESCRIPTION:
//
// This method allows the thread to store a new object. It will check to
// make sure that the caller is the thread controlled by this object.
// ---------------------------------------
// INPUT: perrNew is a pointer to the new error object to store.
//
// OUTPUT: None
//
// RETURN: None
//
tCIDLib::VOID PROCEXP THREAD::StoreNewError(CIDERROR* perrNew)
{
// Abort if the caller is not this thread
_CheckCallerIsSelf("StoreNewError");
// No need to synchronize now since only this thread calls
if (__perrLast)
delete __perrLast;
__perrLast = perrNew;
}
//
// FUNCTION/METHOD NAME: strName()
//
// DESCRIPTION:
//
// This method returns the name of the thread.
// ---------------------------------------
// INPUT: None
//
// OUTPUT: None
//
// RETURN: The name of the thread
//
const STRG32& PROCEXP THREAD::strName() const
{
return __strName;
}
//
// FUNCTION/METHOD NAME: Sync()
//
// DESCRIPTION:
//
// This method will block if another thread has called bWaitSync() and is
// waiting for this thread to sync up.
// ---------------------------------------
// INPUT: None
//
// OUTPUT: None
//
// RETURN: eTRUE if successful, else eFALSE
//
tCIDLib::VOID PROCEXP THREAD::Sync()
{
_CheckCallerIsSelf("THREAD::Sync");
tCIDLib::CARD4 c4Err;
// If there is no sync request, then just return
if (!__bSyncRequest)
return;
// Clear the response semaphore to indicate that we are not waiting
c4Err = DosPostEventSem(__hevResponse);
// Block on the sync semaphore
c4Err = DosWaitEventSem(__hevSync, SEM_INDEFINITE_WAIT);
// If an error, then abort
if (c4Err)
{
facCIDLib.LogSysErr(__FILE__
, "THREAD::bSync"
, __LINE__
, c4Err
, "Could not wait on the sync semaphore"
, tCIDLib::eSEV_PROCESS_FATAL);
}
// We are awake again so the syncing thread has released us
}
// -----------------------------------------------------------------------------
// THREAD: Protected, inherited methods
// -----------------------------------------------------------------------------
//
// FUNCTION/METHOD NAME: _bIsEqual(objTarget) const
//
// DESCRIPTION:
//
// This method compares this object to the target object, at this class level.
// ---------------------------------------
// INPUT: objTarget is the target object to compare against
//
// OUTPUT: None
//
// RETURN: eTRUE if the objects are equal, else eFALSE.
//
tCIDLib::eBOOL PROCEXP THREAD::_bIsEqual(const CIDOBJECT& objTarget) const
{
// Call our parent's version first
if (!CIDOBJECT::_bIsEqual(objTarget))
return tCIDLib::eFALSE;
// Look at the target object as a thread
THREAD* pthrTmp = (THREAD*)(&objTarget);
if (__bRunning != pthrTmp->__bRunning)
return tCIDLib::eFALSE;
if (__tidThread != pthrTmp->__tidThread)
return tCIDLib::eFALSE;
if (__strName != pthrTmp->__strName)
return tCIDLib::eFALSE;
if (__pData != pthrTmp->__pData)
return tCIDLib::eFALSE;
if (__pfnFunc != pthrTmp->__pfnFunc)
return tCIDLib::eFALSE;
return tCIDLib::eTRUE;
}
//
// FUNCTION/METHOD NAME: _CheckCallerIsSelf(pszMethod, pszAuxText) const
//
// DESCRIPTION:
//
// This method will check to make sure that the calling thread is this
// thread. If not, it will issue a fatal error message and abort.
// ---------------------------------------
// INPUT: pszMethod is the name of the calling method, which will be used
// in the abort message if needed.
// pszAuxText is an optional text string for the aux text of the
// logged error. It defaults to 0.
//
// OUTPUT: None
//
// RETURN: None
//
tCIDLib::VOID PROCEXP
THREAD::_CheckCallerIsSelf( const tCIDLib::CH* pszMethod
, const tCIDLib::CH* pszAuxText) const
{
THREAD* pthrCaller = _pthrCurrent();
if (pthrCaller != this)
{
facCIDLib.LogErr( __FILE__
, pszMethod
, __LINE__
, LIBERR_PRC_NOT_THIS_THREAD
, pszAuxText ? pszAuxText : ""
, tCIDLib::eSEV_PROCESS_FATAL
, tCIDLib::eCLASS_AUTHORITY
, pthrCaller->strName()
, this->strName());
}
}
//
// FUNCTION/METHOD NAME: _Exiting()
//
// DESCRIPTION:
//
// This method is provided so that the standard thread exception handler
// can clean up the thread flags when it is killed.
// ---------------------------------------
// INPUT: None
//
// OUTPUT: None
//
// RETURN: None
//
tCIDLib::VOID PROCEXP THREAD::_Exiting()
{
//
// Call the on-exit function to let the thread object clean up. This
// is for user code since it does not require a separate derivation to
// do.
//
if (__pfnOnExit)
__pfnOnExit(*this);
//
// Call the virtual terminate method to let derived thread classes do
// internal cleanup.
//
Terminate();
__bRunning = tCIDLib::eFALSE;
__tidThread= TID(-1);
// If there is a last error, then clean it up
if (__perrLast)
{
delete __perrLast;
__perrLast = 0;
}
}
// -----------------------------------------------------------------------------
// THREAD: Private, non-virtual methods
// -----------------------------------------------------------------------------
//
// FUNCTION/METHOD NAME: __CreatePerThreadObjs()
//
// DESCRIPTION:
//
// This method is called by the constructor. It creates instances of all the
// registered per-thread objects for this thread.
// ---------------------------------------
// INPUT: None
//
// OUTPUT: None
//
// RETURN: None
//
tCIDLib::VOID PRIVEXP THREAD::__CreatePerThreadObjs()
{
//
// Loop through the master list of registered per-thread objects. Calling
// the callback for each one.
//
for (tCIDLib::CARD4 c4Ind = 0; c4Ind < __c4PTObjCount; c4Ind++)
__apobjList[c4Ind] = __aPTObjs[c4Ind].pfnPerThread(c4Ind);
}
//
// FUNCTION/METHOD NAME: __DestroyPerThreadObjs()
//
// DESCRIPTION:
//
// This method is called by the destructor. It destroys all of the per-thread
// objects of this thread.
// ---------------------------------------
// INPUT: None
//
// OUTPUT: None
//
// RETURN: None
//
tCIDLib::VOID PRIVEXP THREAD::__DestroyPerThreadObjs()
{
//
// Loop through the per-thread objects, deleting each one. If we find a
// zeroed out entry before we hit the max count, then issue a fatal error.
//
for (tCIDLib::CARD4 c4Ind = 0; c4Ind < __c4PTObjCount; c4Ind++)
{
if (!__apobjList[c4Ind])
{
facCIDLib.LogMsg( __FILE__
, "THREAD::__DestroyPTObjs"
, __LINE__
, "Per-thread object count disagrees with "
"per-thread list"
, tCIDLib::eSEV_PROCESS_FATAL);
}
else
{
delete __apobjList[c4Ind];
}
}
}