home *** CD-ROM | disk | FTP | other *** search
/ Shareware Supreme Volume 6 #1 / swsii.zip / swsii / 215 / DDJWIN.ZIP / THREAD.ASC < prev    next >
Text File  |  1993-08-31  |  11KB  |  336 lines

  1. _THREADS FOR WINDOWS 3_
  2. by David Lee
  3.  
  4. [LISTING ONE]
  5.  
  6. typedef void (FAR * THREADPROC)(DWORD dwParam);
  7. int ThrCreateThread(THREADPROC tp,DWORD dwParam,UINT uiStackSize,BOOL bEnable,DWORD FAR *pdwID);
  8. int ThrYield(void);
  9. int ThrExit(void);
  10. int ThrThreadCount(UINT FAR *puiCount);
  11. int ThrAppYield(void);
  12. int ThrEnableThread(DWORD dwID,BOOL bEnable);
  13.  
  14.  
  15. [LISTING TWO]
  16.  
  17. // Threads for Windows. -- Do what you like with these sources.
  18. // Version 0.2 -- // David Lee -- dlee@inference.com
  19. // Compiled with Visual C++ 1.00.
  20.  
  21. #define NOKEYSTATES
  22. #define NODRAWTEXT
  23. #define NOLOGERROR
  24. #define NOOEMRESOURCE
  25. #define NOOPENFILE
  26. #define NOTEXTMETRIC
  27. #define NODRIVERS
  28. #define NODBCS
  29. #define NOSYSTEMPARAMSINFO
  30. #define NOSCALABLEFONT
  31. #define NOGDICAPMASKS
  32. #define NOATOM
  33. #define NOMETAFILE
  34. #define NOSCROLL
  35. #define NOSOUND
  36. #define NOCOMM
  37. #define NOKANJI
  38. #define NOPROFILER
  39. #define NODEFERWINDOWPOS
  40.  
  41. #define STRICT
  42. #include <windows.h>
  43.  
  44. #include "thr.h"
  45. typedef struct _THREADINFO
  46. {
  47.   WORD ss;                         // stack selector of thread
  48.   WORD sp;                         // stack pointer of thread
  49.   BOOL bCalledYet;                 // TRUE if thread has been started
  50.   BOOL bEnabled;                   // TRUE if thread can run
  51.   DWORD dwParam;                   // passed to thread routine tpStart
  52.   THREADPROC tpStart;              // address of thread routine
  53.   GLOBALHANDLE ghStack;            // stack global handle
  54.   GLOBALHANDLE gh;                 // this structure's global handle
  55.   struct _THREADINFO FAR *ptiNext; // next thread in the list
  56. } THREADINFO, FAR *PTHREADINFO;
  57.  
  58. static PTHREADINFO s_ptiList=NULL;    // linked list of threads
  59. static PTHREADINFO s_ptiCurrent=NULL; // currently running thread
  60. static BOOL s_bThreadRunning=FALSE;   // TRUE if a thread is running
  61. static UINT s_uiTimerID=0;            // timer identifier
  62. static UINT s_uiThreadCount=0;        // # of threads
  63. static UINT s_uiRealSS;               // task SS register buffer
  64. static UINT s_uiRealSP;               // task SP register buffer
  65. static UINT s_uiTmpSS;                // thread SS register buffer
  66. static UINT s_uiTmpSP;                // thread SP register buffer
  67. static THREADPROC s_tpTmp;            // buffer for thread start address
  68. static DWORD s_dwTmp;                 // buffer for thread parameter
  69.  
  70. #define SAVE_CPU_STATE __asm \
  71.   {                          \
  72.     __asm pusha              \
  73.     __asm push es            \
  74.   }  
  75. #define RESTORE_CPU_STATE __asm \
  76.   {                             \
  77.     __asm pop es                \
  78.     __asm popa                  \
  79.   }
  80. // Visual C++ saves and restores di and si in opposite
  81. // order depending on whether debug is turned on.
  82. #ifdef _DEBUG
  83. #define POP_DI_AND_SI __asm \
  84.   {                         \
  85.     __asm pop di            \
  86.     __asm pop si            \
  87.   }
  88. #else // _DEBUG
  89. #define POP_DI_AND_SI __asm \
  90.   {                         \
  91.     __asm pop si            \
  92.     __asm pop di            \
  93.   }
  94. #endif // _DEBUG
  95. void __loadds __export CALLBACK ThreadTimerProc(HWND hwnd,UINT msg,UINT uiID,
  96.                                                 DWORD dwTime);
  97. static void TurnTimerOnOrOff(void);
  98. static void NukeThread(PTHREADINFO pti);
  99. // If any threads exist and are enabled, make sure the timer
  100. // is running.  Otherwise, kill the timer if it is running.
  101. static void TurnTimerOnOrOff(void)
  102. {
  103.   PTHREADINFO pti;
  104.   BOOL bAny;
  105.   for (bAny=FALSE,pti=s_ptiList; !bAny && pti; pti=pti->ptiNext)
  106.     if (pti->bEnabled)
  107.       bAny = TRUE;
  108.   if (bAny && !s_uiTimerID)
  109.     {
  110.       // At least one enabled thread, so start the timer
  111.       s_uiTimerID = SetTimer(0,0,0,(TIMERPROC) ThreadTimerProc);
  112.     }
  113.   else if (!bAny && s_uiTimerID)
  114.     {
  115.       // No enabled threads, so kill the timer
  116.       KillTimer(0,s_uiTimerID);
  117.       s_uiTimerID = 0;
  118.     }    
  119. } /*TurnTimerOnOrOff*/
  120. // Free storage associated with a thread that is being removed.
  121. static void NukeThread(PTHREADINFO pti)
  122. {
  123.   GLOBALHANDLE gh;
  124.   PTHREADINFO ptiTmp;
  125.   // Get rid of the thread's stack
  126.   GlobalUnlock(pti->ghStack);
  127.   GlobalFree(pti->ghStack);
  128.   // Remove thread structure from the linked list of threads
  129.   if (s_ptiList == pti)
  130.     s_ptiList = pti->ptiNext;
  131.   else
  132.     {
  133.       for (ptiTmp=s_ptiList; ptiTmp; ptiTmp=ptiTmp->ptiNext)
  134.         if (ptiTmp->ptiNext == pti)
  135.           {
  136.             ptiTmp->ptiNext = pti->ptiNext;
  137.             break;
  138.           }    
  139.     }
  140.   gh = pti->gh;    
  141.   GlobalUnlock(gh);
  142.   GlobalFree(gh);
  143.   
  144.   TurnTimerOnOrOff();
  145.   s_uiThreadCount--;
  146.   s_ptiCurrent = NULL;
  147.   s_bThreadRunning = FALSE;
  148. } /*NukeThread*/
  149. // This callback is called by Windows for each timer event.
  150. // It picks a thread to give a shot at the CPU and runs it.
  151. void __loadds __export CALLBACK ThreadTimerProc(HWND hwnd,UINT msg,UINT uiID,
  152.                                                 DWORD dwTime)
  153. {
  154.   UINT uiPass;
  155.   PTHREADINFO pti,ptiTmp;
  156.   // The timer callback can be called when a thread is running -- don't recurse
  157.   // because this code _and_ the thread code don't expect it.
  158.   if (!s_bThreadRunning && s_uiThreadCount)
  159.     {
  160.       s_bThreadRunning = TRUE;
  161.       // Pick a thread to run, usually the thread after the last thread run.
  162.       ptiTmp = s_ptiCurrent ? s_ptiCurrent : s_ptiList;
  163.       for (pti=NULL,uiPass=0; !pti && uiPass<=s_uiThreadCount; uiPass++)
  164.         {
  165.           ptiTmp = ptiTmp->ptiNext ? ptiTmp->ptiNext : s_ptiList;
  166.           if (ptiTmp->bEnabled)
  167.             pti = ptiTmp;
  168.         }
  169.       if (pti)
  170.         {  
  171.           s_ptiCurrent = pti;                             
  172.           if (pti->bCalledYet)
  173.             {
  174.               // Save the real task's state
  175.               SAVE_CPU_STATE;
  176.               __asm mov s_uiRealSS, ss
  177.               __asm mov s_uiRealSP, sp
  178.               // Restore the thread's state.
  179.               // Can't dereference pti in __asm statement.
  180.               s_uiTmpSS = pti->ss;
  181.               s_uiTmpSP = pti->sp;
  182.               __asm mov ss, s_uiTmpSS
  183.               __asm mov sp, s_uiTmpSP
  184.               RESTORE_CPU_STATE;
  185.               // Return back to thread as if we are returning from ThrYield().
  186.               POP_DI_AND_SI;
  187.               __asm leave
  188.               __asm retf
  189.             }
  190.           else
  191.             {
  192.               pti->bCalledYet = TRUE;
  193.               s_tpTmp = pti->tpStart;
  194.               s_dwTmp = pti->dwParam;
  195.               // Save the real task's state
  196.               SAVE_CPU_STATE;
  197.               __asm mov s_uiRealSS, ss
  198.               __asm mov s_uiRealSP, sp
  199.               // Set up the thread's stack
  200.               s_uiTmpSS = pti->ss;
  201.               s_uiTmpSP = pti->sp;
  202.               __asm mov ss, s_uiTmpSS
  203.               __asm mov sp, s_uiTmpSP
  204.               // Kick off the thread
  205.               s_tpTmp(s_dwTmp);
  206.               // Thread routine returned.  It is finished, so restore
  207.               // the task's real state and delete the thread.
  208.               __asm mov ss, s_uiRealSS
  209.               __asm mov sp, s_uiRealSP
  210.               RESTORE_CPU_STATE;
  211.               NukeThread(pti);
  212.             }
  213.         }
  214.       else s_bThreadRunning = FALSE;
  215.     }
  216. } /*ThreadTimerProc*/
  217. // The thread requests termination, so get rid of it and continue on. This  
  218. // should only be called by a thread, not by the main app thread.
  219. int ThrExit(void)
  220. {
  221.   if (s_bThreadRunning) // make sure
  222.     {
  223.       // Restore state of machine to the point before thread was invoked.
  224.       __asm mov ss, s_uiRealSS
  225.       __asm mov sp, s_uiRealSP
  226.       RESTORE_CPU_STATE;
  227.       NukeThread(s_ptiCurrent);
  228.       // Return to Windows as if we are returning from ThreadTimerProc().
  229.       POP_DI_AND_SI;
  230.       __asm leave
  231.       __asm retf 0xa
  232.     }    
  233.   return(0);    
  234. } /*ThrExit*/
  235. // This should be called by threads every .05 to .1 seconds.
  236. // This routine yields the CPU to other threads and other apps.
  237. int ThrYield(void)
  238. {
  239.   if (s_bThreadRunning) // make sure
  240.     {
  241.       // Save the thread's state.  Can't dereference s_ptiCurrent
  242.       // in __asm statement, so store in a temporary buffer.
  243.       SAVE_CPU_STATE;
  244.       __asm mov s_uiTmpSS, ss
  245.       __asm mov s_uiTmpSP, sp
  246.       s_ptiCurrent->ss = s_uiTmpSS;    
  247.       s_ptiCurrent->sp = s_uiTmpSP;
  248.       // Restore state of machine to point before the thread was invoked.
  249.       __asm mov ss, s_uiRealSS
  250.       __asm mov sp, s_uiRealSP
  251.       RESTORE_CPU_STATE;
  252.       s_bThreadRunning = FALSE;
  253.       // Return to Windows as if we are returning from ThreadTimerProc().
  254.       POP_DI_AND_SI;
  255.       __asm leave
  256.       __asm retf 0xa
  257.     }    
  258.   return(0);    
  259. } /*ThrYield*/
  260. // Allows an application to yield to its threads without dropping into app's
  261. // main event queue.  Returns TRUE if a thread was run.
  262. int ThrAppYield(void)
  263. {
  264.   MSG msg;
  265.   int iRet=FALSE;
  266.   if (PeekMessage(&msg,NULL,WM_TIMER,WM_TIMER,PM_NOREMOVE))
  267.     if (GetMessage(&msg,NULL,WM_TIMER,WM_TIMER))
  268.       {
  269.         iRet = TRUE;
  270.         DispatchMessage(&msg);
  271.       }
  272.   return(iRet);
  273. } /*ThrAppYield*/
  274. // Turns a thread on/off without killing the thread.
  275. int ThrEnableThread(DWORD dwID,BOOL bEnable)
  276. {
  277.   PTHREADINFO pti = (PTHREADINFO) dwID;
  278.   pti->bEnabled = bEnable;
  279.   TurnTimerOnOrOff();
  280.   return(0);
  281. } /*ThrEnableThread*/
  282. // Returns the number of threads that currently exist.
  283. int ThrThreadCount(UINT FAR *puiCount)
  284. {
  285.   *puiCount = s_uiThreadCount;
  286.   return(0);
  287. } /*ThrThreadCount*/
  288. // Kick off a thread.  Return 0 if OK, nonzero otherwise.
  289. //            tp: Address of the thread procedure (cdecl)
  290. //       dwParam: Passed to the thread procedure
  291. //   uiStackSize: Amount to allocate for the stack (or 0 for 8192)
  292. //       bEnable: TRUE if thread should be initially runnable
  293. //         pdwID: returns the thread ID
  294. int ThrCreateThread(THREADPROC tp,DWORD dwParam,UINT uiStackSize,
  295.                     BOOL bEnable,DWORD FAR *pdwID)
  296. {
  297.   GLOBALHANDLE gh;
  298.   PTHREADINFO pti;
  299.   int iRet=0;
  300.   *pdwID = 0L;
  301.   // Allocate empty thread structure and get it ready to run.
  302.   if (gh = GlobalAlloc(GHND,sizeof(THREADINFO)))
  303.     {
  304.       pti = (PTHREADINFO) GlobalLock(gh);
  305.       pti->gh = gh;
  306.       if (!uiStackSize)
  307.         uiStackSize = 8192;
  308.       if (gh = GlobalAlloc(GHND,uiStackSize))
  309.         {
  310.           *pdwID = (DWORD) pti;
  311.           s_uiThreadCount++;
  312.           pti->ghStack = gh;    
  313.           pti->tpStart = tp;
  314.           pti->ss = HIWORD(GlobalLock(gh));
  315.           pti->sp = uiStackSize;
  316.           pti->bCalledYet = FALSE;
  317.           pti->dwParam = dwParam;
  318.           pti->bEnabled = bEnable;
  319.           // Put the new thread at the head of the linked list of threads.
  320.           pti->ptiNext = s_ptiList;
  321.           s_ptiList = pti;
  322.           TurnTimerOnOrOff();
  323.         }
  324.       else
  325.         {
  326.           iRet = 2;
  327.           gh = pti->gh;
  328.           GlobalUnlock(gh);
  329.           GlobalFree(gh);
  330.         }  
  331.     }
  332.   else iRet = 1;
  333.   return(iRet);
  334. } /*ThrCreateThread*/
  335.  
  336.