windows-nt/Source/XPSP1/NT/shell/shlwapi/tpstimer.cpp
2020-09-26 16:20:57 +08:00

966 lines
22 KiB
C++

/*++
Copyright (c) 1998 Microsoft Corporation
Module Name:
tpstimer.cpp
Abstract:
Contains Win32 thread pool services timer functions
Contents:
TerminateTimers
SHCreateTimerQueue
(IECreateTimerQueue)
SHDeleteTimerQueue
(IEDeleteTimerQueue)
SHSetTimerQueueTimer
(IESetTimerQueueTimer)
(NTSetTimerQueueTimer)
SHChangeTimerQueueTimer
(IEChangeTimerQueueTimer)
SHCancelTimerQueueTimer
(IECancelTimerQueueTimer)
(NTCancelTimerQueueTimer)
(InitializeTimerThread)
(TimerCleanup)
(CreateDefaultTimerQueue)
(DeleteDefaultTimerQueue)
(CleanupDefaultTimerQueue)
(TimerThread)
(DeleteTimerQueue)
(AddTimer)
(ChangeTimer)
(CancelTimer)
Author:
Richard L Firth (rfirth) 10-Feb-1998
Environment:
Win32 user-mode
Notes:
Code reworked in C++ from NT-specific C code written by Gurdeep Singh Pall
(gurdeep)
Revision History:
10-Feb-1998 rfirth
Created
--*/
#include "priv.h"
#include "threads.h"
#include "tpsclass.h"
#include "tpstimer.h"
//
// private prototypes
//
typedef HANDLE (WINAPI * t_CreateTimerQueue)(VOID);
typedef BOOL (WINAPI * t_DeleteTimerQueue)(HANDLE);
typedef HANDLE (WINAPI * t_SetTimerQueueTimer)(HANDLE,
WAITORTIMERCALLBACKFUNC,
LPVOID,
DWORD,
DWORD,
LPCSTR,
DWORD
);
typedef BOOL (WINAPI * t_ChangeTimerQueueTimer)(HANDLE, HANDLE, DWORD, DWORD);
typedef BOOL (WINAPI * t_CancelTimerQueueTimer)(HANDLE, HANDLE);
// These are KERNEL32 functions that do not match our SHLWAPI APIs
typedef BOOL (WINAPI * t_CreateTimerQueueTimer)(PHANDLE,
HANDLE,
WAITORTIMERCALLBACKFUNC,
LPVOID,
DWORD,
DWORD,
ULONG
);
typedef BOOL (WINAPI * t_DeleteTimerQueueTimer)(HANDLE, HANDLE, HANDLE);
PRIVATE
DWORD
InitializeTimerThread(
VOID
);
PRIVATE
VOID
TimerCleanup(
VOID
);
PRIVATE
HANDLE
CreateDefaultTimerQueue(
VOID
);
PRIVATE
VOID
DeleteDefaultTimerQueue(
VOID
);
PRIVATE
VOID
CleanupDefaultTimerQueue(
VOID
);
PRIVATE
VOID
TimerThread(
VOID
);
PRIVATE
VOID
DeleteTimerQueue(
IN CTimerQueueDeleteRequest * pRequest
);
PRIVATE
VOID
AddTimer(
IN CTimerAddRequest * pRequest
);
PRIVATE
VOID
ChangeTimer(
IN CTimerChangeRequest * pRequest
);
PRIVATE
VOID
CancelTimer(
IN CTimerCancelRequest * pRequest
);
//
// global data
//
CTimerQueueList g_TimerQueueList;
HANDLE g_hDefaultTimerQueue = NULL;
HANDLE g_hTimerThread = NULL;
DWORD g_dwTimerId = 0;
LONG g_UID = 0;
BOOL g_bTimerInit = FALSE;
BOOL g_bTimerInitDone = FALSE;
BOOL g_bDeferredTimerTermination = FALSE;
//
// Forward-declared data.
//
extern t_CreateTimerQueue _I_CreateTimerQueue;
extern t_DeleteTimerQueue _I_DeleteTimerQueue;
extern t_SetTimerQueueTimer _I_SetTimerQueueTimer;
extern t_ChangeTimerQueueTimer _I_ChangeTimerQueueTimer;
extern t_CancelTimerQueueTimer _I_CancelTimerQueueTimer;
extern t_CreateTimerQueueTimer _I_CreateTimerQueueTimer;
extern t_DeleteTimerQueueTimer _I_DeleteTimerQueueTimer;
//
// Wrappers for NT5 because the Shlwapi version differs slightly from the
// NT version.
//
LWSTDAPI_(HANDLE)
NTSetTimerQueueTimer(
IN HANDLE hQueue,
IN WAITORTIMERCALLBACKFUNC pfnCallback,
IN LPVOID pContext,
IN DWORD dwDueTime,
IN DWORD dwPeriod,
IN LPCSTR lpszLibrary OPTIONAL,
IN DWORD dwFlags
)
{
//
// Translate the flags from TPS flags to WT flags.
//
DWORD dwWTFlags = 0;
if (dwFlags & TPS_EXECUTEIO) dwWTFlags |= WT_EXECUTEINIOTHREAD;
if (dwFlags & TPS_LONGEXECTIME) dwWTFlags |= WT_EXECUTELONGFUNCTION;
HANDLE hTimer;
if (_I_CreateTimerQueueTimer(&hTimer, hQueue, pfnCallback, pContext, dwDueTime, dwPeriod, dwWTFlags))
{
return hTimer;
}
return NULL;
}
LWSTDAPI_(BOOL)
NTCancelTimerQueueTimer(
IN HANDLE hQueue,
IN HANDLE hTimer
)
{
return _I_DeleteTimerQueueTimer(hQueue, hTimer, INVALID_HANDLE_VALUE);
}
STDAPI_(void) InitTimerQueue()
{
if (IsOS(OS_WHISTLERORGREATER))
{
HMODULE hKernel32 = GetModuleHandle("KERNEL32.DLL");
if (hKernel32)
{
t_CreateTimerQueue NTCreateTimerQueue;
t_DeleteTimerQueue NTDeleteTimerQueue;
t_CreateTimerQueueTimer NTCreateTimerQueueTimer;
t_ChangeTimerQueueTimer NTChangeTimerQueueTimer;
t_DeleteTimerQueueTimer NTDeleteTimerQueueTimer;
#define GetKernelProc(fn) \
((NT##fn = (t_##fn)GetProcAddress(hKernel32, #fn)) != NULL)
if (GetKernelProc(CreateTimerQueue) &&
GetKernelProc(DeleteTimerQueue) &&
GetKernelProc(CreateTimerQueueTimer) &&
GetKernelProc(ChangeTimerQueueTimer) &&
GetKernelProc(DeleteTimerQueueTimer))
{
#define SwitchToNTVersion(fn) (_I_##fn = NT##fn)
// Redirect the SHLWAPI APIs to the NT versions
// (They either point directly to the KERNEL API
// or to our stub functions.)
SwitchToNTVersion(CreateTimerQueue);
SwitchToNTVersion(DeleteTimerQueue);
SwitchToNTVersion(ChangeTimerQueueTimer);
SwitchToNTVersion(SetTimerQueueTimer);
SwitchToNTVersion(CancelTimerQueueTimer);
// Save these values so our stub functions can
// call the KERNEL API after they do their translation.
SwitchToNTVersion(CreateTimerQueueTimer);
SwitchToNTVersion(DeleteTimerQueueTimer);
}
#undef GetKernelProc
#undef SwitchToNTVersion
}
}
}
//
// functions
//
VOID
TerminateTimers(
VOID
)
/*++
Routine Description:
Terminate timer thread and global variables
Arguments:
None.
Return Value:
None.
--*/
{
if (g_bTimerInitDone) {
DWORD threadId = GetCurrentThreadId();
if ((g_hTimerThread != NULL) && (threadId != g_dwTimerId)) {
QueueNullFunc(g_hTimerThread);
DWORD ticks = GetTickCount();
while (g_hTimerThread != NULL) {
SleepEx(0, TRUE);
if (GetTickCount() - ticks > 10000) {
CloseHandle(g_hTimerThread);
g_hTimerThread = NULL;
break;
}
}
}
if (g_dwTimerId == threadId) {
g_bDeferredTimerTermination = TRUE;
} else {
TimerCleanup();
}
}
}
LWSTDAPI_(HANDLE)
SHCreateTimerQueue(
VOID
)
{
return _I_CreateTimerQueue();
}
LWSTDAPI_(HANDLE)
IECreateTimerQueue(
VOID
)
/*++
Routine Description:
Creates a timer queue
Arguments:
None.
Return Value:
HANDLE
Success - non-NULL pointer to CTimerQueue object
Failure - NULL. GetLastError() for more info
--*/
{
InterlockedIncrement((LPLONG)&g_ActiveRequests);
HANDLE hResult = NULL;
DWORD error = ERROR_SUCCESS;
if (!g_bTpsTerminating) {
if (g_hTimerThread == NULL) {
error = InitializeTimerThread();
}
if (error == ERROR_SUCCESS) {
//
// timer queue handle is just pointer to timer queue object
//
hResult = (HANDLE) new CTimerQueue(&g_TimerQueueList);
} else {
SetLastError(error);
}
} else {
SetLastError(ERROR_SHUTDOWN_IN_PROGRESS); // error code? looks valid -justmann
}
InterlockedDecrement((LPLONG)&g_ActiveRequests);
return hResult;
}
LWSTDAPI_(BOOL)
SHDeleteTimerQueue(
IN HANDLE hQueue
)
{
return _I_DeleteTimerQueue(hQueue);
}
LWSTDAPI_(BOOL)
IEDeleteTimerQueue(
IN HANDLE hQueue
)
/*++
Routine Description:
Deletes the specified timer queue
Arguments:
hQueue - handle of queue to delete; NULL for default timer queue
Return Value:
BOOL
Success - TRUE
Failure - FALSE. Call GetLastError() for more info
--*/
{
InterlockedIncrement((LPLONG)&g_ActiveRequests);
BOOL bSuccess = FALSE;
if (!g_bTpsTerminating) {
if (hQueue == NULL) {
hQueue = g_hDefaultTimerQueue;
}
if ((hQueue != NULL) && (g_hTimerThread != NULL)) {
CTimerQueueDeleteRequest request(hQueue);
if (QueueUserAPC((PAPCFUNC)DeleteTimerQueue,
g_hTimerThread,
(ULONG_PTR)&request)) {
request.WaitForCompletion();
bSuccess = request.SetThreadStatus();
} else {
#if DBG
DWORD error = GetLastError();
ASSERT(error == ERROR_SUCCESS);
#endif
}
} else {
SetLastError(ERROR_INVALID_PARAMETER); // correct error code? looks valid -justmann
}
} else {
SetLastError(ERROR_SHUTDOWN_IN_PROGRESS); // error code? looks valid -justmann
}
InterlockedDecrement((LPLONG)&g_ActiveRequests);
return bSuccess;
}
LWSTDAPI_(HANDLE)
SHSetTimerQueueTimer(
IN HANDLE hQueue,
IN WAITORTIMERCALLBACKFUNC pfnCallback,
IN LPVOID pContext,
IN DWORD dwDueTime,
IN DWORD dwPeriod,
IN LPCSTR lpszLibrary OPTIONAL,
IN DWORD dwFlags
)
{
return _I_SetTimerQueueTimer(hQueue, pfnCallback, pContext, dwDueTime, dwPeriod, lpszLibrary, dwFlags);
}
LWSTDAPI_(HANDLE)
IESetTimerQueueTimer(
IN HANDLE hQueue,
IN WAITORTIMERCALLBACKFUNC pfnCallback,
IN LPVOID pContext,
IN DWORD dwDueTime,
IN DWORD dwPeriod,
IN LPCSTR lpszLibrary OPTIONAL,
IN DWORD dwFlags
)
/*++
Routine Description:
Add a timer to a queue
Arguments:
hQueue - handle of timer queue; NULL for default queue
pfnCallback - function to call when timer triggers
pContext - parameter to pfnCallback
dwDueTime - initial firing time in milliseconds from now
dwPeriod - repeating period. 0 for one-shot
lpszLibrary - if specified, name of library (DLL) to reference
dwFlags - flags controlling function:
TPS_EXECUTEIO - Execute callback in I/O thread
Return Value:
HANDLE
Success - non-NULL handle
Failure - NULL. Call GetLastError() for more info
--*/
{
InterlockedIncrement((LPLONG)&g_ActiveRequests);
HANDLE hTimer = NULL;
if (!g_bTpsTerminating) {
DWORD error = ERROR_SUCCESS;
if (g_hTimerThread == NULL) {
error = InitializeTimerThread();
}
ASSERT(g_hTimerThread != NULL);
if (error == ERROR_SUCCESS) {
if (hQueue == NULL) {
hQueue = CreateDefaultTimerQueue();
}
if (hQueue != NULL) {
CTimerAddRequest * pRequest = new CTimerAddRequest(hQueue,
pfnCallback,
pContext,
dwDueTime,
dwPeriod,
dwFlags
);
if (pRequest != NULL) {
hTimer = pRequest->GetHandle();
if (QueueUserAPC((PAPCFUNC)AddTimer,
g_hTimerThread,
(ULONG_PTR)pRequest
)) {
} else {
#if DBG
error = GetLastError();
ASSERT(GetLastError() != ERROR_SUCCESS);
#endif
delete pRequest;
hTimer = NULL;
#if DBG
SetLastError(error);
#endif
}
}
}
} else {
SetLastError(error);
}
} else {
SetLastError(ERROR_SHUTDOWN_IN_PROGRESS); // error code? looks valid -justmann
}
InterlockedDecrement((LPLONG)&g_ActiveRequests);
return hTimer;
}
LWSTDAPI_(BOOL)
SHChangeTimerQueueTimer(
IN HANDLE hQueue,
IN HANDLE hTimer,
IN DWORD dwDueTime,
IN DWORD dwPeriod
)
{
return _I_ChangeTimerQueueTimer(hQueue, hTimer, dwDueTime, dwPeriod);
}
LWSTDAPI_(BOOL)
IEChangeTimerQueueTimer(
IN HANDLE hQueue,
IN HANDLE hTimer,
IN DWORD dwDueTime,
IN DWORD dwPeriod
)
/*++
Routine Description:
Change the due time or periodicity of a timer
Arguments:
hQueue - handle of queue on which timer resides. NULL for default queue
hTimer - handle of timer to change
dwDueTime - new due time
dwPeriod - new period
Return Value:
BOOL
Success - TRUE
Failure - FALSE. Call GetLastError() for more info
--*/
{
InterlockedIncrement((LPLONG)&g_ActiveRequests);
BOOL bSuccess = FALSE;
DWORD error = ERROR_SHUTDOWN_IN_PROGRESS; // error code? looks valid -justmann
if (!g_bTpsTerminating) {
error = ERROR_OBJECT_NOT_FOUND;
if (g_hTimerThread != NULL) {
if (hQueue == NULL) {
hQueue = g_hDefaultTimerQueue;
}
if (hQueue != NULL) {
CTimerChangeRequest request(hQueue, hTimer, dwDueTime, dwPeriod);
error = ERROR_SUCCESS; // both paths call SetLastError() if reqd
if (QueueUserAPC((PAPCFUNC)ChangeTimer,
g_hTimerThread,
(ULONG_PTR)&request
)) {
request.WaitForCompletion();
bSuccess = request.SetThreadStatus();
} else {
#if DBG
ASSERT(GetLastError() == ERROR_SUCCESS);
#endif
}
}
}
}
InterlockedDecrement((LPLONG)&g_ActiveRequests);
if (error != ERROR_SUCCESS) {
SetLastError(error);
}
return bSuccess;
}
LWSTDAPI_(BOOL)
SHCancelTimerQueueTimer(
IN HANDLE hQueue,
IN HANDLE hTimer
)
{
return _I_CancelTimerQueueTimer(hQueue, hTimer);
}
LWSTDAPI_(BOOL)
IECancelTimerQueueTimer(
IN HANDLE hQueue,
IN HANDLE hTimer
)
/*++
Routine Description:
Cancels a timer
Arguments:
hQueue - handle to queue on which timer resides
hTimer - handle of timer to cancel
Return Value:
BOOL
Success - TRUE
Failure - FALSE. Call GetLastError() for more info
--*/
{
InterlockedIncrement((LPLONG)&g_ActiveRequests);
BOOL bSuccess = FALSE;
if (!g_bTpsTerminating) {
if (hQueue == NULL) {
hQueue = g_hDefaultTimerQueue;
}
if ((hQueue != NULL) && (g_hTimerThread != NULL)) {
CTimerCancelRequest request(hQueue, hTimer);
if (QueueUserAPC((PAPCFUNC)CancelTimer,
g_hTimerThread,
(ULONG_PTR)&request
)) {
request.WaitForCompletion();
bSuccess = request.SetThreadStatus();
} else {
#if DBG
DWORD error = GetLastError();
ASSERT(error == ERROR_SUCCESS);
#endif
}
} else {
SetLastError(ERROR_INVALID_HANDLE);
}
} else {
SetLastError(ERROR_SHUTDOWN_IN_PROGRESS); // error code? looks valid -justmann
}
InterlockedDecrement((LPLONG)&g_ActiveRequests);
return bSuccess;
}
//
// private functions
//
PRIVATE
DWORD
InitializeTimerThread(
VOID
)
{
DWORD error = ERROR_SUCCESS;
while (!g_bTimerInitDone) {
if (!InterlockedExchange((LPLONG)&g_bTimerInit, TRUE)) {
//
// N.B. if CTimerQueueList::Init() does anything more than just
// initialize lists then add a Deinit()
//
g_TimerQueueList.Init();
ASSERT(g_hTimerThread == NULL);
error = StartThread((LPTHREAD_START_ROUTINE)TimerThread,
&g_hTimerThread,
FALSE
);
if (error == ERROR_SUCCESS) {
g_bTimerInitDone = TRUE;
} else {
InterlockedExchange((LPLONG)&g_bTimerInit, FALSE);
}
break;
} else {
SleepEx(0, FALSE);
}
}
return error;
}
PRIVATE
VOID
TimerCleanup(
VOID
)
{
while (!g_TimerQueueList.QueueListHead()->IsEmpty()) {
CTimerQueueDeleteRequest request((CTimerQueue *)
g_TimerQueueList.QueueListHead()->Next());
DeleteTimerQueue(&request);
}
DeleteDefaultTimerQueue();
g_UID = 0;
g_bTimerInit = FALSE;
g_bTimerInitDone = FALSE;
}
BOOL bDefaultQueueInit = FALSE;
BOOL bDefaultQueueInitDone = FALSE;
BOOL bDefaultQueueInitFailed = FALSE;
PRIVATE
HANDLE
CreateDefaultTimerQueue(
VOID
)
{
do {
if ((g_hDefaultTimerQueue != NULL) || bDefaultQueueInitFailed) {
return g_hDefaultTimerQueue;
}
if (!InterlockedExchange((LPLONG)&bDefaultQueueInit, TRUE)) {
InterlockedExchange((LPLONG)&bDefaultQueueInitDone, FALSE);
g_hDefaultTimerQueue = SHCreateTimerQueue();
if (g_hDefaultTimerQueue == NULL) {
bDefaultQueueInitFailed = TRUE;
InterlockedExchange((LPLONG)&bDefaultQueueInit, FALSE);
}
InterlockedExchange((LPLONG)&bDefaultQueueInitDone, TRUE);
} else {
do {
SleepEx(0, FALSE);
} while (!bDefaultQueueInitDone);
}
} while (TRUE);
}
PRIVATE
VOID
DeleteDefaultTimerQueue(
VOID
)
{
if (g_hDefaultTimerQueue != NULL) {
CTimerQueueDeleteRequest request((CTimerQueue *)g_hDefaultTimerQueue);
DeleteTimerQueue(&request);
g_hDefaultTimerQueue = NULL;
}
CleanupDefaultTimerQueue();
}
PRIVATE
VOID
CleanupDefaultTimerQueue(
VOID
)
{
g_hDefaultTimerQueue = NULL;
bDefaultQueueInit = FALSE;
bDefaultQueueInitDone = FALSE;
bDefaultQueueInitFailed = FALSE;
}
PRIVATE
VOID
TimerThread(
VOID
)
{
g_dwTimerId = GetCurrentThreadId();
HMODULE hDll = LoadLibrary(g_cszShlwapi);
ASSERT(hDll != NULL);
ASSERT(g_TpsTls != 0xFFFFFFFF);
TlsSetValue(g_TpsTls, (LPVOID)TPS_TIMER_SIGNATURE);
while (!g_bTpsTerminating || (g_ActiveRequests != 0)) {
if (g_TimerQueueList.Wait()) {
if (g_bTpsTerminating && (g_ActiveRequests == 0)) {
break;
}
g_TimerQueueList.ProcessCompletions();
}
}
ASSERT(g_hTimerThread != NULL);
CloseHandle(g_hTimerThread);
g_hTimerThread = NULL;
if (g_dwTimerId == g_dwTerminationThreadId) {
TimerCleanup();
g_bTpsTerminating = FALSE;
g_dwTerminationThreadId = 0;
g_bDeferredTimerTermination = FALSE;
}
g_dwTimerId = 0;
FreeLibraryAndExitThread(hDll, ERROR_SUCCESS);
}
PRIVATE
VOID
DeleteTimerQueue(
IN CTimerQueueDeleteRequest * pRequest
)
{
CTimerQueue * pQueue = (CTimerQueue *)pRequest->GetQueue();
DWORD dwStatus = ERROR_INVALID_PARAMETER;
if (g_TimerQueueList.FindQueue((CDoubleLinkedListEntry *)pQueue) != NULL) {
pQueue->DeleteTimers();
if (pQueue == g_hDefaultTimerQueue) {
CleanupDefaultTimerQueue();
}
delete pQueue;
dwStatus = ERROR_SUCCESS;
}
pRequest->SetCompletionStatus(dwStatus);
}
PRIVATE
VOID
AddTimer(
IN CTimerAddRequest * pRequest
)
{
CTimerQueue * pQueue = pRequest->GetQueue();
//
// add timer object to global list of timer objects, in expiration time
// order
//
pRequest->InsertBack(g_TimerQueueList.TimerListHead());
//
// add timer object to end of timer queue list in no particular order. Only
// used to delete all objects belonging to queue when queue is deleted
//
pRequest->TimerListHead()->InsertTail(pQueue->TimerListHead());
pRequest->SetComplete();
}
PRIVATE
VOID
ChangeTimer(
IN CTimerChangeRequest * pRequest
)
{
CTimerQueue * pQueue = (CTimerQueue *)pRequest->GetQueue();
CTimerQueueEntry * pTimer = pQueue->FindTimer(pRequest->GetTimer());
DWORD dwStatus = ERROR_INVALID_PARAMETER;
if (pTimer != NULL) {
pTimer->SetPeriod(pRequest->GetPeriod());
pTimer->SetExpirationTime(pRequest->GetDueTime());
dwStatus = ERROR_SUCCESS;
}
pRequest->SetCompletionStatus(dwStatus);
}
PRIVATE
VOID
CancelTimer(
IN CTimerCancelRequest * pRequest
)
{
CTimerQueue * pQueue = (CTimerQueue *)pRequest->GetQueue();
CTimerQueueEntry * pTimer = pQueue->FindTimer(pRequest->GetTimer());
DWORD dwStatus = ERROR_INVALID_PARAMETER;
if (pTimer != NULL) {
if (pTimer->IsInUse()) {
pTimer->SetCancelled();
} else {
pTimer->Remove();
delete pTimer;
}
dwStatus = ERROR_SUCCESS;
}
pRequest->SetCompletionStatus(dwStatus);
}
//
// Definitions of forward-declared data.
//
t_CreateTimerQueue _I_CreateTimerQueue = IECreateTimerQueue;
t_DeleteTimerQueue _I_DeleteTimerQueue = IEDeleteTimerQueue;
t_SetTimerQueueTimer _I_SetTimerQueueTimer = IESetTimerQueueTimer;
t_ChangeTimerQueueTimer _I_ChangeTimerQueueTimer = IEChangeTimerQueueTimer;
t_CancelTimerQueueTimer _I_CancelTimerQueueTimer = IECancelTimerQueueTimer;
//
// KERNEL functions that our NT stubs use. Not used if in IE mode.
//
t_CreateTimerQueueTimer _I_CreateTimerQueueTimer = NULL;
t_DeleteTimerQueueTimer _I_DeleteTimerQueueTimer = NULL;