/*++ 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; }