375 lines
7.1 KiB
C
375 lines
7.1 KiB
C
/*++
|
|
|
|
Copyright (c) 1994 Microsoft Corporation
|
|
All rights reserved
|
|
|
|
Module Name:
|
|
|
|
ThreadM.c
|
|
|
|
Abstract:
|
|
|
|
Generic thread manager for spooler.
|
|
|
|
Author:
|
|
|
|
Albert Ting (AlbertT) 13-Feb-1994
|
|
|
|
Environment:
|
|
|
|
User Mode -Win32
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
#include "threadm.h"
|
|
|
|
#define ENTER_CRITICAL(pTMStateVar) \
|
|
EnterCriticalSection(pTMStateVar->pTMStateStatic->pCritSec)
|
|
#define LEAVE_CRITICAL(pTMStateVar) \
|
|
LeaveCriticalSection(pTMStateVar->pTMStateStatic->pCritSec)
|
|
|
|
//
|
|
// Prototypes
|
|
//
|
|
DWORD
|
|
xTMThreadProc(
|
|
LPVOID pVoid);
|
|
|
|
|
|
BOOL
|
|
TMCreateStatic(
|
|
PTMSTATESTATIC pTMStateStatic)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Intialize static state.
|
|
|
|
Arguments:
|
|
|
|
pTMStateStatic - static state to initialize
|
|
|
|
Return Value:
|
|
|
|
TRUE = success
|
|
FALSE = fail
|
|
|
|
--*/
|
|
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
TMDestroyStatic(
|
|
PTMSTATESTATIC pTMStateStatic)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Destroys static state.
|
|
|
|
Arguments:
|
|
|
|
pTMStateStatic - static state to destroy
|
|
|
|
Return Value:
|
|
|
|
VOID
|
|
|
|
--*/
|
|
|
|
{
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOL
|
|
TMCreate(
|
|
PTMSTATESTATIC pTMStateStatic,
|
|
PTMSTATEVAR pTMStateVar)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Creates a virtual TM object.
|
|
|
|
Arguments:
|
|
|
|
pTMStateStatic - static portion of the TM object that can be
|
|
used multiple times for subsequent instantiations.
|
|
|
|
pTMStateVar - variable portion of the structure; 1 per instantiation
|
|
|
|
Return Value:
|
|
|
|
TRUE = success
|
|
FALSE = fail
|
|
|
|
--*/
|
|
|
|
{
|
|
pTMStateVar->hTrigger = CreateEvent(NULL,
|
|
FALSE,
|
|
FALSE,
|
|
NULL);
|
|
|
|
if (!pTMStateVar->hTrigger)
|
|
return FALSE;
|
|
|
|
pTMStateVar->uIdleThreads = 0;
|
|
pTMStateVar->uActiveThreads = 0;
|
|
pTMStateVar->Status = TMSTATUS_NULL;
|
|
pTMStateVar->pTMStateStatic = pTMStateStatic;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
TMDestroy(
|
|
PTMSTATEVAR pTMStateVar)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Destroy TM object. If threads are currently processing the object,
|
|
mark it pending and return.
|
|
|
|
Arguments:
|
|
|
|
pTMStateVar - TM Object to destroy
|
|
|
|
Return Value:
|
|
|
|
TRUE = success
|
|
FALSE = fail
|
|
|
|
--*/
|
|
|
|
{
|
|
ENTER_CRITICAL(pTMStateVar);
|
|
|
|
pTMStateVar->Status |= TMSTATUS_DESTROY_REQ;
|
|
|
|
if (!pTMStateVar->uActiveThreads) {
|
|
|
|
//
|
|
// Mark as destroyed so that no more jobs are processed.
|
|
//
|
|
pTMStateVar->Status |= TMSTATUS_DESTROYED;
|
|
|
|
LEAVE_CRITICAL(pTMStateVar);
|
|
|
|
if (pTMStateVar->pTMStateStatic->pfnCloseState)
|
|
(*pTMStateVar->pTMStateStatic->pfnCloseState)(pTMStateVar);
|
|
|
|
} else {
|
|
|
|
LEAVE_CRITICAL(pTMStateVar);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
TMAddJob(
|
|
PTMSTATEVAR pTMStateVar)
|
|
{
|
|
DWORD dwThreadId;
|
|
HANDLE hThread;
|
|
BOOL rc = TRUE;
|
|
|
|
ENTER_CRITICAL(pTMStateVar);
|
|
|
|
if (pTMStateVar->Status & TMSTATUS_DESTROY_REQ) {
|
|
|
|
rc = FALSE;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Check if we can give it to an idle thread.
|
|
//
|
|
if (pTMStateVar->uIdleThreads) {
|
|
|
|
pTMStateVar->uIdleThreads--;
|
|
|
|
DBGMSG(DBG_NOTIFY, ("Trigger event: uIdleThreads = %d\n",
|
|
pTMStateVar->uIdleThreads));
|
|
|
|
SetEvent(pTMStateVar->hTrigger);
|
|
|
|
} else if (pTMStateVar->uActiveThreads <
|
|
pTMStateVar->pTMStateStatic->uMaxThreads) {
|
|
|
|
//
|
|
// We have less active threads than the max; create a new one.
|
|
//
|
|
DBGMSG(DBG_NOTIFY, ("Create thread: uActiveThreads = %d\n",
|
|
pTMStateVar->uActiveThreads));
|
|
|
|
hThread = CreateThread(NULL,
|
|
0,
|
|
xTMThreadProc,
|
|
pTMStateVar,
|
|
0,
|
|
&dwThreadId);
|
|
if (hThread) {
|
|
|
|
CloseHandle(hThread);
|
|
|
|
//
|
|
// We have successfully created a thread; up the
|
|
// count.
|
|
//
|
|
pTMStateVar->uActiveThreads++;
|
|
|
|
} else {
|
|
|
|
rc = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
LEAVE_CRITICAL(pTMStateVar);
|
|
|
|
return rc;
|
|
}
|
|
|
|
DWORD
|
|
xTMThreadProc(
|
|
LPVOID pVoid)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Worker thread routine that calls the client to process the jobs.
|
|
|
|
Arguments:
|
|
|
|
pVoid - pTMStateVar
|
|
|
|
Return Value:
|
|
|
|
Ignored.
|
|
|
|
--*/
|
|
|
|
{
|
|
PTMSTATEVAR pTMStateVar = (PTMSTATEVAR)pVoid;
|
|
PJOB pJob;
|
|
BOOL bQuit = FALSE;
|
|
|
|
pJob = (*pTMStateVar->pTMStateStatic->pfnNextJob)(pTMStateVar);
|
|
|
|
do {
|
|
|
|
while (pJob) {
|
|
|
|
//
|
|
// Call back to client to process the job.
|
|
//
|
|
(*pTMStateVar->pTMStateStatic->pfnProcessJob)(pTMStateVar, pJob);
|
|
|
|
//
|
|
// If we are pending shutdown, quit now.
|
|
//
|
|
if (pTMStateVar->Status & TMSTATUS_DESTROY_REQ) {
|
|
bQuit = TRUE;
|
|
break;
|
|
}
|
|
|
|
pJob = (*pTMStateVar->pTMStateStatic->pfnNextJob)(pTMStateVar);
|
|
}
|
|
|
|
ENTER_CRITICAL(pTMStateVar);
|
|
|
|
pTMStateVar->uIdleThreads++;
|
|
pTMStateVar->uActiveThreads--;
|
|
|
|
DBGMSG(DBG_NOTIFY, ("Going to sleep: uIdle = %d, uActive = %d\n",
|
|
pTMStateVar->uIdleThreads,
|
|
pTMStateVar->uActiveThreads));
|
|
|
|
LEAVE_CRITICAL(pTMStateVar);
|
|
|
|
if (bQuit)
|
|
break;
|
|
|
|
//
|
|
// Done, now relax and go idle for a bit. We don't care whether
|
|
// we timeout or get triggered; in either case we check for another
|
|
// job.
|
|
//
|
|
WaitForSingleObject(pTMStateVar->hTrigger,
|
|
pTMStateVar->pTMStateStatic->uIdleLife);
|
|
|
|
ENTER_CRITICAL(pTMStateVar);
|
|
|
|
if (pTMStateVar->Status & TMSTATUS_DESTROY_REQ) {
|
|
|
|
pJob = NULL;
|
|
|
|
} else {
|
|
|
|
//
|
|
// We must check here instead of relying on the return value
|
|
// of WaitForSingleObject since someone may see uIdleThreads!=0
|
|
// and set the trigger, but we timeout before it gets set.
|
|
//
|
|
pJob = (*pTMStateVar->pTMStateStatic->pfnNextJob)(pTMStateVar);
|
|
|
|
}
|
|
|
|
if (pJob) {
|
|
|
|
pTMStateVar->uActiveThreads++;
|
|
|
|
DBGMSG(DBG_NOTIFY, ("Woke and found job: uActiveThreads = %d\n",
|
|
pTMStateVar->uActiveThreads));
|
|
} else {
|
|
|
|
if (!pTMStateVar->uIdleThreads) {
|
|
|
|
//
|
|
// We may add a job that already is on the list, so
|
|
// uIdleThreads gets dec'd twice, but only 1 job left.
|
|
//
|
|
DBGMSG(DBG_NOTIFY, ("threadm: No jobs, yet no idle threads\n"));
|
|
|
|
} else {
|
|
|
|
//
|
|
// No jobs, we are going to exit, so we are no longer idle
|
|
//
|
|
pTMStateVar->uIdleThreads--;
|
|
}
|
|
}
|
|
|
|
LEAVE_CRITICAL(pTMStateVar);
|
|
|
|
} while (pJob);
|
|
|
|
DBGMSG(DBG_NOTIFY, ("No active jobs: uIdleThreads = %d, uActiveThreads = %d\n",
|
|
pTMStateVar->uIdleThreads,
|
|
pTMStateVar->uActiveThreads));
|
|
|
|
return 0;
|
|
}
|
|
|