home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / deans.zip / PROCESS.CPP < prev    next >
C/C++ Source or Header  |  1994-09-14  |  62KB  |  1,996 lines

  1. //
  2. // NAME: CIDLib_Process.Cpp
  3. //
  4. // DESCRIPTION:
  5. //
  6. //  This module implements the THREAD class, which provides process control
  7. //  for all CIDLib based applications.
  8. //
  9. //
  10. // AUTHOR: Dean Roddey
  11. //
  12. // CREATE DATE: 04/16/93
  13. //
  14. // COPYRIGHT: 1992..1994, 'CIDCorp
  15. //
  16. // CAVEATS/GOTCHAS:
  17. //
  18. //  1)  The bInitProcess() method that is called during facility init, has to
  19. //      bypass the normal THREAD constructor and so calls the default
  20. //      constructor and fills in the thread object (which stands for the main
  21. //      thread). So be sure to check it if any new THREAD data members are
  22. //      added.
  23. //
  24. //  2)  The local thread list and per-thread object manipulation functions
  25. //      assume that the methods calling them have locked the access semaphore.
  26. //
  27. //  3)  When a message is logged by any code on a thread (with a severity of
  28. //      APIFAILED or greater), it is stored in the thead object as the
  29. //      thread's last error. This can be queried later by the client code
  30. //      to see what happened. If it is fatal, then an exception is thrown.
  31. //
  32. // MODIFICATION LOG:
  33. //
  34.  
  35.  
  36. // -----------------------------------------------------------------------------
  37. //  Local defines
  38. // -----------------------------------------------------------------------------
  39. #define     CIDLIB_PROCESS_CPP
  40.  
  41. #define     INCL_DOSEXCEPTIONS
  42. #define     INCL_DOSSEMAPHORES
  43.  
  44. #define     INCL_CIDLIB_ERROBJS
  45. #define     INCL_CIDLIB_ERRORIDS
  46. #define     INCL_CIDLIB_FACOBJECT
  47. #define     INCL_CIDLIB_METRICS
  48. #define     INCL_CIDLIB_PROCESS
  49.  
  50.  
  51.  
  52. // -----------------------------------------------------------------------------
  53. //  C runtime support includes
  54. // -----------------------------------------------------------------------------
  55. #include    <process.h>
  56. #include    <setjmp.h>
  57. #include    <string.h>
  58.  
  59. // -----------------------------------------------------------------------------
  60. //  Facility specific includes
  61. //
  62. //  We bring a special header CIDLib_Exception_.Hpp here, which is shared
  63. //  only by this module and CIDLib_Exception.Cpp. It provides us a call for
  64. //  a function to log exception information.
  65. // -----------------------------------------------------------------------------
  66. #include    "CIDLib_.Hpp"
  67. #include    "CIDLib_Exception_.Hpp"
  68.  
  69.  
  70. // -----------------------------------------------------------------------------
  71. //  Static, constant members of tCIDLib that we are responsible for
  72. // -----------------------------------------------------------------------------
  73. const tCIDLib::CARD4    tCIDLib::c4MaxPrioLev   = 31;
  74.  
  75.  
  76. // -----------------------------------------------------------------------------
  77. //  Constant data for local use
  78. //
  79. //  __c4MaxThreads
  80. //      The maximum number of threads supported per-process.
  81. // -----------------------------------------------------------------------------
  82. const tCIDLib::CARD4    __c4MaxThreads          = 512;
  83.  
  84.  
  85. // -----------------------------------------------------------------------------
  86. //  Local data types
  87. //
  88. //  PERTHREADINFO
  89. //      This is used to allow per-thread objects to be stored. Other subsystems
  90. //      can register the need to have per-thread objects. When each thread
  91. //      is created, an array of per-thread object pointers are created for the
  92. //      thread. An array of these structures is used to store the master list
  93. //      of objects to create. It stores the name of the object (for logging
  94. //      error messages and insuring non-duplication) and a call out function
  95. //      that the subsystem provides to create the objects.
  96. //
  97. //  STARTDATA
  98. //      This structure is used to pass startup data to the __ThreadStart()
  99. //      function. This function is used to start all threads. It does a
  100. //      regular call to the actual thread function.
  101. // -----------------------------------------------------------------------------
  102. struct                  PERTHREADINFO
  103. {
  104.     tCIDLib::ZSTR64     szObjName;
  105.     tCIDLib::pfnPTHRFUNC pfnPerThread;
  106. };
  107.  
  108. struct                  STARTDATA
  109. {
  110.     THREAD*             pThread;
  111.     tCIDLib::VOID*      pData;
  112. };
  113.  
  114.  
  115.  
  116. // -----------------------------------------------------------------------------
  117. //  Variables for local use
  118. //
  119. //  __aPTObjs
  120. //      The list of registered per-thread objects.
  121. //
  122. //  __apThreads
  123. //      A list of the thread objects that currently exist. We can use the
  124. //      _bRunning member to know whether the thread is currently running or
  125. //      not.
  126. //
  127. //  __bInited
  128. //      This is set when the _bInitProcess method has been called. So, if an
  129. //      error occurs during startup, we won't flip out by trying to access
  130. //      bad data.
  131. //
  132. //  __c4PTObjCount
  133. //      The count of items in the __aPTObjs list, which holds entries for the
  134. //      registered per-thread objects.
  135. //
  136. //  __c4ThreadCount
  137. //      The count of thread objects currently defined. They are not all
  138. //      necessarily running.
  139. //
  140. //  __hmtxListAccess
  141. //      A semaphore to coordinate access to the __apThreads and __aPTObjs
  142. //      arrays, to insure that multiple threads don't step on each other's
  143. //      toes.
  144. //
  145. //  __pthrMain
  146. //      This is a dummy thread object manually set up in the _bInitProcess()
  147. //      function for the startup thread.
  148. // -----------------------------------------------------------------------------
  149. PERTHREADINFO           __aPTObjs[c4MaxPTObjs];
  150. THREAD*                 __apThreads[__c4MaxThreads];
  151. tCIDLib::eBOOL          __bInited;
  152. tCIDLib::CARD4          __c4PTObjCount;
  153. tCIDLib::CARD4          __c4ThreadCount;
  154. HMTX                    __hmtxListAccess;
  155. THREAD*                 __pthrMain;
  156.  
  157.  
  158.  
  159. // -----------------------------------------------------------------------------
  160. //   CLASS: LSTLOCK
  161. //  PREFIX: lck
  162. //
  163. //  Define a local tiny class that will make it safer to lock/unlock the
  164. //  thread list access semaphore (__hmtxListAccess above).
  165. // -----------------------------------------------------------------------------
  166. class               LSTLOCK
  167. {
  168.     public          :
  169.         // --------------------------------------------------------------------
  170.         //  Constructors and destructors
  171.         // --------------------------------------------------------------------
  172.  
  173.         LSTLOCK(tCIDLib::CH* pszFunc)
  174.         {
  175.             tCIDLib::CARD4  c4Err;
  176.  
  177.             // Get access to the thread list
  178.             c4Err = DosRequestMutexSem( __hmtxListAccess
  179.                                         , SEM_INDEFINITE_WAIT);
  180.             if (c4Err)
  181.             {
  182.                 facCIDLib.LogSysErr(__FILE__
  183.                                     , pszFunc
  184.                                     , __LINE__
  185.                                     , c4Err
  186.                                     , "Could not lock thread list access semaphore"
  187.                                     , tCIDLib::eSEV_PROCESS_FATAL);
  188.             }
  189.             strcpy(__szFunc, pszFunc);
  190.         }
  191.  
  192.         ~LSTLOCK()
  193.         {
  194.            tCIDLib::CARD4  c4Err;
  195.  
  196.            // Get access to the thread list
  197.            c4Err = DosReleaseMutexSem(__hmtxListAccess);
  198.            if (c4Err)
  199.            {
  200.                 facCIDLib.LogSysErr(__FILE__
  201.                                     , __szFunc
  202.                                     , __LINE__
  203.                                     , c4Err
  204.                                     , "Could not release thread list access semaphore"
  205.                                     , tCIDLib::eSEV_PROCESS_FATAL);
  206.            }
  207.         }
  208.  
  209.     private         :
  210.         // --------------------------------------------------------------------
  211.         //  Private data members
  212.         //
  213.         //  __szFunc
  214.         //      The name of the function with the lock
  215.         // --------------------------------------------------------------------
  216.         tCIDLib::CH             __szFunc[128];
  217. };
  218.  
  219.  
  220. // -----------------------------------------------------------------------------
  221. //  Functions for local use only
  222. // -----------------------------------------------------------------------------
  223.  
  224. //
  225. // FUNCTION/METHOD NAME: __AddThread(pThead)
  226. //
  227. // DESCRIPTION:
  228. //
  229. //  This method is called when a new thread is to be added to the list. It
  230. //  will abort if the thread count exceeds the maximum.
  231. // ---------------------------------------
  232. //   INPUT: pThread is the new thread object to add.
  233. //
  234. //  OUTPUT: None
  235. //
  236. //  RETURN: None
  237. //
  238. static tCIDLib::VOID __AddThread(THREAD* pThread)
  239. {
  240.     if (__c4ThreadCount == __c4MaxThreads)
  241.     {
  242.         facCIDLib.LogErr(   __FILE__
  243.                             , "__AddThread"
  244.                             , __LINE__
  245.                             , LIBERR_PRC_TOO_MANY_THREADS
  246.                             , tCIDLib::eSEV_PROCESS_FATAL
  247.                             , tCIDLib::eCLASS_OUTRESOURCE
  248.                             , CARDINAL(__c4MaxThreads));
  249.     }
  250.  
  251.     //
  252.     //  Scan for an empty entry in the list. If we don't find one, then
  253.     //  there is a major screw up because the thread count is wrong. Also,
  254.     //  count how many are taken while we look. If it exceeds
  255.     //  __c4ThreadCount, then that is an error too.
  256.     //
  257.     tCIDLib::CARD4  c4Ind, c4Count = 0;
  258.     for (c4Ind = 0; c4Ind < __c4MaxThreads; c4Ind++)
  259.     {
  260.         if (!__apThreads[c4Ind])
  261.             break;
  262.         c4Count++;
  263.     }
  264.  
  265.     if ((c4Count > __c4ThreadCount)
  266.     ||  (c4Ind == __c4MaxThreads))
  267.     {
  268.         facCIDLib.LogErr(   __FILE__
  269.                             , "__AddThread"
  270.                             , __LINE__
  271.                             , LIBERR_PRC_THREAD_COUNT
  272.                             , tCIDLib::eSEV_PROCESS_FATAL
  273.                             , tCIDLib::eCLASS_INTERNAL
  274.                             , CARDINAL(__c4ThreadCount)
  275.                             , CARDINAL(c4Count));
  276.     }
  277.  
  278.     // Store the thread and bump up the count
  279.     __apThreads[c4Ind] = pThread;
  280.     __c4ThreadCount++;
  281. }
  282.  
  283.  
  284. //
  285. // FUNCTION/METHOD NAME: __bRemoveId(tidThread)
  286. //
  287. // DESCRIPTION:
  288. //
  289. //  This method will find the thread in the list with the passed id and
  290. //  then remove the entry, setting it to 0 and bumping down the thread
  291. //  count.
  292. // ---------------------------------------
  293. //   INPUT: tidThread is the thread id to look for
  294. //
  295. //  OUTPUT: None
  296. //
  297. //  RETURN: A pointer to the thread or 0 if not found
  298. //
  299. static tCIDLib::eBOOL __bRemoveId(TID tidThread)
  300. {
  301.     if (!__c4ThreadCount)
  302.     {
  303.         facCIDLib.LogErr(   __FILE__
  304.                             , "__bRemoveId"
  305.                             , __LINE__
  306.                             , LIBERR_PRC_THREAD_COUNT_0
  307.                             , tCIDLib::eSEV_PROCESS_FATAL
  308.                             , tCIDLib::eCLASS_INTERNAL);
  309.     }
  310.  
  311.     for (tCIDLib::CARD4 c4Ind = 0; c4Ind < __c4MaxThreads; c4Ind++)
  312.     {
  313.         if (__apThreads[c4Ind])
  314.         {
  315.             if (__apThreads[c4Ind]->tidThread() == tidThread)
  316.             {
  317.                 __c4ThreadCount--;
  318.                 __apThreads[c4Ind] = (THREAD*)0;
  319.                 return tCIDLib::eTRUE;
  320.             }
  321.         }
  322.     }
  323.     return tCIDLib::eFALSE;
  324. }
  325.  
  326.  
  327. //
  328. // FUNCTION/METHOD NAME: __c4AddPTObj(strObjName, pfnPerThread)
  329. //
  330. // DESCRIPTION:
  331. //
  332. //  This method will add a per-thread object to the master list. It is assumed
  333. //  that the caller has locked the thread access semaphore, so we don't worry
  334. //  about manipulating locally global data.
  335. // ---------------------------------------
  336. //   INPUT: strObjName is a descriptive name for the object.
  337. //          pfnPerThread is the callout function that will be called to create
  338. //              the object.
  339. //
  340. //  OUTPUT: None
  341. //
  342. //  RETURN: The index at which the new item was added.
  343. //
  344. static tCIDLib::CARD4
  345.         __c4AddPTObj( const   STRG64&                 strObjName
  346.                       ,       tCIDLib::pfnPTHRFUNC    pfnPerThread)
  347. {
  348.     if (__c4PTObjCount >= c4MaxPTObjs)
  349.     {
  350.         facCIDLib.LogMsg(   __FILE__
  351.                             , "__c4AddPTObj"
  352.                             , __LINE__
  353.                             , "Too many per thread objects registered"
  354.                             , tCIDLib::eSEV_PROCESS_FATAL);
  355.     }
  356.  
  357.     // Look for an object with this name already in the list
  358.     for (tCIDLib::CARD4 c4Ind = 0; c4Ind < __c4PTObjCount; c4Ind++)
  359.     {
  360.         if (!stricmp(__aPTObjs[c4Ind].szObjName, strObjName.pSZ()))
  361.         {
  362.             facCIDLib.LogMsg(   __FILE__
  363.                                 , "__c4AddPTObj"
  364.                                 , __LINE__
  365.                                 , "Per-thread object %(1) already exists"
  366.                                 , tCIDLib::eSEV_PROCESS_FATAL
  367.                                 , tCIDLib::eCLASS_ALREADY
  368.                                 , strObjName);
  369.         }
  370.     }
  371.  
  372.     // Bump up the index
  373.     __c4PTObjCount++;
  374.  
  375.     // Add the new guy
  376.     strcpy(__aPTObjs[__c4PTObjCount].szObjName, strObjName.pSZ());
  377.     __aPTObjs[__c4PTObjCount].pfnPerThread = pfnPerThread;
  378.  
  379.     // Return the new index
  380.     return __c4PTObjCount;
  381. }
  382.  
  383.  
  384. //
  385. // FUNCTION/METHOD NAME: __pthrFindId(tidThread)
  386. //
  387. // DESCRIPTION:
  388. //
  389. //  This method will find the thread in the list with the passed id.
  390. // ---------------------------------------
  391. //   INPUT: tidThread is the thread id to look for
  392. //
  393. //  OUTPUT: None
  394. //
  395. //  RETURN: A pointer to the thread or 0 if not found
  396. //
  397. static THREAD* __pthrFindId(TID tidThread)
  398. {
  399.     for (tCIDLib::CARD4 c4Ind = 0; c4Ind < __c4MaxThreads; c4Ind++)
  400.     {
  401.         if (__apThreads[c4Ind])
  402.         {
  403.             if (__apThreads[c4Ind]->tidThread() == tidThread)
  404.                 return __apThreads[c4Ind];
  405.         }
  406.     }
  407.     return (THREAD*)0;
  408. }
  409.  
  410.  
  411. //
  412. // FUNCTION/METHOD NAME: __pthrFindName(strName)
  413. //
  414. // DESCRIPTION:
  415. //
  416. //  This method will find the named thread in the list.
  417. // ---------------------------------------
  418. //   INPUT: strName is the name of the thread to look for
  419. //
  420. //  OUTPUT: None
  421. //
  422. //  RETURN: A pointer to the thread or 0 if not found
  423. //
  424. static THREAD* __pthrFindName(const STRGBUF& strName)
  425. {
  426.     for (tCIDLib::CARD4 c4Ind = 0; c4Ind < __c4MaxThreads; c4Ind++)
  427.     {
  428.         if (__apThreads[c4Ind])
  429.         {
  430.             if (__apThreads[c4Ind]->strName() == strName)
  431.                 return __apThreads[c4Ind];
  432.         }
  433.     }
  434.     return 0;
  435. }
  436.  
  437.  
  438. //
  439. // FUNCTION/METHOD NAME: __ThreadException(pRep, pReg, pContext, pDummy)
  440. //
  441. // DESCRIPTION:
  442. //
  443. //  This is an exception handler that is installed for every thread. It
  444. //  watches for exceptions that indicate the thread is dying. If so, it
  445. //  clears the __bRunning flag in the thread's thread object. It is installed
  446. //  on every thread started below in __ThreadStart.
  447. //
  448. //  This guy is a friend of THREAD so it can call the protected method
  449. //  required to clear the thread info.
  450. // ---------------------------------------
  451. //   INPUT: pRep is the exception report record
  452. //          pReg is the exception registration record
  453. //          pContext contains context information for this exception
  454. //          pDummy is a pointer to magic info which we ignore.
  455. //
  456. //  OUTPUT: None
  457. //
  458. //  RETURN: We return the correct code according to whether we handle the
  459. //              exception or ignore it.
  460. //
  461. APIRET APIENTRY
  462.     __ThreadException(  struct _EXCEPTIONREPORTRECORD*          pRep
  463.                         , struct _EXCEPTIONREGISTRATIONRECORD*  pReg
  464.                         , struct _CONTEXT*                      pContext
  465.                         , tCIDLib::VOID*                        pDummy)
  466. {
  467.     // Make the compiler happy
  468.     pDummy; pContext;
  469.  
  470.     // See if we are exiting
  471.     if ((pRep->fHandlerFlags & EH_EXIT_UNWIND)
  472.     ||  (pRep->fHandlerFlags & EH_UNWINDING)
  473.     ||  (pRep->fHandlerFlags & EH_NESTED_CALL))
  474.     {
  475.         return XCPT_CONTINUE_SEARCH;
  476.     }
  477.     
  478.     //
  479.     //  If a terminate then clean up the thread object by calling _Exiting. If
  480.     //  a fatal exception, then log to file. Don't do guard page exceptions
  481.     //  because they are expected and will be handled for us.
  482.     //
  483.     if ((pRep->ExceptionNum == XCPT_PROCESS_TERMINATE)
  484.     ||  (pRep->ExceptionNum == XCPT_ASYNC_PROCESS_TERMINATE))
  485.     {
  486.         // Call the internal exiting method to clean up thread flags
  487.         ((CIDTHREADEXP*)pReg)->pthrThis->_Exiting();
  488.     }
  489.      else if (((pRep->ExceptionNum & 0xC0000000) == 0xC0000000)
  490.           &&   (pRep->ExceptionNum != XCPT_GUARD_PAGE_VIOLATION)
  491.           &&   (pRep->ExceptionNum != XCPT_FLOAT_UNDERFLOW)
  492.           &&   (pRep->ExceptionNum != XCPT_FLOAT_OVERFLOW))
  493.     {
  494.         // Log the exception info
  495.         _DumpExceptInfo(pRep, pReg, pContext);
  496.     }
  497.     return XCPT_CONTINUE_SEARCH;
  498. }
  499.  
  500.  
  501. //
  502. // FUNCTION/METHOD NAME: __ThreadStart(pStart)
  503. //
  504. // DESCRIPTION:
  505. //
  506. //  This method will call the thread's function and pass it a pointer to the
  507. //  thread object and to the user passed data buffer. When the thread function
  508. //  returns, control will come back here and we will turn off the _bRunning
  509. //  flag.
  510. //
  511. //  On entry, we block on the thread's sync semaphore until the thread that
  512. //  started us is ready. This is done in the bStart() method, below.
  513. // ---------------------------------------
  514. //   INPUT: pStart is a pointer to a STARTDATA data structure.
  515. //
  516. //  OUTPUT: None
  517. //
  518. //  RETURN: None
  519. //
  520. tCIDLib::VOID THREADFUNC __ThreadStart(tCIDLib::VOID* pData)
  521. {
  522.     tCIDLib::CARD4  c4Err;
  523.     CIDTHREADEXP    ThreadExp;
  524.     THREAD*         pThread;
  525.     tCIDLib::VOID*  pUserData;
  526.  
  527.     // Look at the passed pointer as a start data structure
  528.     STARTDATA*  pStart  = (STARTDATA*)(pData);
  529.  
  530.     //
  531.     //  Get the thread pointer and data pointer out of the passed data buffer
  532.     //  then delete it. It had to be allocated because of the async nature
  533.     //  of the thread startup.
  534.     //
  535.     pThread     = pStart->pThread;
  536.     pUserData   = pStart->pData;
  537.  
  538.     // Delete the start data buffer
  539.     delete pStart;
  540.  
  541.     //
  542.     //  Make sure that main() has completed, which also means that all of the
  543.     //  global data is created
  544.     //
  545.     _ThreadSyncStart();
  546.  
  547.     // Wait on the sync semaphore
  548.     DosWaitEventSem(pThread->__hevSync, SEM_INDEFINITE_WAIT);
  549.  
  550.     // Register the standard thread exception
  551.     ThreadExp.pthrThis      = pThread;
  552.     ThreadExp.pfnHandler    = (PFN)__ThreadException;
  553.     c4Err = DosSetExceptionHandler((PEXCEPTIONREGISTRATIONRECORD)&ThreadExp);
  554.  
  555.     if (c4Err)
  556.     {
  557.         facCIDLib.LogSysErr(__FILE__
  558.                             , "__ThreadStart"
  559.                             , __LINE__
  560.                             , c4Err
  561.                             , "Could not register standard exception handler"
  562.                             , tCIDLib::eSEV_PROCESS_FATAL);
  563.     }
  564.  
  565.     // Call the init method
  566.     pThread->Init();
  567.  
  568.     // Call the thread function
  569.     tCIDLib::eBOOL bFatalExit = tCIDLib::eFALSE;
  570.     try
  571.     {
  572.         pThread->__pfnFunc(*pThread, pUserData);
  573.     }
  574.     catch (CIDERROR errRT)
  575.     {
  576.         //
  577.         //  DO NOT log a fatal error here or we trigger another throw
  578.         //
  579.         facCIDLib.LogMsg(   __FILE__
  580.                             , "__ThreadStart"
  581.                             , __LINE__
  582.                             , "Unhandled runtime error. See ErrorInf.CIDLib"
  583.                             , tCIDLib::eSEV_INFORMATION);
  584.         bFatalExit = tCIDLib::eTRUE;
  585.     }
  586.  
  587.     // Remove the exception handler
  588.     c4Err = DosUnsetExceptionHandler((PEXCEPTIONREGISTRATIONRECORD)&ThreadExp);
  589.  
  590.     if (c4Err)
  591.     {
  592.         // !!! Don't log a fatal error here because it would throw again
  593.         facCIDLib.LogSysErr(__FILE__
  594.                             , "__ThreadStart"
  595.                             , __LINE__
  596.                             , c4Err
  597.                             , "Could not deregister standard exception handler"
  598.                             , tCIDLib::eSEV_WARNING);
  599.         bFatalExit = tCIDLib::eTRUE;
  600.     }
  601.  
  602.     //
  603.     //  If the __bRunning flag is still set, then we need to call _Exiting()
  604.     //  to clean up. Otherwise, the exception handler has already done it
  605.     //  for us.
  606.     //
  607.     if (pThread->__bRunning)
  608.         pThread->_Exiting();
  609.  
  610.     //
  611.     //  If this is not thread 0 exiting and bFatalExit is eTRUE, then shut
  612.     //  down the program.
  613.     //
  614.     if (pThread->__tidThread && bFatalExit)
  615.         facCIDLib.ExitProcess(tCIDLib::eEXIT_FATAL);
  616. }
  617.  
  618.  
  619. // -----------------------------------------------------------------------------
  620. //  Functions for intrafacility use
  621. // -----------------------------------------------------------------------------
  622.  
  623. //
  624. // FUNCTION/METHOD NAME: _bInitProcess()
  625. //
  626. // DESCRIPTION:
  627. //
  628. //  This is the init function for this module. It is called from the Facility's
  629. //  main module when the Facility is loaded. This guy needs to create some local
  630. //  data, mainly the synchronization semphore. It also adds an entry in the
  631. //  thread list for this start up thread.
  632. // ---------------------------------------
  633. //   INPUT: None
  634. //
  635. //  OUTPUT: None
  636. //
  637. //  RETURN: eTRUE if successful, else eFALSE.
  638. //
  639. tCIDLib::eBOOL _bInitProcess()
  640. {
  641.     tCIDLib::CARD4  c4Err;
  642.  
  643.     // Create the linked list access semaphore
  644.     c4Err = DosCreateMutexSem(0, &__hmtxListAccess, 0, 0);
  645.     if (c4Err)
  646.     {
  647.         // Do a text popup and return eFALSE
  648.         _ERRPOPUP_( "_bInitProcess"
  649.                     , "Could not create list access semaphore"
  650.                     , c4Err);
  651.         return tCIDLib::eFALSE;
  652.     }
  653.  
  654.     //
  655.     //  We need to create a dummy node for the main thread, which is the one
  656.     //  that we are running under now. This function is a friend of THREAD, so
  657.     //  it can call the default constructor and then fill it in.
  658.     //
  659.     __pthrMain = new THREAD();
  660.     PTIB            pTBlk;
  661.     PPIB            pPBlk;
  662.  
  663.     DosGetInfoBlocks(&pTBlk, &pPBlk);
  664.     __pthrMain->__strName       = "_Thread1_";
  665.     __pthrMain->__tidThread     = pTBlk->tib_ptib2->tib2_ultid;
  666.     __pthrMain->__bRunning      = tCIDLib::eTRUE;
  667.  
  668.     // Add the new thread to the list
  669.     __AddThread(__pthrMain);
  670.  
  671.     // Set the local inited flag
  672.     __bInited = tCIDLib::eTRUE;
  673.  
  674.     return tCIDLib::eTRUE;
  675. }
  676.  
  677.  
  678. //
  679. // FUNCTION/METHOD NAME: _pthrCurrent()
  680. //
  681. // DESCRIPTION:
  682. //
  683. //  This function will get the thread id of the current thread, lock the
  684. //  thread list, look up the thread, and return a pointer to the thread
  685. //  object.
  686. //
  687. //  NOTE:   If the thread is not found, then this is a major error, so we log
  688. //          an error and abort. Note also that we cannot log from this call
  689. //          because we will get into a big circle jerk with the logging
  690. //          mechanism and overflow the stack.
  691. // ---------------------------------------
  692. //   INPUT: None
  693. //
  694. //  OUTPUT: None
  695. //
  696. //  RETURN: A pointer to the thread object of the calling thread.
  697. //
  698. THREAD* _pthrCurrent()
  699. {
  700.     tCIDLib::CARD4  c4Err;
  701.     PTIB            pTBlk;
  702.     PPIB            pPBlk;
  703.     THREAD*         pThread;
  704.     TID             tidTmp;
  705.  
  706.     if (!__bInited)
  707.     {
  708.         //
  709.         //  Return a bogus thread object, since we know that this is happening
  710.         //  due to a start up error and he just wants a thread name. He is
  711.         //  going to abort anyway so this memory will not get lost.
  712.         //
  713.         return new THREAD("_Thread1_", 0);
  714.     }
  715.  
  716.     // Get access to the thread list
  717.     c4Err = DosRequestMutexSem(__hmtxListAccess, 2000);
  718.     if (c4Err)
  719.     {
  720.         _ERRPOPUP_( "_pthrCurrent"
  721.                     , "An error occured while locking thread list access sem"
  722.                     , c4Err);
  723.         facCIDLib.ExitProcess(tCIDLib::eEXIT_FATAL);
  724.     }
  725.  
  726.     //
  727.     //  Query the info blocks and store away some information from them.
  728.     //
  729.     c4Err = DosGetInfoBlocks(&pTBlk, &pPBlk);
  730.     if (c4Err)
  731.     {
  732.         _ERRPOPUP_( "_pthrCurrent"
  733.                     , "An error occured while getting thread information"
  734.                     , c4Err);
  735.         facCIDLib.ExitProcess(tCIDLib::eEXIT_FATAL);
  736.     }
  737.     tidTmp = pTBlk->tib_ptib2->tib2_ultid;
  738.  
  739.     // Look for this thread id
  740.     pThread = __pthrFindId(tidTmp);
  741.  
  742.     // Release the list access semaphore
  743.     DosReleaseMutexSem(__hmtxListAccess);
  744.  
  745.     //
  746.     //  If we did get a nul node back, then a thread with that id does not
  747.     //  exist. This is a major error, so we need to abort.
  748.     //
  749.     if (!pThread)
  750.     {
  751.         _ERRPOPUP_( "_pthrCurrent"
  752.                     , "The calling thread was not found in the thread list"
  753.                     , 0);
  754.         facCIDLib.ExitProcess(tCIDLib::eEXIT_FATAL);
  755.     }
  756.  
  757.     // We did find the node, so return the thread object in the node
  758.     return pThread;
  759. }
  760.  
  761.  
  762.  
  763. // -----------------------------------------------------------------------------
  764. //   CLASS: THREAD
  765. //  PREFIX: thr
  766. //
  767. //  This class provides thread support.
  768. // -----------------------------------------------------------------------------
  769.  
  770. // -----------------------------------------------------------------------------
  771. //  THREAD: Constructors and Destructors
  772. // -----------------------------------------------------------------------------
  773.  
  774. //
  775. // FUNCTION/METHOD NAME: THREAD(strName, pfnFunc, c4StackSz, pData, bAutoStart)
  776. //
  777. // DESCRIPTION:
  778. //
  779. //  This is the only public constructor for thread objects. The calls must pass
  780. //  a thread name, a pointer to a thread function, a pointer to a data buffer
  781. //  which will be passed to the thread function, and an optional autostart flag.
  782. //  The thread object is placed into the linked list of current threads, after
  783. //  checking to make sure that a thread object with the same name does not
  784. //  already exist.
  785. // ---------------------------------------
  786. //   INPUT: strName is the name to give to the thread
  787. //          pfnFunc is the address of the thread's function
  788. //          c4StackSz is the size of the stack to give to the thread. If less
  789. //              than 8K, it will be bumped up to that amount.
  790. //          pData is an optional pointer to a data buffer that is passed to the
  791. //              thread function.
  792. //          bAutoStart is an optional autostart flag. If eTRUE, then thread will
  793. //              be started immediately. It defaults eFALSE, so that the client
  794. //              app needs to call bStart() to start it.
  795. //          bSelfPrio indicates whether this thread should prevent any other
  796. //              thread from setting his priority.
  797. //
  798. //  OUTPUT: None
  799. //
  800. //  RETURN: None
  801. //
  802. PROCEXP THREAD::THREAD( const   STRG32&                 strName
  803.                         ,       tCIDLib::pfnTHREADFUNC  pfnFunc
  804.                         ,       tCIDLib::CARD4          c4StackSz
  805.                         ,       tCIDLib::VOID*          pData
  806.                         ,       tCIDLib::eBOOL          bAutoStart
  807.                         ,       tCIDLib::eBOOL          bSelfPrio) :
  808.  
  809.     __bRunning(tCIDLib::eFALSE)
  810.     , __bSelfPrio(bSelfPrio)
  811.     , __bSyncRequest(tCIDLib::eFALSE)
  812.     , __c4StackSz(c4StackSz)
  813.     , __perrLast(0)
  814.     , __pfnFunc(pfnFunc)
  815.     , __pfnOnExit(0)
  816.     , __pData(pData)
  817.     , __strName(strName)
  818. {
  819.     tCIDLib::CARD4  c4Err;
  820.  
  821.     // Make sure that the stack size is not less than 8K
  822.     if (__c4StackSz < 8192)
  823.     {
  824.         __c4StackSz = 8192;
  825.     }
  826.  
  827.     // Bump up the count of threads
  828.     _pmtrLIBCore->OffsetCard(tCIDLib_::eCOREMETRIC_THREADCNT, 1);
  829.  
  830.     // Get access to the thread list
  831.     LSTLOCK Lock("THREAD::THREAD");
  832.  
  833.     //
  834.     //  Check the thread list and see if there is already a thread with
  835.     //  this name.
  836.     //
  837.     THREAD* pThread = __pthrFindName(__strName);
  838.  
  839.     // If we get a thread back, then a thread with that name already exists
  840.     if (pThread)
  841.     {
  842.        facCIDLib.LogErr(__FILE__
  843.                         , "THREAD"
  844.                         , __LINE__
  845.                         , LIBERR_PRC_THREAD_EXISTS
  846.                         , tCIDLib::eSEV_PROCESS_FATAL
  847.                         , tCIDLib::eCLASS_BADPARMS
  848.                         , strName);
  849.     }
  850.  
  851.     // Add a new node to the list.
  852.     __AddThread(this);
  853.  
  854.     // Create this thread's sync semaphore
  855.     c4Err = DosCreateEventSem(0, &__hevSync, 0, 0);
  856.     if (c4Err)
  857.     {
  858.         // Fill in the last error member with the error info and log it
  859.         facCIDLib.LogSysErr(__FILE__
  860.                             , "THREAD"
  861.                             , __LINE__
  862.                             , c4Err
  863.                             , "Could not create the sync semaphore"
  864.                             , tCIDLib::eSEV_PROCESS_FATAL);
  865.     }
  866.  
  867.     // Create this thread's sync response semaphore.
  868.     c4Err = DosCreateEventSem(0, &__hevResponse, 0, 0);
  869.     if (c4Err)
  870.     {
  871.         // Fill in the last error member with the error info and log it
  872.         facCIDLib.LogSysErr(__FILE__
  873.                             , "THREAD"
  874.                             , __LINE__
  875.                             , c4Err
  876.                             , "Could not create the sync response semaphore"
  877.                             , tCIDLib::eSEV_PROCESS_FATAL);
  878.     }
  879.  
  880.     //
  881.     //  Create all of the per-thread objects for this thread. If there are not
  882.     //  any then nothing will happen. Zero out the array first so we can do a
  883.     //  sanity check later when we delete all of the objects.
  884.     //
  885.     memset(__apobjList, 0, sizeof(__apobjList));
  886.     __CreatePerThreadObjs();
  887.  
  888.     // If the auto start flag is set, then call the start method
  889.     if (bAutoStart)
  890.         bStart();
  891. }
  892.  
  893.  
  894. //
  895. // FUNCTION/METHOD NAME: THREAD()
  896. //
  897. // DESCRIPTION:
  898. //
  899. //  This default constructor is protected so it can't be called by the general
  900. //  public.
  901. // ---------------------------------------
  902. //   INPUT: None
  903. //
  904. //  OUTPUT: None
  905. //
  906. //  RETURN: None
  907. //
  908. PROCEXP THREAD::THREAD() :
  909.  
  910.     __bRunning(tCIDLib::eTRUE)
  911.     , __bSelfPrio(tCIDLib::eFALSE)
  912.     , __bSyncRequest(tCIDLib::eFALSE)
  913.     , __c4StackSz(0)
  914.     , __pData(0)
  915.     , __perrLast(0)
  916.     , __pfnFunc(0)
  917.     , __tidThread(0)
  918. {
  919.     _pmtrLIBCore->OffsetCard(tCIDLib_::eCOREMETRIC_THREADCNT, 1);
  920.  
  921.     memset(__apobjList, 0, sizeof(__apobjList));
  922.     __CreatePerThreadObjs();
  923.     DosCreateEventSem(0, &__hevSync, 0, 0);
  924.     DosCreateEventSem(0, &__hevResponse, 0, 0);
  925. }
  926.  
  927. THREAD::THREAD(const THREAD& thrSrc)
  928. {
  929.     facCIDLib.LogMsg(   __FILE__
  930.                         , "THREAD"
  931.                         , __LINE__
  932.                         , "Copy constructor cannot be called"
  933.                         , tCIDLib::eSEV_PROCESS_FATAL);
  934. }
  935.  
  936.  
  937. //
  938. // FUNCTION/METHOD NAME: ~THREAD()
  939. //
  940. // DESCRIPTION:
  941. //
  942. //  This is the destructor for threads. It cleans up the sync semaphore and
  943. //  removes this thread object from the linked list of running threads.
  944. // ---------------------------------------
  945. //   INPUT: None
  946. //
  947. //  OUTPUT: None
  948. //
  949. //  RETURN: None
  950. //
  951. PROCEXP THREAD::~THREAD()
  952. {
  953.     tCIDLib::CARD4  c4Err;
  954.  
  955.     // Bump down the thread count
  956.     _pmtrLIBCore->OffsetCard(tCIDLib_::eCOREMETRIC_THREADCNT, -1);
  957.  
  958.     // Get access to the thread list
  959.     LSTLOCK Lock("~THREAD");
  960.  
  961.     // Remove this thread from the running list
  962.     if (!__bRemoveId(__tidThread))
  963.     {
  964.         facCIDLib.LogErr(   __FILE__
  965.                             , "~THREAD"
  966.                             , __LINE__
  967.                             , LIBERR_PRC_THREAD_NOT_FOUND
  968.                             , tCIDLib::eSEV_PROCESS_FATAL
  969.                             , tCIDLib::eCLASS_INTERNAL
  970.                             , __strName);
  971.     }
  972.  
  973.     // If the thread is running, then kill it
  974.     if (__bRunning)
  975.     {
  976.         c4Err = DosKillThread(__tidThread);
  977.         if (c4Err)
  978.         {
  979.             STRG128 strTmp("Could not kill thread ");
  980.             strTmp << __strName;
  981.  
  982.             facCIDLib.LogSysErr(__FILE__
  983.                                 , "~THREAD"
  984.                                 , __LINE__
  985.                                 , c4Err
  986.                                 , strTmp
  987.                                 , tCIDLib::eSEV_PROCESS_FATAL);
  988.         }
  989.     }
  990.  
  991.     // Close the sync semaphore
  992.     c4Err = DosCloseEventSem(__hevSync);
  993.     if (c4Err)
  994.     {
  995.         facCIDLib.LogSysErr(__FILE__
  996.                             , "~THREAD"
  997.                             , __LINE__
  998.                             , c4Err
  999.                             , "Could not close the sync semaphore"
  1000.                             , tCIDLib::eSEV_PROCESS_FATAL);
  1001.     }
  1002.  
  1003.     // Close the sync response semaphore
  1004.     c4Err = DosCloseEventSem(__hevResponse);
  1005.     if (c4Err)
  1006.     {
  1007.         facCIDLib.LogSysErr(__FILE__
  1008.                             , "~THREAD"
  1009.                             , __LINE__
  1010.                             , c4Err
  1011.                             , "Could not close the sync response semaphore"
  1012.                             , tCIDLib::eSEV_PROCESS_FATAL);
  1013.     }
  1014.  
  1015.     // Destroy the per-thread objects if any
  1016.     __DestroyPerThreadObjs();
  1017.  
  1018.     // Delete the last error object if any
  1019.     if (__perrLast)
  1020.         delete __perrLast;
  1021. }
  1022.  
  1023.  
  1024. // -----------------------------------------------------------------------------
  1025. //  THREAD: Public, static methods
  1026. // -----------------------------------------------------------------------------
  1027.  
  1028. //
  1029. // FUNCTION/METHOD NAME: c4RegisterPerThreadObj(strObjName, pfnPerThread)
  1030. //
  1031. // DESCRIPTION:
  1032. //
  1033. //  This method will register a per-thread object. The information is placed
  1034. //  into the master per-thread object list, above. When thread objects are
  1035. //  created, the callout for each entry in the list is called to allow the
  1036. //  subsystems to create the object. This guy will also retofit this object to
  1037. //  any existing threads.
  1038. // ---------------------------------------
  1039. //   INPUT: strObjName is the name of the object create. It is just a
  1040. //              descriptive string for logging messages and to insure that the
  1041. //              clients do not accidentally register the same object twice.
  1042. //          pfnPerThread is a pointer to a callout that will be made to let
  1043. //              the caller create the object as desired.
  1044. //
  1045. //  OUTPUT: None
  1046. //
  1047. //  RETURN: A handle that the subsystem can use to get access to the object
  1048. //              later.
  1049. //
  1050. tCIDLib::CARD4 THREAD::c4RegisterPerThreadObj
  1051.                             (   const   STRG64&                 strObjName
  1052.                                 ,       tCIDLib::pfnPTHRFUNC    pfnPerThread)
  1053. {
  1054.     // Get access to the thread list
  1055.     LSTLOCK Lock("THREAD::_c4RegisterPerThreadObj");
  1056.  
  1057.     //
  1058.     //  Add a new record. It will make sure the name is unique. If so it will
  1059.     //  add it to the master list.
  1060.     //
  1061.     tCIDLib::CARD4 c4Handle = __c4AddPTObj(strObjName, pfnPerThread);
  1062.  
  1063.     //
  1064.     //  Loop through any existing threads and retrofit this object to them
  1065.     //  all so we don't have to worry about different lists for every thread.
  1066.     //
  1067.     for (tCIDLib::CARD4 c4Ind = 0; c4Ind < __c4MaxThreads; c4Ind++)
  1068.     {
  1069.         if (__apThreads[c4Ind])
  1070.         {
  1071.             __apThreads[c4Ind]->__apobjList[c4Handle]
  1072.                                 = __aPTObjs[c4Handle].pfnPerThread(c4Handle);
  1073.         }
  1074.     }
  1075.     return c4Handle;
  1076. }
  1077.  
  1078.  
  1079. // -----------------------------------------------------------------------------
  1080. //  THREAD: Public, virtual methods
  1081. // -----------------------------------------------------------------------------
  1082.  
  1083. //
  1084. // FUNCTION/METHOD NAME: operator=(thrSrc)
  1085. //
  1086. // DESCRIPTION:
  1087. //
  1088. //  This method will handle assignment of one thread object to another.
  1089. // ---------------------------------------
  1090. //   INPUT: thrSrc is the source thread to copy.
  1091. //
  1092. //  OUTPUT: None
  1093. //
  1094. //  RETURN: A reference to this object.
  1095. //
  1096. THREAD& THREAD::operator=(const THREAD& thrSrc)
  1097. {
  1098.     facCIDLib.LogMsg(   __FILE__
  1099.                         , "THREAD::operator="
  1100.                         , __LINE__
  1101.                         , "Assignment operator cannot be called"
  1102.                         , tCIDLib::eSEV_PROCESS_FATAL);
  1103.  
  1104.     // Make compiler happy
  1105.     return *this;
  1106. }
  1107.  
  1108.  
  1109. // -----------------------------------------------------------------------------
  1110. //  THREAD: Public, inherited methods
  1111. // -----------------------------------------------------------------------------
  1112.  
  1113. //
  1114. // FUNCTION/METHOD NAME: FormatToStr(strbDest) const
  1115. //
  1116. // DESCRIPTION:
  1117. //
  1118. //  This method will format this object into the destination string. For this
  1119. //  class, we just format some debugging info into the string.
  1120. // ---------------------------------------
  1121. //   INPUT: None
  1122. //
  1123. //  OUTPUT: strbDest is the destination string to format into
  1124. //
  1125. //  RETURN: None
  1126. //
  1127. tCIDLib::VOID PROCEXP THREAD::FormatToStr(STRGBUF& strbDest) const
  1128. {
  1129.     strbDest << "Thread=" << __strName << ",Running=" << __bRunning;
  1130. }
  1131.  
  1132.  
  1133. // -----------------------------------------------------------------------------
  1134. //  THREAD: Public, non-virtual methods
  1135. // -----------------------------------------------------------------------------
  1136.  
  1137. //
  1138. // FUNCTION/METHOD NAME: bProbeBuffer(pBuffer, c4Len)
  1139. //
  1140. // DESCRIPTION:
  1141. //
  1142. //  This method will probe the passed buffer to see if it will cause an
  1143. //  exception.
  1144. // ---------------------------------------
  1145. //   INPUT: pBuffer is the buffer to probe
  1146. //          c4Len is the length of the buffer
  1147. //
  1148. //  OUTPUT: None
  1149. //
  1150. //  RETURN: eTRUE if the buffer is ok, else eFALSE if it caused an exception
  1151. //
  1152. struct          BUFPROBEEXP
  1153. {
  1154.     PVOID       pNext;
  1155.     PFN         pfnHandler;
  1156.     jmp_buf     JmpInfo;
  1157. };
  1158. APIRET APIENTRY __ProbeException(   EXCEPTIONREPORTRECORD*          pRep
  1159.                                     , EXCEPTIONREGISTRATIONRECORD*  pReg
  1160.                                     , CONTEXTRECORD*                pContext
  1161.                                     , tCIDLib::VOID*                pDummy)
  1162. {
  1163.     // Make the compiler happy
  1164.     pDummy; pContext;
  1165.  
  1166.     // See if we are exiting
  1167.     if ((pRep->fHandlerFlags & EH_EXIT_UNWIND)
  1168.     ||  (pRep->fHandlerFlags & EH_UNWINDING)
  1169.     ||  (pRep->fHandlerFlags & EH_NESTED_CALL))
  1170.     {
  1171.         return XCPT_CONTINUE_SEARCH;
  1172.     }
  1173.     
  1174.     // If an access violation, then do the long jump to stored state
  1175.     if (pRep->ExceptionNum == XCPT_ACCESS_VIOLATION)
  1176.         longjmp(((BUFPROBEEXP*)pReg)->JmpInfo, pRep->ExceptionNum);
  1177.  
  1178.     // Not an access violation so let it go
  1179.     return XCPT_CONTINUE_SEARCH;
  1180. }
  1181.  
  1182. tCIDLib::eBOOL PROCEXP THREAD::bProbeBuffer(tCIDLib::VOID*      pBuffer
  1183.                                             , tCIDLib::CARD4    c4Len)
  1184. {
  1185.     BUFPROBEEXP     BufProbe;
  1186.     tCIDLib::CARD4  c4Err, c4Except;
  1187.  
  1188.     BufProbe.pfnHandler = (PFN)__ProbeException;
  1189.     c4Err = DosSetExceptionHandler((PEXCEPTIONREGISTRATIONRECORD)&BufProbe);
  1190.     if (c4Err)
  1191.     {
  1192.         facCIDLib.LogSysErr(__FILE__
  1193.                             , "bProbeBuffer"
  1194.                             , __LINE__
  1195.                             , c4Err
  1196.                             , "Could not register buffer probe exception"
  1197.                             , tCIDLib::eSEV_PROCESS_FATAL);
  1198.     }
  1199.  
  1200.     // Store the jump info
  1201.     c4Except = setjmp(BufProbe.JmpInfo);
  1202.  
  1203.     if (c4Except)
  1204.     {
  1205.         c4Err =
  1206.             DosUnsetExceptionHandler((PEXCEPTIONREGISTRATIONRECORD)&BufProbe);
  1207.         if (c4Except == XCPT_ACCESS_VIOLATION)
  1208.             return tCIDLib::eFALSE;
  1209.     }
  1210.  
  1211.     // Do the buffer probing
  1212.     tCIDLib::CARD1  c1Tmp;
  1213.     tCIDLib::CARD1* pc1Buf = (tCIDLib::CARD1*)pBuffer;
  1214.     c1Tmp = pc1Buf[0];
  1215.     c1Tmp = pc1Buf[c4Len-1];
  1216.  
  1217.     // Make the compiler happy
  1218.     c1Tmp;
  1219.  
  1220.     c4Err = DosUnsetExceptionHandler((PEXCEPTIONREGISTRATIONRECORD)&BufProbe);
  1221.     if (c4Err)
  1222.     {
  1223.         facCIDLib.LogSysErr(__FILE__
  1224.                             , "bProbeBuffer"
  1225.                             , __LINE__
  1226.                             , c4Err
  1227.                             , "Could not deregister buffer probe exception"
  1228.                             , tCIDLib::eSEV_PROCESS_FATAL);
  1229.     }
  1230.  
  1231.     // Went ok, so return eTRUE
  1232.     return tCIDLib::eTRUE;
  1233. }
  1234.  
  1235.  
  1236. //
  1237. // FUNCTION/METHOD NAME: bRelease()
  1238. //
  1239. // DESCRIPTION:
  1240. //
  1241. //  This method is called by another thread that has sync'd with this thread.
  1242. //  Calling this method will release this thread again.
  1243. // ---------------------------------------
  1244. //   INPUT: None
  1245. //
  1246. //  OUTPUT: None
  1247. //
  1248. //  RETURN: eTRUE if successful, else eFALSE.
  1249. //
  1250. tCIDLib::eBOOL PROCEXP THREAD::bRelease()
  1251. {
  1252.     LSTLOCK Lock("THREAD::bRelease");
  1253.  
  1254.     // If there is not an outstanding sync request, then this call is bogus
  1255.     if (!__bSyncRequest)
  1256.     {
  1257.         facCIDLib.LogMsg(   __FILE__
  1258.                             , "THREAD::bRelease"
  1259.                             , __LINE__
  1260.                             , "There was no outstanding thread sync to release"
  1261.                             , tCIDLib::eSEV_PROCESS_FATAL);
  1262.     }
  1263.  
  1264.     // Make sure the caller is the one that did the sync
  1265.     #if     DEBUGLOW
  1266.     THREAD* pthrCaller = facCIDLib.pthrCaller();
  1267.     if (pthrCaller->tidThread() != __tidSyncReq)
  1268.     {
  1269.         facCIDLib.LogErr(   __FILE__
  1270.                             , "THREAD::bRelease"
  1271.                             , __LINE__
  1272.                             , LIBERR_PRC_NOT_SYNCREQ
  1273.                             , tCIDLib::eSEV_PROCESS_FATAL
  1274.                             , tCIDLib::eCLASS_APPERROR
  1275.                             , pthrCaller->strName()
  1276.                             , __strName);
  1277.     }
  1278.     #endif
  1279.  
  1280.     // Clear the sync request flag
  1281.     __bSyncRequest = tCIDLib::eFALSE;
  1282.  
  1283.     // Post the sync semaphore to let the thread go
  1284.     tCIDLib::CARD4  c4Err = DosPostEventSem(__hevSync);
  1285.  
  1286.     if (c4Err)
  1287.     {
  1288.         facCIDLib.LogSysErr(__FILE__
  1289.                             , "THREAD::bRelease"
  1290.                             , __LINE__
  1291.                             , c4Err
  1292.                             , "Could not post the sync semaphore"
  1293.                             , tCIDLib::eSEV_PROCESS_FATAL);
  1294.     }
  1295.     return tCIDLib::eTRUE;
  1296. }
  1297.  
  1298.  
  1299. //
  1300. // FUNCTION/METHOD NAME: bStart()
  1301. //
  1302. // DESCRIPTION:
  1303. //
  1304. //  This method will start the thread running if it is not already running.
  1305. //  Note that we want to exit here with the __hevSync semaphore posted so
  1306. //  that the thread will not block prematurely when it calls bSync().
  1307. // ---------------------------------------
  1308. //   INPUT: None
  1309. //
  1310. //  OUTPUT: None
  1311. //
  1312. //  RETURN: eTRUE if successful, else eFALSE.
  1313. //
  1314. tCIDLib::eBOOL PROCEXP THREAD::bStart()
  1315. {
  1316.     if (__bRunning)
  1317.     {
  1318.         facCIDLib.LogErr(   __FILE__
  1319.                             , "THREAD::bStart"
  1320.                             , __LINE__
  1321.                             , LIBERR_PRC_THREAD_RUNNING
  1322.                             , tCIDLib::eSEV_WARNING
  1323.                             , tCIDLib::eCLASS_ALREADY
  1324.                             , __strName);
  1325.         return tCIDLib::eFALSE;
  1326.     }
  1327.  
  1328.     // Try to start up the thread
  1329.     tCIDLib::CARD4      c4Err, c4PostCount;
  1330.  
  1331.     // Reset the sync semaphore so it won't go until we tell it to
  1332.     c4Err = DosResetEventSem(__hevSync, &c4PostCount);
  1333.     if ((c4Err) && (c4Err != LIBERR_EVSEM_ALREADY_RESET))
  1334.     {
  1335.         facCIDLib.LogSysErr(__FILE__
  1336.                             , "THREAD::bStart"
  1337.                             , __LINE__
  1338.                             , c4Err
  1339.                             , "Could not reset the sync semaphore"
  1340.                             , tCIDLib::eSEV_PROCESS_FATAL);
  1341.     }
  1342.  
  1343.     // Start the thread and pass the startup data
  1344.     STARTDATA*  pStartData  = new STARTDATA;
  1345.     pStartData->pThread     = this;
  1346.     pStartData->pData       = __pData;
  1347.  
  1348.     //
  1349.     //  Note that the stack address is not used in 32 bit world, so we pass
  1350.     //  0.
  1351.     //
  1352.     int id = _beginthread(__ThreadStart, 0, __c4StackSz, pStartData);
  1353.  
  1354.     if (id < 0)
  1355.     {
  1356.         facCIDLib.LogErr(   __FILE__
  1357.                             , "THREAD::bStart"
  1358.                             , __LINE__
  1359.                             , LIBERR_PRC_THREAD_START_FAILED
  1360.                             , tCIDLib::eSEV_WARNING
  1361.                             , tCIDLib::eCLASS_ALREADY
  1362.                             , __strName);
  1363.         facCIDLib.ExitProcess(tCIDLib::eEXIT_FATAL);
  1364.     }
  1365.  
  1366.     //
  1367.     //  It went ok, so set the running flag, store the id, and post to the
  1368.     //  sync semaphore to let it go.
  1369.     //
  1370.     __tidThread = TID(id);
  1371.     __bRunning  = tCIDLib::eTRUE;
  1372.  
  1373.     c4Err = DosPostEventSem(__hevSync);
  1374.     if (c4Err)
  1375.     {
  1376.         facCIDLib.LogSysErr(__FILE__
  1377.                             , "THREAD::bStart"
  1378.                             , __LINE__
  1379.                             , c4Err
  1380.                             , "Could not post the sync semaphore"
  1381.                             , tCIDLib::eSEV_PROCESS_FATAL);
  1382.     }
  1383.     return tCIDLib::eTRUE;
  1384. }
  1385.  
  1386.  
  1387. //
  1388. // FUNCTION/METHOD NAME: bWaitSync()
  1389. //
  1390. // DESCRIPTION:
  1391. //
  1392. //  Other threads call this method to sync up with this thread. On successful
  1393. //  return, the thread is waiting for the calling thread to call its bRelease
  1394. //  method to let it go again.
  1395. // ---------------------------------------
  1396. //   INPUT: None
  1397. //
  1398. //  OUTPUT: None
  1399. //
  1400. //  RETURN: None
  1401. //
  1402. tCIDLib::eBOOL PROCEXP THREAD::bWaitSync()
  1403. {
  1404.     tCIDLib::CARD4  c4Count;
  1405.  
  1406.     // Make sure that thread does not sync with himself
  1407.     #if     DEBUGLOW
  1408.     if (this == facCIDLib.pthrCaller())
  1409.     {
  1410.         facCIDLib.LogErr(   __FILE__
  1411.                             , "THREAD::bWaitSync"
  1412.                             , __LINE__
  1413.                             , LIBERR_PRC_SYNC_WITH_SELF
  1414.                             , tCIDLib::eSEV_PROCESS_FATAL
  1415.                             , tCIDLib::eCLASS_APPERROR
  1416.                             , __strName);
  1417.     }
  1418.     #endif
  1419.  
  1420.     // If there is already a request, then we can't do it
  1421.     DosEnterCritSec();
  1422.     if (__bSyncRequest)
  1423.     {
  1424.         DosExitCritSec();
  1425.         return tCIDLib::eFALSE;
  1426.     }
  1427.  
  1428.     // Set the flag so we won't be interrupted
  1429.     __bSyncRequest = tCIDLib::eTRUE;
  1430.     DosExitCritSec();
  1431.  
  1432.     // Remember the id of the requester
  1433.     PTIB    pTBlk;
  1434.     PPIB    pPBlk;
  1435.     DosGetInfoBlocks(&pTBlk, &pPBlk);
  1436.     __tidSyncReq = pTBlk->tib_ptib2->tib2_ultid;
  1437.  
  1438.     //
  1439.     //  Reset the sync semaphore to cause the thread to block the next time
  1440.     //  it calls Sync().
  1441.     //
  1442.     DosResetEventSem(__hevSync, &c4Count);
  1443.  
  1444.     //
  1445.     //  Block on the response semaphore, which should already be reset. When
  1446.     //  we awake, the thread is blocked on the sync semaphore. So we can
  1447.     //  return.
  1448.     //
  1449.     DosWaitEventSem(__hevResponse, SEM_INDEFINITE_WAIT);
  1450.  
  1451.     return tCIDLib::eTRUE;
  1452. }
  1453.  
  1454.  
  1455. //
  1456. // FUNCTION/METHOD NAME: ClrLastErr()
  1457. //
  1458. // DESCRIPTION:
  1459. //
  1460. //  Clears the last error on this thread.
  1461. // ---------------------------------------
  1462. //   INPUT: None
  1463. //
  1464. //  OUTPUT: None
  1465. //
  1466. //  RETURN: None
  1467. //
  1468. tCIDLib::VOID PROCEXP THREAD::ClrLastErr()
  1469. {
  1470.     // Abort if the caller is not this thread
  1471.     _CheckCallerIsSelf("ClrLastErr");
  1472.  
  1473.     if (__perrLast)
  1474.     {
  1475.         delete (__perrLast);
  1476.         __perrLast = 0;
  1477.     }
  1478. }
  1479.  
  1480.  
  1481. //
  1482. // FUNCTION/METHOD NAME: c4LastErrCode() const
  1483. //
  1484. // DESCRIPTION:
  1485. //
  1486. //  This method will return the error code of the current error on this
  1487. //  thread. It then deletes the error info. If no info is available the
  1488. //  return value is 0.
  1489. // ---------------------------------------
  1490. //   INPUT: None
  1491. //
  1492. //  OUTPUT: None
  1493. //
  1494. //  RETURN: The last error code
  1495. //
  1496. tCIDLib::CARD4 PROCEXP THREAD::c4LastErrCode()
  1497. {
  1498.     // Abort if the caller is not this thread
  1499.     _CheckCallerIsSelf("c4LastErrCode");
  1500.  
  1501.     // Lock the thread list
  1502.     LSTLOCK Lock("THREAD::cLastErrCode");
  1503.  
  1504.     tCIDLib::CARD4  c4Code;
  1505.     if (__perrLast)
  1506.     {
  1507.         c4Code = __perrLast->c4ErrCode();
  1508.         delete __perrLast;
  1509.         __perrLast = 0;
  1510.     }
  1511.     return c4Code;
  1512. }
  1513.  
  1514.  
  1515. //
  1516. // FUNCTION/METHOD NAME: GetPriority(ePrtyClass, c4PrioLev)
  1517. //
  1518. // DESCRIPTION:
  1519. //
  1520. //  This method will return the priority of this thread.
  1521. // ---------------------------------------
  1522. //   INPUT: NOne
  1523. //
  1524. //  OUTPUT: ePrtyClass is filled in with the thread's class
  1525. //          c4PrioLev is filled in with the thread's priority level.
  1526. //
  1527. //  RETURN: None
  1528. //
  1529. tCIDLib::VOID PROCEXP THREAD::GetPriority(  tCIDLib::ePRTYCLASS&    ePrtyClass
  1530.                                             , tCIDLib::CARD4&       c4PrioLev)
  1531. {
  1532.     // Abort if the caller is not this thread
  1533.     _CheckCallerIsSelf( "GetPriority"
  1534.                         ,"Only a thread can get its own priority");
  1535.  
  1536.     tCIDLib::CARD4  c4Err;
  1537.     PTIB            pTBlk;
  1538.     PPIB            pPBlk;
  1539.  
  1540.     c4Err = DosGetInfoBlocks(&pTBlk, &pPBlk);
  1541.  
  1542.     if (c4Err)
  1543.     {
  1544.         facCIDLib.LogSysErr(__FILE__
  1545.                             , "THREAD::GetPriority"
  1546.                             , __LINE__
  1547.                             , c4Err
  1548.                             , "DosGetInfoBlocks failed"
  1549.                             , tCIDLib::eSEV_PROCESS_FATAL);
  1550.     }
  1551.  
  1552.     //
  1553.     //  The values is encoded with the class in the 2nd byte and the level
  1554.     //  in the 1st byte.
  1555.     //
  1556.     ePrtyClass = tCIDLib::ePRTYCLASS(pTBlk->tib_ptib2->tib2_ulpri >> 8);
  1557.     c4PrioLev  = pTBlk->tib_ptib2->tib2_ulpri & 0xFF;
  1558. }
  1559.  
  1560.  
  1561. //
  1562. // FUNCTION/METHOD NAME: pfnSetOnExit(pfnNew)
  1563. //
  1564. // DESCRIPTION:
  1565. //
  1566. //  This method will set the on-exit function and return the current one. It
  1567. //  can only be called by this thread.
  1568. // ---------------------------------------
  1569. //   INPUT: pfnNew is the new on-exit function to call when the thread shuts
  1570. //              down.
  1571. //
  1572. //  OUTPUT: None
  1573. //
  1574. //  RETURN: The current on-exit function
  1575. //
  1576. tCIDLib::pfnEXITFUNC
  1577.     PROCEXP THREAD::pfnSetOnExit(tCIDLib::pfnEXITFUNC pfnNew)
  1578. {
  1579.     tCIDLib::pfnEXITFUNC    pfnTmp = __pfnOnExit;
  1580.     __pfnOnExit = pfnNew;
  1581.     return pfnTmp;
  1582. }
  1583.  
  1584.  
  1585. //
  1586. // FUNCTION/METHOD NAME: perrLast()
  1587. //
  1588. // DESCRIPTION:
  1589. //
  1590. //  This method will return the last error that occured. The caller is now
  1591. //  responsible for deleting the error object!!!
  1592. // ---------------------------------------
  1593. //   INPUT: None
  1594. //
  1595. //  OUTPUT: None
  1596. //
  1597. //  RETURN: The address of the last error object, 0 if there was no error
  1598. //              info.
  1599. //
  1600. CIDERROR* PROCEXP THREAD::perrLast()
  1601. {
  1602.     // Abort if the caller is not this thread
  1603.     _CheckCallerIsSelf("perrLast");
  1604.  
  1605.     CIDERROR* pErr;
  1606.  
  1607.     // Lock the thread list
  1608.     LSTLOCK Lock("THREAD::perrLast");
  1609.  
  1610.     if (__perrLast)
  1611.     {
  1612.         pErr = __perrLast;
  1613.         __perrLast = 0;
  1614.     }
  1615.      else
  1616.     {
  1617.         pErr = 0;
  1618.     }
  1619.     return pErr;
  1620. }
  1621.  
  1622.  
  1623. //
  1624. // FUNCTION/METHOD NAME: SetPriority(ePrtyClass, c4PrioLev)
  1625. //
  1626. // DESCRIPTION:
  1627. //
  1628. //  The first version of this method will set the priority class of the
  1629. //  thread and it's absolute level within that class. The second version
  1630. //  does a relative adjustment of the level without changing the class.
  1631. // ---------------------------------------
  1632. //   INPUT: ePrtyClass is the class to set
  1633. //          c4PrioLev is the priority level with the class, which must be
  1634. //              from 0 to tCIDLib::c4MaxPrioLev.
  1635. //
  1636. //  OUTPUT: None
  1637. //
  1638. //  RETURN: None
  1639. //
  1640. tCIDLib::VOID PROCEXP THREAD::SetPriority(  tCIDLib::ePRTYCLASS ePrtyClass
  1641.                                             , tCIDLib::CARD4    c4PrioLev)
  1642. {
  1643.     tCIDLib::CARD4  c4Err;
  1644.  
  1645.     #if DEBUGMED
  1646.     if (c4PrioLev > tCIDLib::c4MaxPrioLev)
  1647.     {
  1648.         facCIDLib.LogErr(   __FILE__
  1649.                             , "THREAD::SetPriority"
  1650.                             , __LINE__
  1651.                             , LIBERR_PRC_BAD_PRIO_LEV
  1652.                             , "The value was clipped to valid range"
  1653.                             , tCIDLib::eSEV_WARNING
  1654.                             , tCIDLib::eCLASS_BADPARMS
  1655.                             , CARDINAL(c4PrioLev)
  1656.                             , CARDINAL(tCIDLib::c4MaxPrioLev));
  1657.         c4PrioLev = tCIDLib::c4MaxPrioLev;
  1658.     }
  1659.     #endif
  1660.  
  1661.     //
  1662.     //  If this thread is in SELFPRIO mode, then only it can set its own
  1663.     //  priority.
  1664.     //
  1665.     if (__bSelfPrio)
  1666.     {
  1667.         // Abort if the caller is not this thread
  1668.          _CheckCallerIsSelf("SetPriority", "This thread is in SELFPRIO mode");
  1669.     }
  1670.  
  1671.     c4Err = DosSetPriority(PRTYS_THREAD, ePrtyClass, c4PrioLev, __tidThread);
  1672.  
  1673.     if (c4Err)
  1674.     {
  1675.         facCIDLib.LogSysErr(__FILE__
  1676.                             , "THREAD::SetPriority"
  1677.                             , __LINE__
  1678.                             , c4Err
  1679.                             , "DosSetPriority failed"
  1680.                             , tCIDLib::eSEV_PROCESS_FATAL);
  1681.     }
  1682. }
  1683.  
  1684. tCIDLib::VOID PROCEXP THREAD::SetPriority(tCIDLib::INT4 i4PrioLev)
  1685. {
  1686.     tCIDLib::CARD4  c4Err;
  1687.  
  1688.     //
  1689.     //  If this thread is in SELFPRIO mode, then only it can set its own
  1690.     //  priority.
  1691.     //
  1692.     if (__bSelfPrio)
  1693.     {
  1694.         // Abort if the caller is not this thread
  1695.          _CheckCallerIsSelf("SetPriority", "This thread is in SELFPRIO mode");
  1696.     }
  1697.  
  1698.     c4Err = DosSetPriority( PRTYS_THREAD
  1699.                             , PRTYC_NOCHANGE
  1700.                             , i4PrioLev
  1701.                             , __tidThread);
  1702.     if (c4Err)
  1703.     {
  1704.         facCIDLib.LogSysErr(__FILE__
  1705.                             , "THREAD::SetPriority"
  1706.                             , __LINE__
  1707.                             , c4Err
  1708.                             , "DosSetPriority failed"
  1709.                             , tCIDLib::eSEV_PROCESS_FATAL);
  1710.     }
  1711. }
  1712.  
  1713.  
  1714. //
  1715. // FUNCTION/METHOD NAME: StoreNewError(perrNew)
  1716. //
  1717. // DESCRIPTION:
  1718. //
  1719. //  This method allows the thread to store a new object. It will check to
  1720. //  make sure that the caller is the thread controlled by this object.
  1721. // ---------------------------------------
  1722. //   INPUT: perrNew is a pointer to the new error object to store.
  1723. //
  1724. //  OUTPUT: None
  1725. //
  1726. //  RETURN: None
  1727. //
  1728. tCIDLib::VOID PROCEXP THREAD::StoreNewError(CIDERROR* perrNew)
  1729. {
  1730.     // Abort if the caller is not this thread
  1731.     _CheckCallerIsSelf("StoreNewError");
  1732.  
  1733.     // No need to synchronize now since only this thread calls
  1734.     if (__perrLast)
  1735.         delete __perrLast;
  1736.     __perrLast = perrNew;
  1737. }
  1738.  
  1739.  
  1740. //
  1741. // FUNCTION/METHOD NAME: strName()
  1742. //
  1743. // DESCRIPTION:
  1744. //
  1745. //  This method returns the name of the thread.
  1746. // ---------------------------------------
  1747. //   INPUT: None
  1748. //
  1749. //  OUTPUT: None
  1750. //
  1751. //  RETURN: The name of the thread
  1752. //
  1753. const STRG32& PROCEXP THREAD::strName() const
  1754. {
  1755.     return __strName;
  1756. }
  1757.  
  1758.  
  1759. //
  1760. // FUNCTION/METHOD NAME: Sync()
  1761. //
  1762. // DESCRIPTION:
  1763. //
  1764. //  This method will block if another thread has called bWaitSync() and is
  1765. //  waiting for this thread to sync up.
  1766. // ---------------------------------------
  1767. //   INPUT: None
  1768. //
  1769. //  OUTPUT: None
  1770. //
  1771. //  RETURN: eTRUE if successful, else eFALSE
  1772. //
  1773. tCIDLib::VOID PROCEXP THREAD::Sync()
  1774. {
  1775.     _CheckCallerIsSelf("THREAD::Sync");
  1776.  
  1777.     tCIDLib::CARD4  c4Err;
  1778.  
  1779.     // If there is no sync request, then just return
  1780.     if (!__bSyncRequest)
  1781.         return;
  1782.  
  1783.     // Clear the response semaphore to indicate that we are not waiting
  1784.     c4Err = DosPostEventSem(__hevResponse);
  1785.  
  1786.     // Block on the sync semaphore
  1787.     c4Err = DosWaitEventSem(__hevSync, SEM_INDEFINITE_WAIT);
  1788.  
  1789.     // If an error, then abort
  1790.     if (c4Err)
  1791.     {
  1792.         facCIDLib.LogSysErr(__FILE__
  1793.                             , "THREAD::bSync"
  1794.                             , __LINE__
  1795.                             , c4Err
  1796.                             , "Could not wait on the sync semaphore"
  1797.                             , tCIDLib::eSEV_PROCESS_FATAL);
  1798.     }
  1799.  
  1800.     // We are awake again so the syncing thread has released us
  1801. }
  1802.  
  1803.  
  1804.  
  1805. // -----------------------------------------------------------------------------
  1806. //  THREAD: Protected, inherited methods
  1807. // -----------------------------------------------------------------------------
  1808.  
  1809. //
  1810. // FUNCTION/METHOD NAME: _bIsEqual(objTarget) const
  1811. //
  1812. // DESCRIPTION:
  1813. //
  1814. //  This method compares this object to the target object, at this class level.
  1815. // ---------------------------------------
  1816. //   INPUT: objTarget is the target object to compare against
  1817. //
  1818. //  OUTPUT: None
  1819. //
  1820. //  RETURN: eTRUE if the objects are equal, else eFALSE.
  1821. //
  1822. tCIDLib::eBOOL PROCEXP THREAD::_bIsEqual(const CIDOBJECT& objTarget) const
  1823. {
  1824.     // Call our parent's version first
  1825.     if (!CIDOBJECT::_bIsEqual(objTarget))
  1826.         return tCIDLib::eFALSE;
  1827.  
  1828.     // Look at the target object as a thread
  1829.     THREAD*     pthrTmp = (THREAD*)(&objTarget);
  1830.  
  1831.     if (__bRunning != pthrTmp->__bRunning)
  1832.         return tCIDLib::eFALSE;
  1833.  
  1834.     if (__tidThread != pthrTmp->__tidThread)
  1835.         return tCIDLib::eFALSE;
  1836.  
  1837.     if (__strName != pthrTmp->__strName)
  1838.         return tCIDLib::eFALSE;
  1839.  
  1840.     if (__pData != pthrTmp->__pData)
  1841.         return tCIDLib::eFALSE;
  1842.  
  1843.     if (__pfnFunc != pthrTmp->__pfnFunc)
  1844.         return tCIDLib::eFALSE;
  1845.  
  1846.     return tCIDLib::eTRUE;
  1847. }
  1848.  
  1849.  
  1850. //
  1851. // FUNCTION/METHOD NAME: _CheckCallerIsSelf(pszMethod, pszAuxText) const
  1852. //
  1853. // DESCRIPTION:
  1854. //
  1855. //  This method will check to make sure that the calling thread is this
  1856. //  thread. If not, it will issue a fatal error message and abort.
  1857. // ---------------------------------------
  1858. //   INPUT: pszMethod is the name of the calling method, which will be used
  1859. //              in the abort message if needed.
  1860. //          pszAuxText is an optional text string for the aux text of the
  1861. //              logged error. It defaults to 0.
  1862. //
  1863. //  OUTPUT: None
  1864. //
  1865. //  RETURN: None
  1866. //
  1867. tCIDLib::VOID PROCEXP
  1868.         THREAD::_CheckCallerIsSelf( const   tCIDLib::CH*    pszMethod
  1869.                                     , const tCIDLib::CH*    pszAuxText) const
  1870. {
  1871.     THREAD* pthrCaller = _pthrCurrent();
  1872.  
  1873.     if (pthrCaller != this)
  1874.     {
  1875.         facCIDLib.LogErr(   __FILE__
  1876.                             , pszMethod
  1877.                             , __LINE__
  1878.                             , LIBERR_PRC_NOT_THIS_THREAD
  1879.                             , pszAuxText ? pszAuxText : ""
  1880.                             , tCIDLib::eSEV_PROCESS_FATAL
  1881.                             , tCIDLib::eCLASS_AUTHORITY
  1882.                             , pthrCaller->strName()
  1883.                             , this->strName());
  1884.     }
  1885. }
  1886.  
  1887.  
  1888. //
  1889. // FUNCTION/METHOD NAME: _Exiting()
  1890. //
  1891. // DESCRIPTION:
  1892. //
  1893. //  This method is provided so that the standard thread exception handler
  1894. //  can clean up the thread flags when it is killed.
  1895. // ---------------------------------------
  1896. //   INPUT: None
  1897. //
  1898. //  OUTPUT: None
  1899. //
  1900. //  RETURN: None
  1901. //
  1902. tCIDLib::VOID PROCEXP THREAD::_Exiting()
  1903. {
  1904.     //
  1905.     //  Call the on-exit function to let the thread object clean up. This
  1906.     //  is for user code since it does not require a separate derivation to
  1907.     //  do.
  1908.     //
  1909.     if (__pfnOnExit)
  1910.         __pfnOnExit(*this);
  1911.  
  1912.     //
  1913.     //  Call the virtual terminate method to let derived thread classes do
  1914.     //  internal cleanup.
  1915.     //
  1916.     Terminate();
  1917.  
  1918.     __bRunning = tCIDLib::eFALSE;
  1919.     __tidThread= TID(-1);
  1920.  
  1921.     // If there is a last error, then clean it up
  1922.     if (__perrLast)
  1923.     {
  1924.         delete __perrLast;
  1925.         __perrLast = 0;
  1926.     }
  1927. }
  1928.  
  1929.  
  1930. // -----------------------------------------------------------------------------
  1931. //  THREAD: Private, non-virtual methods
  1932. // -----------------------------------------------------------------------------
  1933.  
  1934. //
  1935. // FUNCTION/METHOD NAME: __CreatePerThreadObjs()
  1936. //
  1937. // DESCRIPTION:
  1938. //
  1939. //  This method is called by the constructor. It creates instances of all the
  1940. //  registered per-thread objects for this thread.
  1941. // ---------------------------------------
  1942. //   INPUT: None
  1943. //
  1944. //  OUTPUT: None
  1945. //
  1946. //  RETURN: None
  1947. //
  1948. tCIDLib::VOID PRIVEXP THREAD::__CreatePerThreadObjs()
  1949. {
  1950.     //
  1951.     //  Loop through the master list of registered per-thread objects. Calling
  1952.     //  the callback for each one.
  1953.     //
  1954.     for (tCIDLib::CARD4 c4Ind = 0; c4Ind < __c4PTObjCount; c4Ind++)
  1955.         __apobjList[c4Ind] = __aPTObjs[c4Ind].pfnPerThread(c4Ind);
  1956. }
  1957.  
  1958.  
  1959. //
  1960. // FUNCTION/METHOD NAME: __DestroyPerThreadObjs()
  1961. //
  1962. // DESCRIPTION:
  1963. //
  1964. //  This method is called by the destructor. It destroys all of the per-thread
  1965. //  objects of this thread.
  1966. // ---------------------------------------
  1967. //   INPUT: None
  1968. //
  1969. //  OUTPUT: None
  1970. //
  1971. //  RETURN: None
  1972. //
  1973. tCIDLib::VOID PRIVEXP THREAD::__DestroyPerThreadObjs()
  1974. {
  1975.     //
  1976.     //  Loop through the per-thread objects, deleting each one. If we find a
  1977.     //  zeroed out entry before we hit the max count, then issue a fatal error.
  1978.     //
  1979.     for (tCIDLib::CARD4 c4Ind = 0; c4Ind < __c4PTObjCount; c4Ind++)
  1980.     {
  1981.         if (!__apobjList[c4Ind])
  1982.         {
  1983.             facCIDLib.LogMsg(   __FILE__
  1984.                                 , "THREAD::__DestroyPTObjs"
  1985.                                 , __LINE__
  1986.                                 , "Per-thread object count disagrees with "
  1987.                                                 "per-thread list"
  1988.                                 , tCIDLib::eSEV_PROCESS_FATAL);
  1989.         }
  1990.          else
  1991.         {
  1992.             delete __apobjList[c4Ind];
  1993.         }
  1994.     }
  1995. }
  1996.