home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Shareware Supreme Volume 6 #1
/
swsii.zip
/
swsii
/
215
/
DDJWIN.ZIP
/
THREAD.ASC
< prev
next >
Wrap
Text File
|
1993-08-31
|
11KB
|
336 lines
_THREADS FOR WINDOWS 3_
by David Lee
[LISTING ONE]
typedef void (FAR * THREADPROC)(DWORD dwParam);
int ThrCreateThread(THREADPROC tp,DWORD dwParam,UINT uiStackSize,BOOL bEnable,DWORD FAR *pdwID);
int ThrYield(void);
int ThrExit(void);
int ThrThreadCount(UINT FAR *puiCount);
int ThrAppYield(void);
int ThrEnableThread(DWORD dwID,BOOL bEnable);
[LISTING TWO]
// Threads for Windows. -- Do what you like with these sources.
// Version 0.2 -- // David Lee -- dlee@inference.com
// Compiled with Visual C++ 1.00.
#define NOKEYSTATES
#define NODRAWTEXT
#define NOLOGERROR
#define NOOEMRESOURCE
#define NOOPENFILE
#define NOTEXTMETRIC
#define NODRIVERS
#define NODBCS
#define NOSYSTEMPARAMSINFO
#define NOSCALABLEFONT
#define NOGDICAPMASKS
#define NOATOM
#define NOMETAFILE
#define NOSCROLL
#define NOSOUND
#define NOCOMM
#define NOKANJI
#define NOPROFILER
#define NODEFERWINDOWPOS
#define STRICT
#include <windows.h>
#include "thr.h"
typedef struct _THREADINFO
{
WORD ss; // stack selector of thread
WORD sp; // stack pointer of thread
BOOL bCalledYet; // TRUE if thread has been started
BOOL bEnabled; // TRUE if thread can run
DWORD dwParam; // passed to thread routine tpStart
THREADPROC tpStart; // address of thread routine
GLOBALHANDLE ghStack; // stack global handle
GLOBALHANDLE gh; // this structure's global handle
struct _THREADINFO FAR *ptiNext; // next thread in the list
} THREADINFO, FAR *PTHREADINFO;
static PTHREADINFO s_ptiList=NULL; // linked list of threads
static PTHREADINFO s_ptiCurrent=NULL; // currently running thread
static BOOL s_bThreadRunning=FALSE; // TRUE if a thread is running
static UINT s_uiTimerID=0; // timer identifier
static UINT s_uiThreadCount=0; // # of threads
static UINT s_uiRealSS; // task SS register buffer
static UINT s_uiRealSP; // task SP register buffer
static UINT s_uiTmpSS; // thread SS register buffer
static UINT s_uiTmpSP; // thread SP register buffer
static THREADPROC s_tpTmp; // buffer for thread start address
static DWORD s_dwTmp; // buffer for thread parameter
#define SAVE_CPU_STATE __asm \
{ \
__asm pusha \
__asm push es \
}
#define RESTORE_CPU_STATE __asm \
{ \
__asm pop es \
__asm popa \
}
// Visual C++ saves and restores di and si in opposite
// order depending on whether debug is turned on.
#ifdef _DEBUG
#define POP_DI_AND_SI __asm \
{ \
__asm pop di \
__asm pop si \
}
#else // _DEBUG
#define POP_DI_AND_SI __asm \
{ \
__asm pop si \
__asm pop di \
}
#endif // _DEBUG
void __loadds __export CALLBACK ThreadTimerProc(HWND hwnd,UINT msg,UINT uiID,
DWORD dwTime);
static void TurnTimerOnOrOff(void);
static void NukeThread(PTHREADINFO pti);
// If any threads exist and are enabled, make sure the timer
// is running. Otherwise, kill the timer if it is running.
static void TurnTimerOnOrOff(void)
{
PTHREADINFO pti;
BOOL bAny;
for (bAny=FALSE,pti=s_ptiList; !bAny && pti; pti=pti->ptiNext)
if (pti->bEnabled)
bAny = TRUE;
if (bAny && !s_uiTimerID)
{
// At least one enabled thread, so start the timer
s_uiTimerID = SetTimer(0,0,0,(TIMERPROC) ThreadTimerProc);
}
else if (!bAny && s_uiTimerID)
{
// No enabled threads, so kill the timer
KillTimer(0,s_uiTimerID);
s_uiTimerID = 0;
}
} /*TurnTimerOnOrOff*/
// Free storage associated with a thread that is being removed.
static void NukeThread(PTHREADINFO pti)
{
GLOBALHANDLE gh;
PTHREADINFO ptiTmp;
// Get rid of the thread's stack
GlobalUnlock(pti->ghStack);
GlobalFree(pti->ghStack);
// Remove thread structure from the linked list of threads
if (s_ptiList == pti)
s_ptiList = pti->ptiNext;
else
{
for (ptiTmp=s_ptiList; ptiTmp; ptiTmp=ptiTmp->ptiNext)
if (ptiTmp->ptiNext == pti)
{
ptiTmp->ptiNext = pti->ptiNext;
break;
}
}
gh = pti->gh;
GlobalUnlock(gh);
GlobalFree(gh);
TurnTimerOnOrOff();
s_uiThreadCount--;
s_ptiCurrent = NULL;
s_bThreadRunning = FALSE;
} /*NukeThread*/
// This callback is called by Windows for each timer event.
// It picks a thread to give a shot at the CPU and runs it.
void __loadds __export CALLBACK ThreadTimerProc(HWND hwnd,UINT msg,UINT uiID,
DWORD dwTime)
{
UINT uiPass;
PTHREADINFO pti,ptiTmp;
// The timer callback can be called when a thread is running -- don't recurse
// because this code _and_ the thread code don't expect it.
if (!s_bThreadRunning && s_uiThreadCount)
{
s_bThreadRunning = TRUE;
// Pick a thread to run, usually the thread after the last thread run.
ptiTmp = s_ptiCurrent ? s_ptiCurrent : s_ptiList;
for (pti=NULL,uiPass=0; !pti && uiPass<=s_uiThreadCount; uiPass++)
{
ptiTmp = ptiTmp->ptiNext ? ptiTmp->ptiNext : s_ptiList;
if (ptiTmp->bEnabled)
pti = ptiTmp;
}
if (pti)
{
s_ptiCurrent = pti;
if (pti->bCalledYet)
{
// Save the real task's state
SAVE_CPU_STATE;
__asm mov s_uiRealSS, ss
__asm mov s_uiRealSP, sp
// Restore the thread's state.
// Can't dereference pti in __asm statement.
s_uiTmpSS = pti->ss;
s_uiTmpSP = pti->sp;
__asm mov ss, s_uiTmpSS
__asm mov sp, s_uiTmpSP
RESTORE_CPU_STATE;
// Return back to thread as if we are returning from ThrYield().
POP_DI_AND_SI;
__asm leave
__asm retf
}
else
{
pti->bCalledYet = TRUE;
s_tpTmp = pti->tpStart;
s_dwTmp = pti->dwParam;
// Save the real task's state
SAVE_CPU_STATE;
__asm mov s_uiRealSS, ss
__asm mov s_uiRealSP, sp
// Set up the thread's stack
s_uiTmpSS = pti->ss;
s_uiTmpSP = pti->sp;
__asm mov ss, s_uiTmpSS
__asm mov sp, s_uiTmpSP
// Kick off the thread
s_tpTmp(s_dwTmp);
// Thread routine returned. It is finished, so restore
// the task's real state and delete the thread.
__asm mov ss, s_uiRealSS
__asm mov sp, s_uiRealSP
RESTORE_CPU_STATE;
NukeThread(pti);
}
}
else s_bThreadRunning = FALSE;
}
} /*ThreadTimerProc*/
// The thread requests termination, so get rid of it and continue on. This
// should only be called by a thread, not by the main app thread.
int ThrExit(void)
{
if (s_bThreadRunning) // make sure
{
// Restore state of machine to the point before thread was invoked.
__asm mov ss, s_uiRealSS
__asm mov sp, s_uiRealSP
RESTORE_CPU_STATE;
NukeThread(s_ptiCurrent);
// Return to Windows as if we are returning from ThreadTimerProc().
POP_DI_AND_SI;
__asm leave
__asm retf 0xa
}
return(0);
} /*ThrExit*/
// This should be called by threads every .05 to .1 seconds.
// This routine yields the CPU to other threads and other apps.
int ThrYield(void)
{
if (s_bThreadRunning) // make sure
{
// Save the thread's state. Can't dereference s_ptiCurrent
// in __asm statement, so store in a temporary buffer.
SAVE_CPU_STATE;
__asm mov s_uiTmpSS, ss
__asm mov s_uiTmpSP, sp
s_ptiCurrent->ss = s_uiTmpSS;
s_ptiCurrent->sp = s_uiTmpSP;
// Restore state of machine to point before the thread was invoked.
__asm mov ss, s_uiRealSS
__asm mov sp, s_uiRealSP
RESTORE_CPU_STATE;
s_bThreadRunning = FALSE;
// Return to Windows as if we are returning from ThreadTimerProc().
POP_DI_AND_SI;
__asm leave
__asm retf 0xa
}
return(0);
} /*ThrYield*/
// Allows an application to yield to its threads without dropping into app's
// main event queue. Returns TRUE if a thread was run.
int ThrAppYield(void)
{
MSG msg;
int iRet=FALSE;
if (PeekMessage(&msg,NULL,WM_TIMER,WM_TIMER,PM_NOREMOVE))
if (GetMessage(&msg,NULL,WM_TIMER,WM_TIMER))
{
iRet = TRUE;
DispatchMessage(&msg);
}
return(iRet);
} /*ThrAppYield*/
// Turns a thread on/off without killing the thread.
int ThrEnableThread(DWORD dwID,BOOL bEnable)
{
PTHREADINFO pti = (PTHREADINFO) dwID;
pti->bEnabled = bEnable;
TurnTimerOnOrOff();
return(0);
} /*ThrEnableThread*/
// Returns the number of threads that currently exist.
int ThrThreadCount(UINT FAR *puiCount)
{
*puiCount = s_uiThreadCount;
return(0);
} /*ThrThreadCount*/
// Kick off a thread. Return 0 if OK, nonzero otherwise.
// tp: Address of the thread procedure (cdecl)
// dwParam: Passed to the thread procedure
// uiStackSize: Amount to allocate for the stack (or 0 for 8192)
// bEnable: TRUE if thread should be initially runnable
// pdwID: returns the thread ID
int ThrCreateThread(THREADPROC tp,DWORD dwParam,UINT uiStackSize,
BOOL bEnable,DWORD FAR *pdwID)
{
GLOBALHANDLE gh;
PTHREADINFO pti;
int iRet=0;
*pdwID = 0L;
// Allocate empty thread structure and get it ready to run.
if (gh = GlobalAlloc(GHND,sizeof(THREADINFO)))
{
pti = (PTHREADINFO) GlobalLock(gh);
pti->gh = gh;
if (!uiStackSize)
uiStackSize = 8192;
if (gh = GlobalAlloc(GHND,uiStackSize))
{
*pdwID = (DWORD) pti;
s_uiThreadCount++;
pti->ghStack = gh;
pti->tpStart = tp;
pti->ss = HIWORD(GlobalLock(gh));
pti->sp = uiStackSize;
pti->bCalledYet = FALSE;
pti->dwParam = dwParam;
pti->bEnabled = bEnable;
// Put the new thread at the head of the linked list of threads.
pti->ptiNext = s_ptiList;
s_ptiList = pti;
TurnTimerOnOrOff();
}
else
{
iRet = 2;
gh = pti->gh;
GlobalUnlock(gh);
GlobalFree(gh);
}
}
else iRet = 1;
return(iRet);
} /*ThrCreateThread*/