Leaked source code of windows server 2003
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.
 
 
 
 
 
 

846 lines
18 KiB

/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
vststmsg.cxx
Abstract:
Implementation of test message classes for the server
Brian Berkowitz [brianb] 05/22/2000
TBD:
Revision History:
Name Date Comments
brianb 05/22/2000 Created
ssteiner 06/07/2000 Split client and server portions into
two files. vststmsgclient.cxx contains
the client portion.
--*/
#include <stdafx.h>
#include <vststmsg.hxx>
#include <vststmsghandler.hxx>
void LogUnexpectedFailure(LPCWSTR wsz, ...);
/*++
Routine Description:
This needs to be run once on the server side to install all of the message handlers.
Arguments:
None
Return Value:
<Enter return values here>
--*/
static void
InstallServerMsgHandlers()
{
g_msgTypes[VSTST_MT_TEXT].pfnHandler = CVsTstMsgHandlerRoutines::PrintMessage;
g_msgTypes[VSTST_MT_IMMEDIATETEXT].pfnHandler = CVsTstMsgHandlerRoutines::PrintMessage;
g_msgTypes[VSTST_MT_FAILURE].pfnHandler = CVsTstMsgHandlerRoutines::HandleFailure;
g_msgTypes[VSTST_MT_OPERATIONFAILURE].pfnHandler = CVsTstMsgHandlerRoutines::HandleOperationFailure;
g_msgTypes[VSTST_MT_UNEXPECTEDEXCEPTION].pfnHandler = CVsTstMsgHandlerRoutines::HandleUnexpectedException;
g_msgTypes[VSTST_MT_SUCCESS].pfnHandler = CVsTstMsgHandlerRoutines::HandleSuccess;
}
CVsTstMsgHandler::CVsTstMsgHandler(
IN LPCWSTR pwszLogFileName
) :
m_bcsQueueInitialized(false),
m_pmsgFirst(NULL),
m_pmsgLast(NULL),
m_cbMaxMsgLength(0),
m_hThreadReader(NULL),
m_hThreadWorker(NULL),
m_hevtWorker(NULL),
m_hevtReader(NULL),
m_bReadEnabled(false),
m_bTerminateWorker(false),
m_bTerminateReader(false),
m_pipeList(NULL),
m_bcsPipeListInitialized(false),
m_cNtLog( pwszLogFileName )
{
//
// Initialize the general message types array and install the message
// handlers.
//
InitMsgTypes();
InstallServerMsgHandlers();
}
// free data allocated by the class
void CVsTstMsgHandler::FreeData()
{
if (m_bcsQueueInitialized)
{
m_csQueue.Term();
m_bcsQueueInitialized = false;
}
if (m_bcsPipeListInitialized)
{
m_csPipeList.Term();
m_bcsPipeListInitialized = false;
}
while(m_pmsgFirst)
{
VSTST_MSG_HDR *pmsgNext = m_pmsgFirst->pmsgNext;
delete m_pmsgFirst;
m_pmsgFirst = pmsgNext;
}
if (m_hThreadWorker)
{
CloseHandle(m_hThreadWorker);
m_hThreadWorker = NULL;
}
if (m_hevtWorker)
{
CloseHandle(m_hevtWorker);
m_hevtWorker = NULL;
}
if (m_hevtReader)
{
CloseHandle(m_hevtReader);
m_hevtReader = NULL;
}
}
CVsTstMsgHandler::~CVsTstMsgHandler()
{
ForceTermination();
FreeData();
}
// initailize message handler and worker thread
HRESULT CVsTstMsgHandler::Initialize(UINT cbMaxMsg)
{
m_cbMaxMsgLength = cbMaxMsg;
try
{
m_csQueue.Init();
m_bcsQueueInitialized = true;
m_csPipeList.Init();
m_bcsPipeListInitialized = true;
}
catch(...)
{
return E_UNEXPECTED;
}
HRESULT hr = S_OK;
m_hevtWorker = CreateEvent(NULL, TRUE, FALSE, NULL);
if (m_hevtWorker == NULL)
goto _ErrExit;
m_hevtReader = CreateEvent(NULL, TRUE, FALSE, NULL);
if (m_hevtReader == NULL)
goto _ErrExit;
DWORD tid;
m_hThreadWorker = CreateThread
(
NULL,
64*1024,
StartWorkerThread,
this,
0,
&tid
);
if (m_hThreadWorker == NULL)
goto _ErrExit;
return S_OK;
_ErrExit:
hr = HRESULT_FROM_WIN32(GetLastError());
FreeData();
return hr;
}
// adjust message pointers
bool CVsTstMsgHandler::AdjustPointers(VSTST_MSG_HDR *phdr)
{
VSTST_MSG_TYPE_TABLE *pType = &g_msgTypes[phdr->type];
BYTE *pb = phdr->rgb;
VOID **ppv = (VOID **) (pb + pType->cbFixed);
pb = (BYTE *) (ppv + pType->cVarPtr);
for(unsigned iVarPtr = 0; iVarPtr < pType->cVarPtr; iVarPtr++, ppv++)
{
*ppv = pb;
size_t cb;
switch(pType->pointerTypes[iVarPtr])
{
default:
return false;
case VSTST_VPT_BYTE:
cb = *(UINT *) pb;
break;
case VSTST_VPT_ANSI:
cb = strlen((char *) pb) + 1;
break;
case VSTST_VPT_UNICODE:
cb = (wcslen((WCHAR *) pb) + 1) * sizeof(WCHAR);
break;
}
// align to pointer boundary
cb = (cb + sizeof(PVOID) - 1) & ~(sizeof(PVOID) - 1);
pb += cb;
}
return true;
}
// process message immediately
bool CVsTstMsgHandler::ProcessMsgImmediate(VSTST_MSG_HDR *phdr)
{
if (!AdjustPointers(phdr))
return false;
VSTST_MSG_TYPE_TABLE *pType = &g_msgTypes[phdr->type];
try
{
pType->pfnHandler(phdr, &m_cNtLog);
}
catch(...)
{
return false;
}
return true;
}
// queue message for worker thrad
bool CVsTstMsgHandler::QueueMsg(VSTST_MSG_HDR *phdr)
{
BYTE *pbMsg = new BYTE[phdr->cbMsg];
if (pbMsg == NULL)
// can't allocate message, wait for queue to of messages
// to complete and then process messages serially
{
WaitForQueueToComplete();
return ProcessMsgImmediate(phdr);
}
memcpy(pbMsg, phdr, phdr->cbMsg);
VSTST_MSG_HDR *phdrT = (VSTST_MSG_HDR *) pbMsg;
if (!AdjustPointers(phdrT))
return false;
m_csQueue.Lock();
if (m_pmsgLast == NULL)
{
VSTST_ASSERT(m_pmsgFirst == NULL);
m_pmsgLast = m_pmsgFirst = phdrT;
SetEvent(m_hevtWorker);
}
else
{
VSTST_ASSERT(m_pmsgLast->pmsgNext == NULL);
m_pmsgLast->pmsgNext = phdrT;
// replace last element on queue
m_pmsgLast = phdrT;
}
m_csQueue.Unlock();
return true;
}
// execute items on the work queue
bool CVsTstMsgHandler::DoWork()
{
// Add this thread as a participant
m_cNtLog.AddParticipant();
// For now start the one and only variation
m_cNtLog.StartVariation( L"VssTestController" );
while(TRUE)
{
// wait for something to get on the work queue or for 1 second
if (WaitForSingleObject(m_hevtWorker, 1000) == WAIT_FAILED)
{
// Log severe error
m_cNtLog.Log( eSevLev_Severe, L"CVsTstMsgHandler::DoWork, WaitForSingleObject returned WAIT_FAILED, dwRet: %d", ::GetLastError() );
// End the variation
m_cNtLog.EndVariation();
// Remove the thread as a participant
m_cNtLog.RemoveParticipant();
return false;
}
while(TRUE)
{
// lock queue
m_csQueue.Lock();
// check whether queue is empty
if (m_pmsgFirst == NULL)
{
VSTST_ASSERT(m_pmsgLast == NULL);
// check whether we should terminate the thread
if (m_bTerminateWorker)
{
// terminate thread
m_csQueue.Unlock();
// End the variation
m_cNtLog.EndVariation();
// Remove the thread as a participant
m_cNtLog.RemoveParticipant();
return true;
}
// setup to wait again
ResetEvent(m_hevtWorker);
m_csQueue.Unlock();
break;
}
// pull first message off of queue
VSTST_MSG_HDR *phdr = m_pmsgFirst;
// move head of queue to next element
m_pmsgFirst = m_pmsgFirst->pmsgNext;
// is queue now empty
if (m_pmsgFirst == NULL)
{
VSTST_ASSERT(m_pmsgLast == phdr);
// set tail of queue to null
m_pmsgLast = NULL;
}
// unlock queue before executing item
m_csQueue.Unlock();
// execute item
VSTST_MSG_TYPE_TABLE *pType = &g_msgTypes[phdr->type];
try
{
pType->pfnHandler(phdr, &m_cNtLog );
}
catch(...)
{
// Log severe error
m_cNtLog.Log( eSevLev_Severe, L"CVsTstMsgHandler::DoWork, caught unexpected exception from message handler" );
// End the variation
m_cNtLog.EndVariation();
// Remove the thread as a participant
m_cNtLog.RemoveParticipant();
return false;
}
}
}
return true;
}
// terminate worker thread, waiting for all work to complete
void CVsTstMsgHandler::WaitForQueueToComplete()
{
m_bTerminateWorker = true;
if (WaitForSingleObject(m_hThreadWorker, INFINITE) == WAIT_FAILED)
{
// polling way to wait if we wait fails. Note that we usually
// would only expect to get here in stress situations
while(TRUE)
{
m_csQueue.Lock();
if (m_pmsgFirst == NULL)
{
m_csQueue.Unlock();
break;
}
m_csQueue.Unlock();
Sleep(100);
}
}
}
DWORD CVsTstMsgHandler::StartWorkerThread(VOID *pv)
{
CVsTstMsgHandler *pHandler = (CVsTstMsgHandler *) pv;
try
{
pHandler->DoWork();
}
catch(...)
{
LogUnexpectedFailure(L"Worker thread unexpectedly terminated");
}
return 0;
}
void CVsTstMsgHandler::StartProcessingMessages()
{
m_bReadEnabled = true;
SetEvent(m_hevtReader);
}
void CVsTstMsgHandler::StopProcessingMessages()
{
ResetEvent(m_hevtReader);
m_bReadEnabled = false;
}
void CVsTstMsgHandler::ForceTermination()
{
m_bReadEnabled = false;
m_bTerminateWorker = true;
m_bTerminateReader = true;
SetEvent(m_hevtReader);
if (m_hThreadWorker)
{
DWORD dwErr = WaitForSingleObject(m_hThreadWorker, 5000);
if (dwErr == WAIT_FAILED || dwErr == WAIT_TIMEOUT)
// force termination of worker thread
TerminateThread(m_hThreadWorker, 1);
CloseHandle(m_hThreadWorker);
m_hThreadWorker = NULL;
}
m_csPipeList.Lock();
while(m_pipeList != NULL)
{
CVsTstPipe *pipe = m_pipeList;
HANDLE hThread = pipe->m_hThreadReader;
pipe->m_hThreadReader = NULL;
m_csPipeList.Unlock();
m_pipeList->ForceTermination(hThread);
m_csPipeList.Lock();
if (m_pipeList == pipe)
delete m_pipeList;
}
m_csPipeList.Unlock();
}
// launch a pipe reader thread
HRESULT CVsTstMsgHandler::LaunchReader()
{
CVsTstPipe *pipe = new CVsTstPipe(this);
if (pipe == NULL)
return E_OUTOFMEMORY;
HRESULT hr = pipe->Initialize(m_cbMaxMsgLength);
if (FAILED(hr))
{
delete pipe;
return hr;
}
return S_OK;
}
// link pipe into pipe list
void CVsTstMsgHandler::LinkPipe(CVsTstPipe *pipe)
{
VSTST_ASSERT(!pipe->m_bLinked);
m_csPipeList.Lock();
pipe->m_prev = NULL;
pipe->m_next = m_pipeList;
if (m_pipeList)
{
VSTST_ASSERT(m_pipeList->m_prev == NULL);
m_pipeList->m_prev = pipe;
}
m_pipeList = pipe;
m_csPipeList.Unlock();
pipe->m_bLinked = true;
}
// unlink pipe from pipe list
void CVsTstMsgHandler::UnlinkPipe(CVsTstPipe *pipe)
{
VSTST_ASSERT(pipe->m_bLinked);
m_csPipeList.Lock();
if (pipe->m_prev == NULL)
{
VSTST_ASSERT(m_pipeList == pipe);
m_pipeList = pipe->m_next;
if (m_pipeList)
{
VSTST_ASSERT(m_pipeList->m_prev == pipe);
m_pipeList->m_prev = NULL;
}
}
else
{
VSTST_ASSERT(pipe->m_prev->m_next == pipe);
pipe->m_prev->m_next = pipe->m_next;
if (pipe->m_next)
{
VSTST_ASSERT(pipe->m_next->m_prev == pipe);
pipe->m_next->m_prev = pipe->m_prev;
}
}
m_csPipeList.Unlock();
pipe->m_bLinked = false;
}
// constructor for a pipe
CVsTstPipe::CVsTstPipe(CVsTstMsgHandler *pHandler) :
m_hPipe(NULL),
m_hevtOverlapped(NULL),
m_rgbMsg(NULL),
m_cbMsg(0),
m_hThreadReader(NULL),
m_bLinked(NULL),
m_pHandler(pHandler),
m_bConnected(false)
{
}
// destructor for a pipe
CVsTstPipe::~CVsTstPipe()
{
// unlink pipe from list if linked
if (m_bLinked)
{
VSTST_ASSERT(m_pHandler);
m_pHandler->UnlinkPipe(this);
}
FreeData();
}
// initailize message handler and worker thread
HRESULT CVsTstPipe::Initialize(UINT cbMaxMsg)
{
HRESULT hr = S_OK;
// create pipe
m_hPipe = CreateNamedPipe
(
s_wszPipeName,
FILE_FLAG_OVERLAPPED|PIPE_ACCESS_INBOUND,
PIPE_TYPE_MESSAGE|PIPE_READMODE_MESSAGE|PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES,
0,
cbMaxMsg,
100,
NULL
);
if (m_hPipe == INVALID_HANDLE_VALUE)
return HRESULT_FROM_WIN32(GetLastError());
// create message buffer
m_cbMsg = cbMaxMsg;
m_rgbMsg = new BYTE[m_cbMsg];
if (m_rgbMsg == NULL)
{
hr = E_OUTOFMEMORY;
goto _ErrCleanup;
}
// create overlapped read event
m_hevtOverlapped = CreateEvent(NULL, TRUE, FALSE, NULL);
if (m_hevtOverlapped == NULL)
goto _ErrExit;
// create reader thread
DWORD tid;
m_hThreadReader = CreateThread
(
NULL,
256*1024,
StartReaderThread,
this,
0,
&tid
);
if (m_hThreadReader == NULL)
goto _ErrExit;
// link pipe into list
m_pHandler->LinkPipe(this);
return S_OK;
_ErrExit:
hr = HRESULT_FROM_WIN32(GetLastError());
_ErrCleanup:
FreeData();
return hr;
}
// free data allocated by the class
void CVsTstPipe::FreeData()
{
delete m_rgbMsg;
m_rgbMsg = NULL;
if (m_hPipe != INVALID_HANDLE_VALUE)
{
// disconnect pipe if connected
if (m_bConnected)
{
DisconnectNamedPipe(m_hPipe);
m_bConnected = false;
}
CloseHandle(m_hPipe);
m_hPipe = NULL;
}
if (m_hThreadReader)
{
CloseHandle(m_hThreadReader);
m_hThreadReader = NULL;
}
if (m_hevtOverlapped)
{
CloseHandle(m_hevtOverlapped);
m_hevtOverlapped = NULL;
}
}
void CVsTstPipe::ForceTermination(HANDLE hThread)
{
// thread should already be terminated
DWORD dwErr = WaitForSingleObject(hThread, 5000);
if (dwErr == WAIT_FAILED || dwErr == WAIT_TIMEOUT)
// force termination of thread
TerminateThread(hThread, 1);
CloseHandle(hThread);
}
// setup overlapped I/O structure used for reading data
// from the pipe
void CVsTstPipe::SetupOverlapped()
{
VSTST_ASSERT(m_hevtOverlapped);
ResetEvent(m_hevtOverlapped);
memset(&m_overlap, 0, sizeof(m_overlap));
m_overlap.hEvent = m_hevtOverlapped;
}
bool CVsTstPipe::WaitForConnection()
{
SetupOverlapped();
if (ConnectNamedPipe(m_hPipe, &m_overlap))
return true;
if (GetLastError() == ERROR_IO_PENDING)
{
while(TRUE)
{
if (!m_pHandler->m_bReadEnabled)
{
CancelIo(m_hPipe);
return false;
}
DWORD dwErr = WaitForSingleObject(m_hevtOverlapped, 1000);
if (dwErr == WAIT_OBJECT_0)
break;
if (dwErr == WAIT_FAILED)
{
Sleep(1000);
CancelIo(m_hPipe);
return false;
}
}
}
return true;
}
// do the work of reading messages
VSTST_READER_STATUS CVsTstPipe::ReadMessages(bool bConnect)
{
if (bConnect)
{
if (!WaitForConnection())
return VSTST_RS_NOTCONNECTED;
// launch a new reader thread to wait for the next connection
m_pHandler->LaunchReader();
}
// while we are doing reads
while(m_pHandler->m_bReadEnabled)
{
DWORD cbRead;
// setup overlapped structure
SetupOverlapped();
if (!ReadFile
(
m_hPipe,
m_rgbMsg,
m_cbMsg,
&cbRead,
&m_overlap
))
{
DWORD dwErr = GetLastError();
if (dwErr == ERROR_IO_PENDING)
{
while(TRUE)
{
if (!m_pHandler->m_bReadEnabled)
{
CancelIo(m_hPipe);
return VSTST_RS_READDISABLED;
}
DWORD dwErr = WaitForSingleObject(m_hevtOverlapped, 1000);
if (dwErr == WAIT_OBJECT_0)
break;
if (dwErr == WAIT_FAILED)
{
Sleep(1000);
CancelIo(m_hPipe);
continue;
}
}
if (!GetOverlappedResult(m_hPipe, &m_overlap, &cbRead, FALSE))
{
CancelIo(m_hPipe);
continue;
}
}
else
{
// unexpected error reading from pipe
DWORD dwErr = GetLastError();
if (dwErr == ERROR_BROKEN_PIPE)
{
// terminate thread as we are no longer reading from
// the pipe
DisconnectNamedPipe(m_hPipe);
return VSTST_RS_DISCONNECTED;
}
else
ReadPipeError(dwErr);
return VSTST_RS_ERROR;
}
}
VSTST_MSG_HDR *phdr = (VSTST_MSG_HDR *) m_rgbMsg;
if (phdr->cbMsg != cbRead ||
phdr->type == VSTST_MT_UNDEFINED ||
phdr->type >= VSTST_MT_MAXMSGTYPE)
LogInvalidMessage(phdr);
else if (g_msgTypes[phdr->type].priority == VSTST_MP_IMMEDIATE)
{
if (!m_pHandler->ProcessMsgImmediate(phdr))
LogInvalidMessage(phdr);
}
else
{
if (!m_pHandler->QueueMsg(phdr))
LogInvalidMessage(phdr);
}
}
return VSTST_RS_READDISABLED;
}
DWORD CVsTstPipe::StartReaderThread(VOID *pv)
{
CVsTstPipe *pipe = (CVsTstPipe *) pv;
bool bConnected = false;
try
{
while(!pipe->m_pHandler->m_bTerminateReader)
{
if (WaitForSingleObject(pipe->m_pHandler->m_hevtReader, INFINITE) == WAIT_FAILED)
break;
VSTST_READER_STATUS status = pipe->ReadMessages(!bConnected);
if (status == VSTST_RS_DISCONNECTED)
{
bConnected = false;
break;
}
else if (status != VSTST_RS_NOTCONNECTED)
bConnected = true;
}
}
catch(...)
{
LogUnexpectedFailure(L"Read thread unexpectedly terminated");
}
delete pipe;
return 0;
}