You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
273 lines
5.9 KiB
273 lines
5.9 KiB
//+----------------------------------------------------------------------------
|
|
//
|
|
// 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 <windows.h>
|
|
#include <thrdpool.h>
|
|
#include <dbgtrace.h>
|
|
|
|
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;
|
|
}
|
|
|