//+---------------------------------------------------------------------------- // // Copyright (C) 1997, Microsoft Corporation // // File: thrdpool.cpp // // Contents: implementation of thrdpool library // // Description: See header file. // // Functions: // // History: 03/15/97 Rajeev Rajan (rajeevr) Created // //----------------------------------------------------------------------------- #include #include #include LONG CWorkerThread::m_lInitCount = -1 ; HANDLE CWorkerThread::m_hCompletionPort = NULL ; BOOL CWorkerThread::InitClass( DWORD dwConcurrency ) { TraceFunctEnter("CWorkerThread::InitClass"); if( InterlockedIncrement( &m_lInitCount ) == 0 ) { // // called for the first time - go ahead with initialization // m_hCompletionPort = CreateIoCompletionPort( INVALID_HANDLE_VALUE, NULL, 0, dwConcurrency ); if( !m_hCompletionPort ) { ErrorTrace(0, "Failed to create completion port: GetLastError is %d", GetLastError()); return FALSE ; } } else { // // bogus Init or already called // InterlockedDecrement( &m_lInitCount ); return FALSE ; } DebugTrace(0,"Created completion port 0x%x", m_hCompletionPort); TraceFunctLeave(); return TRUE ; } BOOL CWorkerThread::TermClass() { TraceFunctEnter("CWorkerThread::TermClass"); if( InterlockedDecrement( &m_lInitCount ) < 0 ) { // // Init has been called so go ahead with termination // _ASSERT( m_hCompletionPort ); _VERIFY( CloseHandle( m_hCompletionPort ) ); return TRUE ; } return FALSE ; } CWorkerThread::CWorkerThread() : m_hThread(NULL), m_hShutdownEvent( NULL ) { DWORD dwThreadId; TraceFunctEnter("CWorkerThread::CWorkerThread"); // // create shutdown event // if( !(m_hShutdownEvent = CreateEvent(NULL, TRUE, FALSE, NULL) ) ) { ErrorTrace(0,"Failed to create shutdown event"); _ASSERT( FALSE ); return; } // // create worker thread // if (!(m_hThread = ::CreateThread( NULL, 0, ThreadDispatcher, this, CREATE_SUSPENDED, &dwThreadId))) { ErrorTrace(0,"Failed to create thread: error: %d", GetLastError()); _ASSERT( FALSE ); } else { _VERIFY( ResumeThread( m_hThread ) != 0xFFFFFFFF ); } TraceFunctLeave(); return; } CWorkerThread::~CWorkerThread() { TraceFunctEnter("CWorkerThread::~CWorkerThread"); _ASSERT( m_hCompletionPort ); _ASSERT( m_hThread ); _ASSERT( m_hShutdownEvent ); // // signal worker thread to shutdown // this depends on derived class completion routines // checking this event - if they dont, we will block // till the thread finishes. // _VERIFY( SetEvent( m_hShutdownEvent ) ); // // post a null termination packet // if( !PostWork( NULL ) ) { ErrorTrace(0,"Error terminating worker thread"); _ASSERT( FALSE ); } // // wait for worker thread to terminate // DWORD dwWait = WaitForSingleObject(m_hThread, INFINITE); if(WAIT_OBJECT_0 != dwWait) { ErrorTrace(0,"WFSO: returned %d", dwWait); _ASSERT( FALSE ); } _VERIFY( CloseHandle(m_hThread) ); _VERIFY( CloseHandle(m_hShutdownEvent) ); m_hThread = NULL; m_hShutdownEvent = NULL; } DWORD __stdcall CWorkerThread::ThreadDispatcher(PVOID pvWorkerThread) { // // get pointer to this CWorkerThread object // CWorkerThread *pWorkerThread = (CWorkerThread *) pvWorkerThread; // // call GetQueuedCompletionStatus() to get work completion // pWorkerThread->GetWorkCompletion(); return 0; } VOID CWorkerThread::GetWorkCompletion(VOID) { DWORD dwBytesTransferred; DWORD_PTR dwCompletionKey; DWORD dwWait; LPOVERLAPPED lpo; LPWorkContextEnv lpWCE; PVOID pvWorkContext; TraceFunctEnter("CWorkerThread::GetWorkCompletion"); _ASSERT( m_hThread ); _ASSERT( m_hCompletionPort ); do { // // wait for work items to be queued // if( !GetQueuedCompletionStatus( m_hCompletionPort, &dwBytesTransferred, &dwCompletionKey, &lpo, INFINITE // wait timeout ) ) { ErrorTrace(0,"GetQueuedCompletionStatus() failed: error: %d", GetLastError()); break ; } // // get a hold of the work context envelope and work context // lpWCE = (LPWorkContextEnv) lpo; pvWorkContext = lpWCE->pvWorkContext; // // check for termination packet // if( pvWorkContext == NULL ) { DebugTrace(0,"Received termination packet - bailing"); delete lpWCE; lpWCE = NULL; break; } // // check for termination signal // dwWait = WaitForSingleObject( m_hShutdownEvent, 0 ); // // call derived class method to process work completion // if( WAIT_TIMEOUT == dwWait ) { DebugTrace(0,"Calling WorkCompletion() routine"); WorkCompletion( pvWorkContext ); } // // destroy the WorkContextEnv object allocated before PostQueuedCompletionStatus() // delete lpWCE; lpWCE = NULL; } while( TRUE ); return; } BOOL CWorkerThread::PostWork(PVOID pvWorkerContext) { TraceFunctEnter("CWorkerThread::PostWork"); _ASSERT( m_hThread ); _ASSERT( m_hCompletionPort ); // // allocate a WorkContextEnv blob - this is destroyed after GetQueuedCompletionStatus() // completes ! We may want to have a pool of such blobs instead of hitting the heap !! // LPWorkContextEnv lpWCE = new WorkContextEnv; if( !lpWCE ) { ErrorTrace(0,"Failed to allocate memory"); return FALSE ; } ZeroMemory( lpWCE, sizeof(WorkContextEnv) ); lpWCE->pvWorkContext = pvWorkerContext; if( !PostQueuedCompletionStatus( m_hCompletionPort, 0, 0, (LPOVERLAPPED)lpWCE ) ) { ErrorTrace(0,"PostQCompletionStatus() failed: error: %d", GetLastError()); return FALSE ; } return TRUE; }