403 lines
8.3 KiB
C++
403 lines
8.3 KiB
C++
/*++
|
|
|
|
Copyright (c) 1998 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
tpswork.cpp
|
|
|
|
Abstract:
|
|
|
|
Contains Win32 thread pool services worker thread functions
|
|
|
|
Contents:
|
|
SHSetThreadPoolLimits
|
|
SHTerminateThreadPool
|
|
SHQueueUserWorkItem
|
|
SHCancelUserWorkItems
|
|
TerminateWorkers
|
|
TpsEnter
|
|
(InitializeWorkerThreadPool)
|
|
(StartIOWorkerThread)
|
|
(QueueIOWorkerRequest)
|
|
(IOWorkerThread)
|
|
(ExecuteIOWorkItem)
|
|
|
|
Author:
|
|
|
|
Richard L Firth (rfirth) 10-Feb-1998
|
|
|
|
Environment:
|
|
|
|
Win32 user-mode
|
|
|
|
Revision History:
|
|
|
|
10-Feb-1998 rfirth
|
|
Created
|
|
|
|
12-Aug-1998 rfirth
|
|
Rewritten for DEMANDTHREAD and LONGEXEC work items. Officially
|
|
divergent from original which was based on NT5 base thread pool API
|
|
|
|
--*/
|
|
|
|
#include "priv.h"
|
|
#include "threads.h"
|
|
#include "tpsclass.h"
|
|
|
|
//
|
|
// private prototypes
|
|
//
|
|
struct WorkItem {
|
|
LPTHREAD_START_ROUTINE pfnCallback;
|
|
LPVOID pContext;
|
|
HMODULE hModuleToFree;
|
|
};
|
|
|
|
PRIVATE
|
|
VOID
|
|
ExecuteWorkItem(
|
|
IN WorkItem *pItem
|
|
);
|
|
|
|
//
|
|
// global data
|
|
//
|
|
|
|
BOOL g_bTpsTerminating = FALSE;
|
|
|
|
const char g_cszShlwapi[] = "SHLWAPI.DLL";
|
|
|
|
DWORD g_ActiveRequests = 0;
|
|
DWORD g_dwTerminationThreadId = 0;
|
|
BOOL g_bDeferredWorkerTermination = FALSE;
|
|
|
|
|
|
|
|
//
|
|
// functions
|
|
//
|
|
|
|
LWSTDAPI_(BOOL)
|
|
SHSetThreadPoolLimits(
|
|
IN PSH_THREAD_POOL_LIMITS pLimits
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Change internal settings
|
|
|
|
Arguments:
|
|
|
|
pLimits - pointer to SH_THREAD_POOL_LIMITS structure containing limits
|
|
to set
|
|
|
|
Return Value:
|
|
|
|
BOOL
|
|
Success - TRUE
|
|
|
|
Failure - FALSE. See GetLastError() for more info
|
|
|
|
--*/
|
|
|
|
{
|
|
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
|
return FALSE;
|
|
}
|
|
|
|
LWSTDAPI_(BOOL)
|
|
SHTerminateThreadPool(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Required to clean up threads before unloading SHLWAPI
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
BOOL
|
|
Success - TRUE
|
|
|
|
Failure - FALSE
|
|
|
|
--*/
|
|
|
|
{
|
|
if (InterlockedExchange((PLONG)&g_bTpsTerminating, TRUE)) {
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// wait until all in-progress requests have finished
|
|
//
|
|
|
|
while (g_ActiveRequests != 0) {
|
|
SleepEx(0, FALSE);
|
|
}
|
|
|
|
//
|
|
// kill all wait threads
|
|
//
|
|
|
|
TerminateWaiters();
|
|
|
|
if (!g_bDeferredWaiterTermination) {
|
|
g_dwTerminationThreadId = 0;
|
|
g_bTpsTerminating = FALSE;
|
|
} else {
|
|
g_dwTerminationThreadId = GetCurrentThreadId();
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
LWSTDAPI_(BOOL)
|
|
SHQueueUserWorkItem(
|
|
IN LPTHREAD_START_ROUTINE pfnCallback,
|
|
IN LPVOID pContext,
|
|
IN LONG lPriority,
|
|
IN DWORD_PTR dwTag,
|
|
OUT DWORD_PTR * pdwId OPTIONAL,
|
|
IN LPCSTR pszModule OPTIONAL,
|
|
IN DWORD dwFlags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Queues a work item and associates a user-supplied tag for use by
|
|
SHCancelUserWorkItems()
|
|
|
|
N.B. IO work items CANNOT be cancelled due to the fact that they are
|
|
queued as APCs and there is no OS support to revoke an APC
|
|
|
|
Arguments:
|
|
|
|
pfnCallback - caller-supplied function to call
|
|
|
|
pContext - caller-supplied context argument to pfnCallback
|
|
|
|
lPriority - relative priority of non-IO work item. Default is 0
|
|
|
|
dwTag - caller-supplied tag for non-IO work item if TPS_TAGGEDITEM
|
|
|
|
pdwId - pointer to returned ID. Pass NULL if not required. ID will be
|
|
0 for an IO work item
|
|
|
|
pszModule - if specified, name of library (DLL) to load and free so that
|
|
the dll will reamin in our process for the lifetime of the work
|
|
item.
|
|
|
|
dwFlags - flags modifying request:
|
|
|
|
TPS_EXECUTEIO
|
|
- execute work item in I/O thread. If set, work item
|
|
cannot be tagged (and therefore cannot be subsequently
|
|
cancelled) and cannot have an associated priority
|
|
(both tag and priority are ignored for I/O work items)
|
|
|
|
TPS_TAGGEDITEM
|
|
- the dwTag field is meaningful
|
|
|
|
TPS_DEMANDTHREAD
|
|
- a thread will be created for this work item if one is
|
|
not currently available. DEMANDTHREAD work items are
|
|
queued at the head of the work queue. That is, they
|
|
get the highest priority
|
|
|
|
TPS_LONGEXECTIME
|
|
- caller expects this work item to take relatively long
|
|
time to complete (e.g. it could be in a UI loop). Work
|
|
items thus described remove a thread from the pool for
|
|
an indefinite amount of time
|
|
|
|
Return Value:
|
|
|
|
BOOL
|
|
Success - TRUE
|
|
|
|
Failure - FALSE. See GetLastError() for more info
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD error;
|
|
|
|
if (dwFlags & TPS_INVALID_FLAGS) {
|
|
error = ERROR_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
|
|
error = TpsEnter();
|
|
if (error != ERROR_SUCCESS) {
|
|
goto exit;
|
|
}
|
|
|
|
ASSERT(!(dwFlags & TPS_EXECUTEIO));
|
|
|
|
if (!(dwFlags & TPS_EXECUTEIO))
|
|
{
|
|
// Use NT Thread pool!
|
|
|
|
WorkItem *pItem = new WorkItem;
|
|
if (pItem)
|
|
{
|
|
pItem->pfnCallback = pfnCallback;
|
|
pItem->pContext = pContext;
|
|
if (pszModule && *pszModule)
|
|
{
|
|
pItem->hModuleToFree = LoadLibrary(pszModule);
|
|
}
|
|
ULONG uFlags = WT_EXECUTEDEFAULT;
|
|
if (dwFlags & TPS_LONGEXECTIME)
|
|
uFlags |= WT_EXECUTELONGFUNCTION;
|
|
|
|
error = ERROR_SUCCESS;
|
|
if (!QueueUserWorkItem((LPTHREAD_START_ROUTINE)ExecuteWorkItem, (PVOID)pItem, uFlags))
|
|
{
|
|
error = GetLastError();
|
|
if (pItem->hModuleToFree)
|
|
FreeLibrary(pItem->hModuleToFree);
|
|
|
|
delete pItem;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
error = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
error = ERROR_CALL_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
|
|
TpsLeave();
|
|
|
|
exit:
|
|
|
|
BOOL success = TRUE;
|
|
|
|
if (error != ERROR_SUCCESS) {
|
|
SetLastError(error);
|
|
success = FALSE;
|
|
}
|
|
return success;
|
|
}
|
|
|
|
LWSTDAPI_(DWORD)
|
|
SHCancelUserWorkItems(
|
|
IN DWORD_PTR dwTagOrId,
|
|
IN BOOL bTag
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Cancels one or more queued work items. By default, if ID is supplied, only
|
|
one work item can be cancelled. If tag is supplied, all work items with same
|
|
tag will be deleted
|
|
|
|
Arguments:
|
|
|
|
dwTagOrId - user-supplied tag or API-supplied ID of work item(s) to
|
|
cancel. Used as search key
|
|
|
|
bTag - TRUE if dwTagOrId is tag else ID
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - Number of work items successfully cancelled (0..0xFFFFFFFE)
|
|
|
|
Failure - 0xFFFFFFFF. Use GetLastError() for more info
|
|
|
|
ERROR_SHUTDOWN_IN_PROGRESS
|
|
- DLL being unloaded/support terminated
|
|
|
|
--*/
|
|
|
|
{
|
|
SetLastError(ERROR_ACCESS_DENIED);
|
|
return 0xFFFFFFFF;
|
|
}
|
|
|
|
//
|
|
// private functions
|
|
//
|
|
|
|
PRIVATE
|
|
VOID
|
|
ExecuteWorkItem(
|
|
IN WorkItem *pItem
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Executes a regular Work function. Runs in the NT thread pool
|
|
|
|
Arguments:
|
|
|
|
pItem - context information. Contains the worker function that needs to
|
|
be run and the hModule to free. We need to free it.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
HMODULE hModule = pItem->hModuleToFree;
|
|
LPTHREAD_START_ROUTINE pfn = pItem->pfnCallback;
|
|
LPVOID ctx = pItem->pContext;
|
|
delete pItem;
|
|
#ifdef DEBUG
|
|
HRESULT hrDebug = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
|
|
if (hrDebug == RPC_E_CHANGED_MODE)
|
|
{
|
|
ASSERTMSG(FALSE, "SHLWAPI Thread pool wrapper: Could not CoInitialize Appartment threaded. We got infected with an MTA!\n");
|
|
}
|
|
else
|
|
{
|
|
CoUninitialize();
|
|
}
|
|
#endif
|
|
|
|
// Do the work now
|
|
pfn(ctx);
|
|
|
|
#ifdef DEBUG
|
|
hrDebug = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
|
|
if (hrDebug == RPC_E_CHANGED_MODE)
|
|
{
|
|
ASSERTMSG(FALSE, "SHLWAPI Thread pool wrapper: Could not CoInitialize Appartment threaded. The task at %x forgot to CoUninitialize or "
|
|
"we got infected with an MTA!\n", pfn);
|
|
}
|
|
else
|
|
{
|
|
CoUninitialize();
|
|
}
|
|
#endif
|
|
|
|
if (hModule)
|
|
FreeLibrary(hModule);
|
|
|
|
}
|
|
|