|
|
/*++
Copyright (c) 1998 Microsoft Corporation
Module Name:
tpswait.cpp
Abstract:
Contains Win32 thread pool services wait functions
Contents: TerminateWaiters SHRegisterWaitForSingleObject SHUnregisterWait (InitializeWaitThreadPool) (FindWaitThreadInfo) (AddWait) (RemoveWait) (WaitThread)
Author:
Richard L Firth (rfirth) 10-Feb-1998
Environment:
Win32 user-mode
Notes:
Taken from NT-specific code written by Gurdeep Singh Pall (gurdeep)
Revision History:
10-Feb-1998 rfirth Created
--*/
#include "priv.h"
#include "threads.h"
#include "tpsclass.h"
#include "tpswait.h"
//
// private prototypes
//
PRIVATE DWORD InitializeWaitThreadPool( VOID );
PRIVATE DWORD FindWaitThreadInfo( OUT CWaitThreadInfo * * pInfo );
PRIVATE VOID AddWait( IN OUT CWaitAddRequest * pRequest );
PRIVATE VOID RemoveWait( IN CWaitRemoveRequest * pRequest );
PRIVATE VOID WaitThread( IN HANDLE hEvent );
//
// global data
//
CDoubleLinkedList g_WaitThreads; CCriticalSection_NoCtor g_WaitCriticalSection; BOOL g_StartedWaitInitialization = FALSE; BOOL g_CompletedWaitInitialization = FALSE; BOOL g_bDeferredWaiterTermination = FALSE;
//
// functions
//
VOID TerminateWaiters( VOID )
/*++
Routine Description:
Terminate waiter threads and global variables
Arguments:
None.
Return Value:
None.
--*/
{ if (g_CompletedWaitInitialization) { g_WaitCriticalSection.Acquire(); while (!g_WaitThreads.IsEmpty()) {
CWaitThreadInfo * pInfo;
pInfo = (CWaitThreadInfo *)g_WaitThreads.RemoveHead();
HANDLE hThread = pInfo->GetHandle();
pInfo->SetHandle(NULL); QueueNullFunc(hThread); SleepEx(0, FALSE); } g_WaitCriticalSection.Release(); g_WaitCriticalSection.Terminate(); g_StartedWaitInitialization = FALSE; g_CompletedWaitInitialization = FALSE; } if (TlsGetValue(g_TpsTls) == (LPVOID)TPS_WAITER_SIGNATURE) { g_bDeferredWaiterTermination = TRUE; } }
LWSTDAPI_(HANDLE) SHRegisterWaitForSingleObject( IN HANDLE hObject, IN WAITORTIMERCALLBACKFUNC pfnCallback, IN LPVOID pContext, IN DWORD dwWaitTime, IN LPCSTR lpszLibrary OPTIONAL, IN DWORD dwFlags )
/*++
Routine Description:
This routine adds a new wait request to the pool of objects being waited on.
Arguments:
hObject - handle to the object to be waited on
pfnCallback - routine called when the wait completes or a timeout occurs
pContext - opaque pointer passed in as an argument to pfnCallback
dwWaitTime - Timeout for the wait in milliseconds. 0 means dont timeout.
lpszLibrary - if specified, name of library (DLL) to reference
dwFlags - flags modifying request:
SRWSO_NOREMOVE - once the handle becomes signalled, do not remove it from the handle array. Intended to be used with auto-reset events which become unsignalled again as soon as the waiting thread is made runnable
Return Value:
HANDLE Success - Non-NULL handle of created wait object
Failure - NULL. Call GetLastError() for error code
--*/
{ InterlockedIncrement((LPLONG)&g_ActiveRequests);
HANDLE hWait = NULL; DWORD error = ERROR_SUCCESS;
if (g_bTpsTerminating) { error = ERROR_SHUTDOWN_IN_PROGRESS; // error code? looks valid - justmann
goto exit; }
if (dwFlags & SRWSO_INVALID_FLAGS) { error = ERROR_INVALID_PARAMETER; goto exit; }
//DWORD dwHandleFlags;
//
//if (!GetHandleInformation(hObject, &dwHandleFlags)) {
//
// //
// // error == ERROR_SUCCESS returns GetHandleInformation() last error
// //
//
// ASSERT(error == ERROR_SUCCESS);
//
// goto exit;
//}
//
// GetHandleInformation() doesn't work on Win95
//
if (WaitForSingleObject(hObject, 0) == WAIT_FAILED) {
//
// error == ERROR_SUCCESS returns WaitForSingleObject() last error
//
ASSERT(error == ERROR_SUCCESS);
goto exit; }
//
// initialize wait thread pool if it isn't already done
//
if (!g_CompletedWaitInitialization) { error = InitializeWaitThreadPool(); if (error != ERROR_SUCCESS) { goto exit; } }
//
// find or create a wait thread that can accomodate another wait request
//
CWaitThreadInfo * pInfo;
error = FindWaitThreadInfo(&pInfo); if (error == ERROR_SUCCESS) {
CWaitAddRequest request(hObject, pfnCallback, pContext, dwWaitTime, dwFlags, pInfo );
//
// queue an APC to the wait thread
//
BOOL bSuccess = QueueUserAPC((PAPCFUNC)AddWait, pInfo->GetHandle(), (ULONG_PTR)&request );
ASSERT(bSuccess);
if (bSuccess) {
//
// relinquish the timeslice until the other thread has initialized
//
request.WaitForCompletion();
//
// the returned handle is the address of the wait object copied to
// the wait thread's stack
//
hWait = request.GetWaitPointer(); } pInfo->Release(); }
exit:
if (error != ERROR_SUCCESS) { SetLastError(error); } ASSERT( 0 != g_ActiveRequests ); InterlockedDecrement((LPLONG)&g_ActiveRequests); return hWait; }
LWSTDAPI_(BOOL) SHUnregisterWait( IN HANDLE hWait )
/*++
Routine Description:
This routine removes the specified wait from the pool of objects being waited on. This routine will block until all callbacks invoked as a result of this wait have been executed. This function MUST NOT be invoked inside the callback routines.
Arguments:
hWait - 'handle' indentifying the wait request
Return Value:
BOOL Success - TRUE
Failure - FALSE. Call GetLastError() for error code
--*/
{ InterlockedIncrement((LPLONG)&g_ActiveRequests);
BOOL bSuccess = FALSE; DWORD error = ERROR_SUCCESS;
if (hWait) { if (!g_bTpsTerminating) {
CWaitThreadInfo * pInfo = ((CWait *)hWait)->GetThreadInfo(); CWaitRemoveRequest request(hWait);
//
// lock the thread control block
//
pInfo->Acquire();
//
// queue an APC to the wait thread
//
if (QueueUserAPC((PAPCFUNC)RemoveWait, pInfo->GetHandle(), (ULONG_PTR)&request )) {
//
// relinquish the timeslice until the other thread has initialized
//
request.WaitForCompletion(); if (!(bSuccess = (request.GetWaitPointer() != NULL))) { error = ERROR_OBJECT_NOT_FOUND; // error code? looks valid -justmann
} }
//
// release lock to the thread control block
//
pInfo->Release(); } else { error = ERROR_SHUTDOWN_IN_PROGRESS; // error code? looks valid -justmann
} } else { error = ERROR_INVALID_PARAMETER; } if (error != ERROR_SUCCESS) { SetLastError(error); } ASSERT( 0 != g_ActiveRequests ); InterlockedDecrement((LPLONG)&g_ActiveRequests); return bSuccess; }
//
// private functions
//
PRIVATE DWORD InitializeWaitThreadPool( VOID )
/*++
Routine Description:
This routine initializes all aspects of the thread pool.
Arguments:
None
Return Value:
DWORD Success - ERROR_SUCCESS
Failure -
--*/
{ DWORD error = ERROR_SUCCESS;
if (!InterlockedExchange((LPLONG)&g_StartedWaitInitialization, TRUE)) { g_WaitCriticalSection.Init(); g_WaitThreads.Init(); g_CompletedWaitInitialization = TRUE; } else {
//
// relinquish the timeslice until the other thread has initialized
//
while (!g_CompletedWaitInitialization) { SleepEx(0, FALSE); // Sleep(0) without an additional call/return
} } return error; }
PRIVATE DWORD FindWaitThreadInfo( OUT CWaitThreadInfo * * ppInfo )
/*++
Routine Description:
Walks thru the list of wait threads and finds one which can accomodate another wait. If one is not found then a new thread is created.
This routine returns with the thread's WaitThreadCriticalSecton owned if it is successful.
Arguments:
ppInfo - pointer to pointer to returned control block
Return Value:
DWORD Success - ERROR_SUCCESS
Failure -
--*/
{ HANDLE hThread = NULL;
//
// take exclusive lock to the wait threads list
//
g_WaitCriticalSection.Acquire();
do {
DWORD error;
//
// walk thru the list of Wait Threads and find a Wait thread that can
// accomodate a new wait request
//
//
// *Consider* finding a wait thread with least # of waits to facilitate
// better load balancing of waits
//
for (CWaitThreadInfo * pInfo = (CWaitThreadInfo *)g_WaitThreads.Next(); !g_WaitThreads.IsHead(pInfo); pInfo = (CWaitThreadInfo *)pInfo->Next()) {
//
// slight cheese: if hThread is not NULL then its because we just
// created a new thread. We know we have g_WaitCriticalSection held
// and no other thread can be accessing the new thread's control
// block, so we can now write in the handle to be used in future
// calls to QueueUserAPC. This saves us having to duplicate the
// thread handle in the new thread
//
if (hThread != NULL) { pInfo->SetHandle(hThread); }
//
// take exclusive lock to the wait thread control block
//
pInfo->Acquire();
//
// wait threads can accomodate up to MAX_WAITS (WaitForMultipleObject
// limit)
//
if (pInfo->IsAvailableEntry()) {
//
// found a thread with some wait slots available. Release lock
// on the wait threads list
//
*ppInfo = pInfo; g_WaitCriticalSection.Release(); return ERROR_SUCCESS; }
//
// release lock to thread control block
//
pInfo->Release(); }
//
// if we reach here, we don't have any more wait threads so create more
//
error = StartThread((LPTHREAD_START_ROUTINE)WaitThread, &hThread, TRUE);
//
// if thread creation fails then return the failure to caller
//
if (error != ERROR_SUCCESS) {
ASSERT(FALSE);
g_WaitCriticalSection.Release(); return error; }
//
// loop back now that we have created another thread and put new wait
// request in new thread
//
} while(TRUE); }
PRIVATE VOID AddWait( IN OUT CWaitAddRequest * pRequest )
/*++
Routine Description:
This routine is used for adding waits to the wait thread. It is executed in an APC.
Arguments:
pRequest - pointer to request object
Return Value:
None.
--*/
{ if (!g_bTpsTerminating) {
CWaitThreadInfo * pInfo = pRequest->GetThreadInfo(); CWait * pWait = pInfo->GetFreeWaiter();
//
// copy relevant fields from request object. C++ knows how to pull CWait
// object out of CWaitAddRequest object. Insert the wait request object in
// the list of active waits, in increasing expiration time order
//
*pWait = *pRequest; pInfo->InsertWaiter(pWait);
//
// return to the caller the address of the wait object on the wait thread's
// stack and indicate to the calling thread that this request is complete
//
pRequest->SetWaitPointer(pWait); } pRequest->SetComplete(); }
PRIVATE VOID RemoveWait( IN CWaitRemoveRequest * pRequest )
/*++
Routine Description:
This routine is used for deleting the specified wait. It is executed in an APC.
Arguments:
pRequest - pointer to request object
Return Value:
None.
--*/
{ if (!g_bTpsTerminating) { if (!pRequest->GetWaitPointer()->GetThreadInfo()->RemoveWaiter(pRequest->GetWaitPointer())) { pRequest->SetWaitPointer(NULL); } } pRequest->SetComplete(); }
PRIVATE VOID WaitThread( IN HANDLE hEvent )
/*++
Routine Description:
This routine is used for all waits in the wait thread pool
Arguments:
hEvent - event handle to signal once initialization is complete
Return Value:
None.
--*/
{ HMODULE hDll = LoadLibrary(g_cszShlwapi);
ASSERT(hDll != NULL); ASSERT(g_TpsTls != 0xFFFFFFFF);
TlsSetValue(g_TpsTls, (LPVOID)TPS_WAITER_SIGNATURE);
CWaitThreadInfo waitInfo(&g_WaitThreads);
SetEvent(hEvent);
while (!g_bTpsTerminating || (g_ActiveRequests != 0)) {
DWORD dwIndex = waitInfo.Wait(waitInfo.GetWaitTime());
if (g_bTpsTerminating && (g_ActiveRequests == 0)) { break; } if (dwIndex == WAIT_TIMEOUT) { waitInfo.ProcessTimeouts(); #pragma warning(push)
#pragma warning(disable:4296)
} else if ((dwIndex >= WAIT_OBJECT_0) #pragma warning(pop)
&& (dwIndex < (WAIT_OBJECT_0 + waitInfo.GetObjectCount()))) { waitInfo.ProcessCompletion(dwIndex); } else if ((dwIndex == 0xFFFFFFFF) && GetLastError() == ERROR_INVALID_HANDLE) { waitInfo.PurgeInvalidHandles(); } else {
ASSERT(dwIndex == WAIT_IO_COMPLETION);
} } while (waitInfo.GetHandle() != NULL) { SleepEx(0, FALSE); } if (GetCurrentThreadId() == g_dwTerminationThreadId) { g_bTpsTerminating = FALSE; g_bDeferredWaiterTermination = FALSE; g_dwTerminationThreadId = 0; } FreeLibraryAndExitThread(hDll, ERROR_SUCCESS); }
|