home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / xwphescr.zip / XWPH0208.ZIP / src / helpers / threads.c < prev    next >
C/C++ Source or Header  |  2002-08-08  |  23KB  |  691 lines

  1.  
  2. /*
  3.  *@@sourcefile threads.c:
  4.  *      contains helper functions for creating, destroying, and
  5.  *      synchronizing threads, including PM threads with a
  6.  *      message queue which is created automatically.
  7.  *
  8.  *      See thrCreate() for how to start such a thread.
  9.  *
  10.  *      Usage: All OS/2 programs.
  11.  *
  12.  *      Function prefixes (new with V0.81):
  13.  *      --  thr*        Thread helper functions
  14.  *
  15.  *      This file is new with V0.81 and contains all the thread
  16.  *      functions that used to be in helpers.c.
  17.  *
  18.  *      Note: Version numbering in this file relates to XWorkplace version
  19.  *            numbering.
  20.  *
  21.  *@@header "helpers\threads.h"
  22.  */
  23.  
  24. /*
  25.  *      Copyright (C) 1997-2002 Ulrich Möller.
  26.  *      This file is part of the "XWorkplace helpers" source package.
  27.  *      This is free software; you can redistribute it and/or modify
  28.  *      it under the terms of the GNU General Public License as published
  29.  *      by the Free Software Foundation, in version 2 as it comes in the
  30.  *      "COPYING" file of the XWorkplace main distribution.
  31.  *      This program is distributed in the hope that it will be useful,
  32.  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
  33.  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  34.  *      GNU General Public License for more details.
  35.  */
  36.  
  37. #define OS2EMX_PLAIN_CHAR
  38.     // this is needed for "os2emx.h"; if this is defined,
  39.     // emx will define PSZ as _signed_ char, otherwise
  40.     // as unsigned char
  41.  
  42. #define INCL_WINMESSAGEMGR
  43. #define INCL_DOSPROCESS
  44. #define INCL_DOSSEMAPHORES
  45. #define INCL_DOSERRORS
  46. #include <os2.h>
  47.  
  48. #include <string.h>
  49. #include <stdlib.h>
  50.  
  51. #include "setup.h"                      // code generation and debugging options
  52.  
  53. #include "helpers\linklist.h"
  54. #include "helpers\threads.h"
  55.  
  56. #pragma hdrstop
  57.  
  58. /*
  59.  *@@category: Helpers\Control program helpers\Thread management
  60.  *      see threads.c.
  61.  */
  62.  
  63. /* ******************************************************************
  64.  *
  65.  *   Global variables
  66.  *
  67.  ********************************************************************/
  68.  
  69. LINKLIST        G_llThreadInfos;
  70.             // linked list of all THREADINFOS ever created...
  71.             // no auto-free
  72. HMTX            G_hmtxThreadInfos = NULLHANDLE;
  73.  
  74. /* ******************************************************************
  75.  *
  76.  *   Functions
  77.  *
  78.  ********************************************************************/
  79.  
  80. /*
  81.  *@@ LockThreadInfos:
  82.  *
  83.  *@@added V0.9.9 (2001-03-07) [umoeller]
  84.  */
  85.  
  86. static BOOL LockThreadInfos(VOID)
  87. {
  88.     APIRET arc = NO_ERROR;
  89.  
  90.     if (G_hmtxThreadInfos == NULLHANDLE)
  91.     {
  92.         // first call:
  93.         arc = DosCreateMutexSem(NULL,     // unnamed
  94.                                 &G_hmtxThreadInfos,
  95.                                 0,        // unshared
  96.                                 TRUE);    // request!
  97.         lstInit(&G_llThreadInfos, FALSE);
  98.     }
  99.     else
  100.         arc = DosRequestMutexSem(G_hmtxThreadInfos,
  101.                                  SEM_INDEFINITE_WAIT);
  102.  
  103.     return (arc == NO_ERROR);
  104. }
  105.  
  106. /*
  107.  *@@ UnlockThreadInfos:
  108.  *
  109.  *@@added V0.9.9 (2001-03-07) [umoeller]
  110.  */
  111.  
  112. static VOID UnlockThreadInfos(VOID)
  113. {
  114.     DosReleaseMutexSem(G_hmtxThreadInfos);
  115. }
  116.  
  117. /*
  118.  *@@ thr_fntGeneric:
  119.  *      generic thread function used by thrCreate.
  120.  *      This in turn calls the actual thread function
  121.  *      specified with thrCreate.
  122.  *
  123.  *@@added V0.9.2 (2000-03-05) [umoeller]
  124.  *@@changed V0.9.7 (2000-12-18) [lafaix]: THRF_TRANSIENT support added
  125.  */
  126.  
  127. static VOID _Optlink thr_fntGeneric(PVOID ptiMyself)
  128. {
  129.     PTHREADINFO pti = (PTHREADINFO)ptiMyself;
  130.  
  131.     if (pti)
  132.     {
  133.         HEV hevExitComplete;
  134.  
  135.         if (pti->flFlags & THRF_WAIT)
  136.             // "Wait" flag set: thrCreate is then
  137.             // waiting on the wait event sem posted...
  138.             // note: we do not post if THRF_WAIT_EXPLICIT!
  139.             // thread must then post itself
  140.             DosPostEventSem(pti->hevRunning);
  141.  
  142.         if (pti->flFlags & THRF_PMMSGQUEUE)
  143.         {
  144.             // create msg queue
  145.             if ((pti->hab = WinInitialize(0)))
  146.             {
  147.                 if ((pti->hmq = WinCreateMsgQueue(pti->hab, 4000)))
  148.                 {
  149.                     // run thread func
  150.                     ((PTHREADFUNC)pti->pThreadFunc)(pti);
  151.                     WinDestroyMsgQueue(pti->hmq);
  152.                 }
  153.                 WinTerminate(pti->hab);
  154.             }
  155.         }
  156.         else
  157.             // no msgqueue:
  158.             // run thread func without msg queue
  159.             ((PTHREADFUNC)pti->pThreadFunc)(pti);
  160.  
  161.         if (pti->flFlags & (THRF_WAIT | THRF_WAIT_EXPLICIT))    // V0.9.9 (2001-03-14) [umoeller]
  162.             // "Wait" flag set: delete semaphore
  163.             DosCloseEventSem(pti->hevRunning);
  164.  
  165.         // V0.9.9 (2001-03-07) [umoeller]
  166.         // remove thread from global list
  167.         if (LockThreadInfos())
  168.         {
  169.             lstRemoveItem(&G_llThreadInfos, pti);
  170.             UnlockThreadInfos();
  171.         }
  172.  
  173.         // copy event sem before freeing pti
  174.         hevExitComplete = pti->hevExitComplete;
  175.  
  176.         // set exit flags
  177.         // V0.9.7 (2000-12-20) [umoeller]
  178.         pti->tid = NULLHANDLE;
  179.  
  180.         if (pti->ptidRunning)
  181.             // clear "running" flag
  182.             *(pti->ptidRunning) = 0;
  183.  
  184.         // (2000-12-18) [lafaix] clean up pti if thread is transient.
  185.         if (pti->flFlags & THRF_TRANSIENT)
  186.             free(pti);
  187.  
  188.         if (hevExitComplete)
  189.             // caller wants notification:
  190.             DosPostEventSem(hevExitComplete);
  191.                     // V0.9.16 (2001-12-08) [umoeller]
  192.     }
  193.  
  194.     // thread func exits
  195. }
  196.  
  197. /*
  198.  *@@ thrCreate:
  199.  *      this function fills a THREADINFO structure and starts
  200.  *      a new thread using _beginthread.
  201.  *
  202.  *      You must pass the thread function in pfn, which will
  203.  *      then be executed. The thread will be passed a pointer
  204.  *      to the THREADINFO structure as its thread parameter.
  205.  *      The ulData field in that structure is set to ulData
  206.  *      here. Use whatever you like.
  207.  *
  208.  *      The thread function must be declared like this:
  209.  *
  210.  +          void _Optlink fntWhatever(PTHREADINFO ptiMyself)
  211.  *
  212.  *      You should manually specify _Optlink because if the
  213.  *      function is prototyped somewhere, VAC will automatically
  214.  *      modify the function's linkage, and you'll run into
  215.  *      crashes.
  216.  *
  217.  *      The thread's ptiMyself is then a pointer to the
  218.  *      THREADINFO structure passed to this function.
  219.  *      In your thread function, ulData may be obtained like this:
  220.  *
  221.  +          ULONG ulData = ptiMyself->ulData;
  222.  *
  223.  *      thrCreate does not start your thread func directly,
  224.  *      but only through the thr_fntGeneric wrapper to
  225.  *      provide additional functionality. As a consequence,
  226.  *      in your own thread function, NEVER call _endthread
  227.  *      explicitly, because this would skip the exit processing
  228.  *      (cleanup) in thr_fntGeneric. Instead, just fall out of
  229.  *      your thread function, or return().
  230.  *
  231.  *      The THREADINFO structure passed to this function must
  232.  *      be accessible all the time while the thread is running
  233.  *      because the thr* functions will use it for maintenance.
  234.  *      This function does NOT check whether a thread is
  235.  *      already running in *pti. Do not use the same THREADINFO
  236.  *      for several threads.
  237.  *
  238.  *      If you do not want to manage the structure yourself,
  239.  *      you can pass the THRF_TRANSIENT flag (see below).
  240.  *
  241.  *      ptidRunning is an optional parameter and can be NULL.
  242.  *      If non-null, it must point to a ULONG which will contain
  243.  *      the thread's TID while the thread is still running.
  244.  *      Once the thread exits, the ULONG will automatically
  245.  *      be set to zero; this is useful for a quick check whether
  246.  *      the thread is currently busy.
  247.  *
  248.  *      Note: ptidRunning is only reliable if you set the
  249.  *      THRF_WAIT or THRF_WAIT_EXPLICIT flags.
  250.  *
  251.  *      flFlags can be any combination of the following:
  252.  *
  253.  *      -- THRF_PMMSGQUEUE: creates a PM message queue on the
  254.  *         thread. Your thread function will find the HAB and
  255.  *         the HMQ in its THREADINFO. These are automatically
  256.  *         destroyed when the thread terminates.
  257.  *
  258.  *         WARNING: Be careful with this setting for short-lived
  259.  *         threads which are started synchronously while some
  260.  *         other thread is locking PM, e.g. in a WinSendMsg
  261.  *         call. While creation of the thread will still
  262.  *         succeed, the thread will _not_ terminate properly
  263.  *         while PM is locked, so do not wait for such a thread
  264.  *         to terminate!! (V0.9.16)
  265.  *
  266.  *      -- THRF_WAIT: if this is set, thrCreate does not
  267.  *         return to the caller until your thread function
  268.  *         has successfully started running. This is done by
  269.  *         waiting on an event semaphore which is automatically
  270.  *         posted by thr_fntGeneric. This is useful for the
  271.  *         typical PM "Worker" thread where you need to disable
  272.  *         menu items on thread 1 while the thread is running.
  273.  *
  274.  *      -- THRF_WAIT_EXPLICIT: like THRF_WAIT, but in this case,
  275.  *         your thread function _must_ post THREADINFO.hevRunning
  276.  *         yourself (thr_fntGeneric will not automatically
  277.  *         post it). Useful for waiting until your own thread
  278.  *         function is fully initialized, e.g. if it creates
  279.  *         an object window.
  280.  *
  281.  *         WARNING: if your thread forgets to post this, we'll
  282.  *         hang.
  283.  *
  284.  *         Added V0.9.9 (2001-03-14) [umoeller].
  285.  *
  286.  *      -- THRF_TRANSIENT: creates a "transient" thread where
  287.  *         pti may be NULL. A THREADINFO structure is then
  288.  *         allocated from the heap internally, but not visible
  289.  *         to the caller.
  290.  *
  291.  *      This now (V0.9.9) returns the TID of the new thread or
  292.  *      null on errors.
  293.  *
  294.  *@@changed V0.9.0 [umoeller]: default stack size raised for Watcom (thanks, Rüdiger Ihle)
  295.  *@@changed V0.9.0 [umoeller]: _beginthread is now only called after all variables have been set (thanks, Rüdiger Ihle)
  296.  *@@changed V0.9.2 (2000-03-04) [umoeller]: added stack size parameter
  297.  *@@changed V0.9.2 (2000-03-06) [umoeller]: now using thr_fntGeneric; thrGoodbye is no longer needed
  298.  *@@changed V0.9.3 (2000-04-29) [umoeller]: removed stack size param; added fCreateMsgQueue
  299.  *@@changed V0.9.3 (2000-05-01) [umoeller]: added pbRunning and flFlags
  300.  *@@changed V0.9.5 (2000-08-26) [umoeller]: now using PTHREADINFO
  301.  *@@changed V0.9.7 (2000-12-18) [lafaix]: THRF_TRANSIENT support added
  302.  *@@changed V0.9.9 (2001-02-06) [umoeller]: now returning TID
  303.  *@@changed V0.9.9 (2001-03-07) [umoeller]: added pcszThreadName
  304.  *@@changed V0.9.9 (2001-03-14) [umoeller]: added THRF_WAIT_EXPLICIT
  305.  *@@changed V0.9.12 (2001-05-20) [umoeller]: changed pfRunning to ptidRunning
  306.  *@@changed V0.9.16 (2001-10-28) [umoeller]: made ptidRunning volatile to prohibit compiler optimizations
  307.  */
  308.  
  309. ULONG thrCreate(PTHREADINFO pti,     // out: THREADINFO data
  310.                 PTHREADFUNC pfn,     // in: _Optlink thread function
  311.                 volatile unsigned long *ptidRunning,
  312.                                      // out: variable set to TID while thread is running;
  313.                                      // ptr can be NULL
  314.                 const char *pcszThreadName, // in: thread name (for identification)
  315.                 ULONG flFlags,       // in: THRF_* flags
  316.                 ULONG ulData)        // in: user data to be stored in THREADINFO
  317. {
  318.     ULONG ulrc = 0;     // V0.9.9 (2001-02-06) [umoeller]
  319.  
  320.     // (2000-12-18) [lafaix] TRANSIENT
  321.     if (flFlags & THRF_TRANSIENT)
  322.     {
  323.         if (pti == NULL)
  324.             pti = (PTHREADINFO)malloc(sizeof(THREADINFO));
  325.                     // cleaned up by thr_fntGeneric on exit
  326.     }
  327.  
  328.     if (pti)
  329.     {
  330.         memset(pti, 0, sizeof(THREADINFO));
  331.         pti->cbStruct = sizeof(THREADINFO);
  332.         pti->pThreadFunc = (PVOID)pfn;
  333.         pti->ptidRunning = ptidRunning;
  334.         pti->pcszThreadName = pcszThreadName; // V0.9.9 (2001-03-07) [umoeller]
  335.         pti->flFlags = flFlags;
  336.         pti->ulData = ulData;
  337.  
  338.         if (flFlags & (THRF_WAIT | THRF_WAIT_EXPLICIT)) // V0.9.9 (2001-03-14) [umoeller]
  339.             // "Wait" flag set: create an event semaphore which
  340.             // will be posted by thr_fntGeneric (THRF_WAIT only)
  341.             if (DosCreateEventSem(NULL,     // unnamed
  342.                                   &pti->hevRunning,
  343.                                   0,        // unshared
  344.                                   FALSE)    // not posted (reset)
  345.                     != NO_ERROR)
  346.             {
  347.                 if (flFlags & THRF_TRANSIENT)
  348.                     free(pti);
  349.  
  350.                 // stop right here
  351.                 pti = NULL;
  352.             }
  353.  
  354.         if (pti)
  355.         {
  356.             pti->tid = _beginthread(        // moved, V0.9.0 (hint: Rüdiger Ihle)
  357.                                     thr_fntGeneric, // changed V0.9.2 (2000-03-06) [umoeller]
  358.                                     0,      // unused compatibility param
  359.                                     3*96000, // plenty of stack
  360.                                     pti);   // parameter passed to thread
  361.             ulrc = pti->tid;
  362.  
  363.             if (pti->ptidRunning)
  364.                 // set "running" flag // V0.9.12 (2001-05-20) [umoeller]
  365.                 *(pti->ptidRunning) = pti->tid;
  366.  
  367.             if (ulrc)
  368.             {
  369.                 if (LockThreadInfos())
  370.                 {
  371.                     lstAppendItem(&G_llThreadInfos, pti);
  372.                     UnlockThreadInfos();
  373.                 }
  374.  
  375.                 if (flFlags & (THRF_WAIT | THRF_WAIT_EXPLICIT))
  376.                 {
  377.                     // "Wait" flag set: wait on event semaphore
  378.                     DosWaitEventSem(pti->hevRunning,
  379.                                     SEM_INDEFINITE_WAIT);
  380.                 }
  381.             }
  382.         }
  383.     }
  384.  
  385.     return (ulrc);
  386. }
  387.  
  388. /*
  389.  *@@ thrRunSync:
  390.  *      runs the specified thread function synchronously.
  391.  *
  392.  *      This is a wrapper around thrCreate. However, this
  393.  *      function does not return until the thread function
  394.  *      finishes. This creates a modal message loop on the
  395.  *      calling thread so that the PM message queue is not
  396.  *      blocked while the thread is running. Naturally this
  397.  *      implies that the calling thread has a message queue.
  398.  *
  399.  *      As a special requirement, your thread function (pfn)
  400.  *      must post WM_USER to THREADINFO.hwndNotify just before
  401.  *      exiting. The mp1 value of WM_USER will then be returned
  402.  *      by this function.
  403.  *
  404.  *@@added V0.9.5 (2000-08-26) [umoeller]
  405.  *@@changed V0.9.9 (2001-03-07) [umoeller]: added pcszThreadName
  406.  *@@changed V0.9.16 (2001-12-08) [umoeller]: fixed hang with thrWait
  407.  */
  408.  
  409. ULONG thrRunSync(HAB hab,               // in: anchor block of calling thread
  410.                  PTHREADFUNC pfn,       // in: passed to thrCreate
  411.                  const char *pcszThreadName, // in: passed to thrCreate
  412.                  ULONG ulData)          // in: passed to thrCreate
  413. {
  414.     ULONG ulrc = 0;
  415.     QMSG qmsg;
  416.     BOOL fQuit = FALSE;
  417.     HWND hwndNotify;
  418.     if (hwndNotify = WinCreateWindow(HWND_OBJECT,
  419.                                      WC_BUTTON,
  420.                                      (PSZ)"",
  421.                                      0,
  422.                                      0,0,0,0,
  423.                                      0,
  424.                                      HWND_BOTTOM,
  425.                                      0,
  426.                                      0,
  427.                                      NULL))
  428.     {
  429.         THREADINFO  ti = {0};
  430.         volatile unsigned long tidRunning = 0;
  431.         thrCreate(&ti,
  432.                   pfn,
  433.                   &tidRunning,
  434.                   pcszThreadName,
  435.                   THRF_PMMSGQUEUE,
  436.                   ulData);
  437.         ti.hwndNotify = hwndNotify;
  438.         // create event sem to wait on V0.9.16 (2001-12-08) [umoeller]
  439.         DosCreateEventSem(NULL,
  440.                           &ti.hevExitComplete,
  441.                           0,
  442.                           FALSE);       // not posted
  443.  
  444.         while (WinGetMsg(hab,
  445.                          &qmsg, 0, 0, 0))
  446.         {
  447.             // current message for our object window?
  448.             if (    (qmsg.hwnd == hwndNotify)
  449.                  && (qmsg.msg == WM_USER)
  450.                )
  451.             {
  452.                 fQuit = TRUE;
  453.                 ulrc = (ULONG)qmsg.mp1;
  454.             }
  455.  
  456.             WinDispatchMsg(hab, &qmsg);
  457.             if (fQuit)
  458.                 break;
  459.         }
  460.  
  461.         // we must wait for the thread to finish, or
  462.         // otherwise THREADINFO is deleted from the stack
  463.         // before the thread exits... will crash!
  464.         // thrWait(&ti);
  465.         // now using event sem V0.9.16 (2001-12-08) [umoeller]
  466.         WinWaitEventSem(ti.hevExitComplete, 5000);
  467.         DosCloseEventSem(ti.hevExitComplete);
  468.  
  469.         WinDestroyWindow(hwndNotify);
  470.     }
  471.  
  472.     return (ulrc);
  473. }
  474.  
  475. /*
  476.  *@@ thrListThreads:
  477.  *      returns an array of THREADINFO structures
  478.  *      for all threads that have been started using
  479.  *      thrCreate (or thrRunSync).
  480.  *
  481.  *      If no threads are running yet, this returns
  482.  *      NULL.
  483.  *
  484.  *      Otherwise, this returns the pointer to the
  485.  *      first array item, and *pcThreads receives
  486.  *      the array item count (NOT the total array
  487.  *      size). The array is a copied snapshot of all
  488.  *      current THREADINFO's and must be free()'d
  489.  *      by the caller.
  490.  *
  491.  *@@added V0.9.9 (2001-03-07) [umoeller]
  492.  */
  493.  
  494. PTHREADINFO thrListThreads(PULONG pcThreads)
  495. {
  496.     PTHREADINFO pArray = 0;
  497.  
  498.     if (LockThreadInfos())
  499.     {
  500.         PTHREADINFO pThis;
  501.         PLISTNODE pNode;
  502.         *pcThreads = lstCountItems(&G_llThreadInfos);
  503.         if (pArray = (PTHREADINFO)malloc(*pcThreads * sizeof(THREADINFO)))
  504.         {
  505.             pThis = pArray;
  506.  
  507.             pNode = lstQueryFirstNode(&G_llThreadInfos);
  508.             while (pNode)
  509.             {
  510.                 memcpy(pThis,
  511.                        (PTHREADINFO)pNode->pItemData,
  512.                        sizeof(THREADINFO));
  513.                 pThis++;
  514.                 pNode = pNode->pNext;
  515.             }
  516.         }
  517.  
  518.         UnlockThreadInfos();
  519.     }
  520.  
  521.     return (pArray);
  522. }
  523.  
  524. /*
  525.  *@@ thrFindThread:
  526.  *      attempts to find the thread with the specified
  527.  *      TID; if found, returns TRUE and copies its
  528.  *      THREADINFO into *pti.
  529.  *
  530.  *@@added V0.9.9 (2001-03-07) [umoeller]
  531.  */
  532.  
  533. BOOL thrFindThread(PTHREADINFO pti,
  534.                    ULONG tid)
  535. {
  536.     BOOL brc = FALSE;
  537.     if (LockThreadInfos())
  538.     {
  539.         PLISTNODE pNode = lstQueryFirstNode(&G_llThreadInfos);
  540.         while (pNode)
  541.         {
  542.             PTHREADINFO ptiThis = (PTHREADINFO)pNode->pItemData;
  543.             if (ptiThis->tid == tid)
  544.             {
  545.                 memcpy(pti, ptiThis, sizeof(THREADINFO));
  546.                 brc = TRUE;
  547.                 break;
  548.             }
  549.             pNode = pNode->pNext;
  550.         }
  551.  
  552.         UnlockThreadInfos();
  553.     }
  554.  
  555.     return brc;
  556. }
  557.  
  558. /*
  559.  *@@ thrClose:
  560.  *      this functions sets the "fExit" flag in
  561.  *      THREADINFO to TRUE.
  562.  *
  563.  *      The thread should monitor this flag
  564.  *      periodically and then terminate itself.
  565.  */
  566.  
  567. BOOL thrClose(PTHREADINFO pti)
  568. {
  569.     if (pti)
  570.     {
  571.         pti->fExit = TRUE;
  572.         return (TRUE);
  573.     }
  574.     return (FALSE);
  575. }
  576.  
  577. /*
  578.  *@@ thrWait:
  579.  *      this function waits for a thread to end by calling
  580.  *      DosWaitThread. Note that this blocks the calling
  581.  *      thread, so only use this function when you're sure
  582.  *      the thread will actually terminate.
  583.  *
  584.  *      Update V0.9.16: Do not use this with PM theads at
  585.  *      all. DosWaitThread can hang the system then.
  586.  *
  587.  *      Returns FALSE if the thread wasn't running or TRUE
  588.  *      if it was and has terminated.
  589.  *
  590.  *@@changed V0.9.0 [umoeller]: now checking for whether pti->tid is still != 0
  591.  */
  592.  
  593. BOOL thrWait(PTHREADINFO pti)
  594. {
  595.     if (pti)
  596.         if (pti->tid)
  597.         {
  598.             DosWaitThread(&pti->tid, DCWW_WAIT);
  599.             pti->tid = NULLHANDLE;
  600.             return (TRUE);
  601.         }
  602.     return (FALSE);
  603. }
  604.  
  605. /*
  606.  *@@ thrFree:
  607.  *      this is a combination of thrClose and
  608.  *      thrWait, i.e. this func does not return
  609.  *      until the specified thread has ended.
  610.  */
  611.  
  612. BOOL thrFree(PTHREADINFO pti)
  613. {
  614.     if (pti->tid)
  615.     {
  616.         thrClose(pti);
  617.         thrWait(pti);
  618.     }
  619.     return (TRUE);
  620. }
  621.  
  622. /*
  623.  *@@ thrKill:
  624.  *      just like thrFree, but the thread is
  625.  *      brutally killed, using DosKillThread.
  626.  *
  627.  *      Note: DO NOT USE THIS. DosKillThread
  628.  *      cannot clean up the C runtime. In the
  629.  *      worst case, this hangs the system
  630.  *      because the runtime hasn't released
  631.  *      a semaphore or something like that.
  632.  */
  633.  
  634. BOOL thrKill(PTHREADINFO pti)
  635. {
  636.     if (pti->tid)
  637.     {
  638.         DosResumeThread(pti->tid);
  639.             // this returns an error if the thread
  640.             // is not suspended, but otherwise the
  641.             // system might hang
  642.         DosKillThread(pti->tid);
  643.     }
  644.     return (TRUE);
  645. }
  646.  
  647. /*
  648.  *@@ thrQueryID:
  649.  *      returns thread ID or NULLHANDLE if
  650.  *      the specified thread is not or no
  651.  *      longer running.
  652.  */
  653.  
  654. TID thrQueryID(const THREADINFO* pti)
  655. {
  656.     if (pti)
  657.         return (pti->tid);
  658.  
  659.     return (NULLHANDLE);
  660. }
  661.  
  662. /*
  663.  *@@ thrQueryPriority:
  664.  *      returns the priority of the calling thread.
  665.  *      The low byte of the low word is a hexadecimal value
  666.  *      representing a rank (value 0 to 31) within a priority class.
  667.  *      Class values, found in the high byte of the low word, are
  668.  *      as follows:
  669.  *      --  0x01  idle
  670.  *      --  0x02  regular
  671.  *      --  0x03  time-critical
  672.  *      --  0x04  server
  673.  *
  674.  *      Note: This cannot retrieve the priority of a
  675.  *      thread other than the one on which this function
  676.  *      is running. Use prc16QueryThreadInfo for that.
  677.  */
  678.  
  679. ULONG thrQueryPriority(VOID)
  680. {
  681.     PTIB    ptib;
  682.     PPIB    ppib;
  683.     if (DosGetInfoBlocks(&ptib, &ppib) == NO_ERROR)
  684.         if (ptib)
  685.             if (ptib->tib_ptib2)
  686.                 return (ptib->tib_ptib2->tib2_ulpri);
  687.     return (0);
  688. }
  689.  
  690.  
  691.