Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

702 lines
19 KiB

/****************************** Module Header ******************************\
* Module Name: timers.c
*
* Copyright (c) 1985-1996, Microsoft Corporation
*
* This module contains the user timer APIs and support routines.
*
* History:
* 12-Nov-1990 DarrinM Created.
* 08-Apr-1992 DarrinM Switched to PM/Win3-like ScanTimers model.
\***************************************************************************/
#define _TIMERS 1 // uses a LARGE_INTEGER
#include "precomp.h"
#pragma hdrstop
/*
* Make sure that if we return a timer id that it is a WORD value. This
* will ensure that WOW doesn't need to handle-translate return values
* from SetTimer().
*
* Start with a large number so that FindTimer() doesn't find a timer we
* calculated with a low cTimerId if the app happens to pass in NULL pwnd
* and a low id (like 1).
*/
#define TIMERID_MAX 0x7FFF
#define TIMERID_MIN 0x100
#define ELLAPSED_MAX 0x7FFFFFFF
#define SYSRIT_TIMER (TMRF_SYSTEM | TMRF_RIT)
WORD cTimerId = TIMERID_MAX;
/***************************************************************************\
* _SetTimer (API)
*
* This API will start the specified timer.
*
* History:
* 15-Nov-1990 DavidPe Created.
\***************************************************************************/
UINT _SetTimer(
PWND pwnd,
UINT nIDEvent,
UINT dwElapse,
WNDPROC_PWND pTimerFunc)
{
/*
* Prevent apps from setting a Timer with a window proc to another app
*/
if (pwnd && (PpiCurrent() != GETPTI(pwnd)->ppi)) {
RIPERR1(ERROR_ACCESS_DENIED,
RIP_WARNING,
"Calling SetTimer with window of another process %lX",
pwnd);
return 0;
}
return InternalSetTimer(pwnd, nIDEvent, dwElapse, pTimerFunc, 0);
}
/***************************************************************************\
* _SetSystemTimer
*
* This API will start start a system timer which will generate WM_SYSTIMER
* messages rather than WM_TIMER
*
* History:
* 15-Nov-1990 DavidPe Created.
* 21-Jan-1991 IanJa Prefix '_' denotes export function (not API)
\***************************************************************************/
UINT _SetSystemTimer(
PWND pwnd,
UINT nIDEvent,
DWORD dwElapse,
WNDPROC_PWND pTimerFunc)
{
/*
* Prevent apps from setting a Timer with a window proc to another app
*/
if (pwnd && PpiCurrent() != GETPTI(pwnd)->ppi) {
RIPERR1(ERROR_ACCESS_DENIED,
RIP_WARNING,
"Calling SetSystemTimer with window of another process %lX",
pwnd);
return 0;
}
return InternalSetTimer(pwnd, nIDEvent, dwElapse, pTimerFunc, TMRF_SYSTEM);
}
/***************************************************************************\
* DestroyTimer
*
* This function does the actual unlinking and freeing of the timer structure.
* I pulled it out of FindTimer() so it could be shared with DestroyQueues-
* Timers.
* Sets *pptmr to point to the next TIMER struct (NULL if none)
*
* History:
* 15-Feb-1991 DarrinM Pulled from FindTimer().
\***************************************************************************/
VOID DestroyTimer(
PTIMER *pptmr)
{
PTIMER ptmr;
CheckCritIn();
ptmr = *pptmr;
/*
* If this timer was just about to be processed, decrement
* the ready-count since we're blowing it off.
*/
if (ptmr->flags & TMRF_READY)
DecTimerCount(ptmr->pti);
/*
* Unlock the window
*/
Unlock(&ptmr->spwnd);
/*
* Unlink this timer
*/
*pptmr = ptmr->ptmrNext;
/*
* Free up the TIMER structure.
*/
UserFreePool((PVOID)ptmr);
}
/***************************************************************************\
* FindTimer
*
* This function will find a timer that matches the parameters. We also
* deal with killing timers here since it's easier to remove items from
* the list while we're scanning it.
*
* History:
* 15-Nov-1990 DavidPe Created.
\***************************************************************************/
PTIMER FindTimer(
PWND pwnd,
UINT nID,
UINT flags,
BOOL fKill)
{
PTIMER *pptmr;
pptmr = &gptmrFirst;
while (*pptmr != NULL) {
/*
* Is this the timer we're looking for?
*/
if (((*pptmr)->spwnd == pwnd) &&
((*pptmr)->nID == nID) &&
((*pptmr)->flags & SYSRIT_TIMER) == (flags & SYSRIT_TIMER)) {
/*
* Are we being called from KillTimer()? If so, destroy the
* timer. return != 0 because *pptmr is gone.
*/
if (fKill) {
DestroyTimer(pptmr);
return (PTIMER)TRUE;
}
/*
* Found the timer, break out of the loop.
*/
break;
}
/*
* No, try the next one.
*/
pptmr = &((*pptmr)->ptmrNext);
}
return *pptmr;
}
/***************************************************************************\
* InternalSetTimer
*
* This is the guts of SetTimer that actually gets things going.
*
* NOTE (darrinm): Technically there is a bit of latency (the time it takes
* between SetTimer's NtSetEvent and when the RIT wakes up and calls ScanTimers)
* between when SetTimer is called and when the counter starts counting down.
* This is uncool but it should be a very short amount of time because the RIT
* is high-priority. If it becomes a problem I know how to fix it.
*
* History:
* 15-Nov-1990 DavidPe Created.
\***************************************************************************/
UINT InternalSetTimer(
PWND pwnd,
UINT nIDEvent,
UINT dwElapse,
WNDPROC_PWND pTimerFunc,
UINT flags)
{
LARGE_INTEGER liT = {1, 0};
PTIMER ptmr;
PTHREADINFO ptiCurrent;
CheckCritIn();
/*
* 1.0 compatibility weirdness. Also, don't allow negative elapse times
* because this'll cause ScanTimers() to generate negative elapse times
* between timers.
*/
if ((dwElapse == 0) || (dwElapse > ELLAPSED_MAX))
dwElapse = 1;
/*
* Attempt to first locate the timer, then create a new one
* if one isn't found.
*/
if ((ptmr = FindTimer(pwnd, nIDEvent, flags, FALSE)) == NULL) {
/*
* Not found. Create a new one.
*/
ptmr = (PTIMER)UserAllocPoolWithQuota(sizeof(TIMER), TAG_TIMER);
if (ptmr == NULL)
return 0;
ptmr->spwnd = NULL;
if (pwnd == NULL) {
WORD timerIdInitial = cTimerId;
/*
* Pick a unique, unused timer ID.
*/
do {
if (--cTimerId <= TIMERID_MIN)
cTimerId = TIMERID_MAX;
if (cTimerId == timerIdInitial) {
/*
* Flat out of timers bud.
*/
UserFreePool(ptmr);
return 0;
}
} while (FindTimer(NULL, cTimerId, flags, FALSE) != NULL);
ptmr->nID = (UINT)cTimerId;
} else {
ptmr->nID = nIDEvent;
}
/*
* Link the new timer into the front of the list.
* Handily this works even when gptmrFirst is NULL.
*/
ptmr->ptmrNext = gptmrFirst;
gptmrFirst = ptmr;
} else {
/*
* If this timer was just about to be processed,
* decrement cTimersReady since we're resetting it.
*/
if (ptmr->flags & TMRF_READY)
DecTimerCount(ptmr->pti);
}
/*
* If pwnd is NULL, create a unique id by
* using the timer handle. RIT timers are 'owned' by the RIT pti
* so they are not deleted when the creating pti dies.
*
* We used to record the pti as the pti of the window if one was
* specified. This is not what Win 3.1 does and it broke 10862
* where some merge app was setting the timer on winword's window
* it it still expected to get the messages not winword.
*
* MS Visual C NT was counting on this bug in the NT 3.1 so if
* a thread sets a timer for a window in another thread in the
* same process the timer goes off in the thread of the window.
* You can see this by doing a build in msvcnt and the files being
* compiled do not show up.
*/
ptiCurrent = (PTHREADINFO)(W32GetCurrentThread()); /*
* This will be NULL
* for a non-GUI thread.
*/
if (pwnd == NULL) {
if (flags & TMRF_RIT) {
ptmr->pti = gptiRit;
} else {
ptmr->pti = ptiCurrent;
UserAssert(ptiCurrent);
}
} else {
/*
* As enforced in the API wrappers. We shouldn't get here
* any other way for an app timer.
*/
if (ptiCurrent->TIF_flags & TIF_16BIT) {
ptmr->pti = ptiCurrent;
UserAssert(ptiCurrent);
} else {
ptmr->pti = GETPTI(pwnd);
}
}
/*
* Initialize the timer-struct.
*
* NOTE: The ptiOptCreator is used to identify a JOURNAL-timer. We
* want to allow these timers to be destroyed when the creator
* thread goes away. For other threads that create timers across
* threads, we do not want to destroy these timers when the
* creator goes away. Currently, we're only checking for a
* TMRF_RIT. However, in the future we might want to add this
* same check for TMRF_SYSTEM.
*/
Lock(&(ptmr->spwnd), pwnd);
ptmr->cmsCountdown = ptmr->cmsRate = dwElapse;
ptmr->flags = flags | TMRF_INIT;
ptmr->pfn = pTimerFunc;
ptmr->ptiOptCreator = (flags & TMRF_RIT ? ptiCurrent : NULL);
/*
* Force the RIT to scan timers.
*
* N.B. The following code sets the raw input thread timer to expire
* at the absolute time 1 which is very far into the past. This
* causes the timer to immediately expire before the set timer
* call returns.
*/
if (ptiCurrent == gptiRit) {
/*
* Don't let RIT timer loop reset the master timer - we already have.
*/
gbMasterTimerSet = TRUE;
}
UserAssert(gptmrMaster);
KeSetTimer(gptmrMaster, liT, NULL);
/*
* Windows 3.1 returns the timer ID if non-zero, otherwise it returns 1.
*/
return (ptmr->nID == 0 ? 1 : ptmr->nID);
}
/***************************************************************************\
* _KillTimer (API)
*
* This API will stop a timer from sending WM_TIMER messages.
*
* History:
* 15-Nov-1990 DavidPe Created.
\***************************************************************************/
BOOL _KillTimer(
PWND pwnd,
UINT nIDEvent)
{
return KillTimer2(pwnd, nIDEvent, FALSE);
}
/***************************************************************************\
* _KillSystemTimer
*
* This API will stop a system timer from sending WM_SYSTIMER messages.
*
* History:
* 15-Nov-1990 DavidPe Created.
* 21-Jan-1991 IanJa Prefix '_' denotes export function (not API)
\***************************************************************************/
BOOL _KillSystemTimer(
PWND pwnd,
UINT nIDEvent)
{
return KillTimer2(pwnd, nIDEvent, TRUE);
}
/***************************************************************************\
* KillTimer2
*
* This is the guts of KillTimer that actually kills the timer.
*
* History:
* 15-Nov-1990 DavidPe Created.
\***************************************************************************/
BOOL KillTimer2(
PWND pwnd,
UINT nIDEvent,
BOOL fSystemTimer)
{
/*
* Call FindTimer() with fKill == TRUE. This will
* basically delete the timer.
*/
return (BOOL)FindTimer(pwnd,
nIDEvent,
(fSystemTimer ? TMRF_SYSTEM : 0),
TRUE);
}
/***************************************************************************\
* DestroyQueuesTimers
*
* This function scans through all the timers and destroys any that are
* associated with the specified queue.
*
* History:
* 15-Feb-1991 DarrinM Created.
\***************************************************************************/
VOID DestroyThreadsTimers(
PTHREADINFO pti)
{
PTIMER *pptmr;
pptmr = &gptmrFirst;
while (*pptmr != NULL) {
/*
* Is this one of the timers we're looking for? If so, destroy it.
*/
if ((*pptmr)->pti == pti || (*pptmr)->ptiOptCreator == pti) {
DestroyTimer(pptmr);
} else {
pptmr = &((*pptmr)->ptmrNext);
}
}
}
/***************************************************************************\
* DestroyWindowsTimers
*
* This function scans through all the timers and destroys any that are
* associated with the specified window.
*
* History:
* 04-Jun-1991 DarrinM Created.
\***************************************************************************/
VOID DestroyWindowsTimers(
PWND pwnd)
{
PTIMER *pptmr;
pptmr = &gptmrFirst;
while (*pptmr != NULL) {
/*
* Is this one of the timers we're looking for? If so, destroy it.
*/
if ((*pptmr)->spwnd == pwnd) {
DestroyTimer(pptmr);
} else {
pptmr = &((*pptmr)->ptmrNext);
}
}
}
/***************************************************************************\
* DoTimer
*
* This function gets called from xxxPeekMessage() if the QS_TIMER bit is
* set. If this timer is okay with the filter specified the appropriate
* WM_*TIMER message will be placed in 'pmsg' and the timer will be reset.
*
* History:
* 15-Nov-1990 DavidPe Created.
* 27-NOv-1991 DavidPe Changed to move 'found' timers to end of list.
\***************************************************************************/
BOOL DoTimer(
PWND pwndFilter,
UINT wMsgFilterMin,
UINT wMsgFilterMax)
{
PTHREADINFO pti;
PTIMER *pptmr;
PTIMER ptmr;
PTIMER ptmrNext;
PQMSG pqmsg;
CheckCritIn();
pti = PtiCurrent();
/*
* Search for a timer that belongs to this queue.
*/
pptmr = &gptmrFirst;
while (*pptmr != NULL) {
ptmr = *pptmr;
/*
* Has this timer gone off and is it one we're looking for?
*/
if ((ptmr->flags & TMRF_READY) &&
(ptmr->pti == pti) &&
CheckPwndFilter(ptmr->spwnd, pwndFilter)) {
/*
* We found an appropriate timer. Put it in the app's queue and
* return success.
*/
if ((pqmsg = AllocQEntry(&pti->mlPost)) != NULL) {
/*
* Store the message and set the QS_POSTMESSAGE bit so the
* thread knows it has a message.
*/
StoreQMessage(pqmsg,
ptmr->spwnd,
(UINT)((ptmr->flags & TMRF_SYSTEM) ?
WM_SYSTIMER : WM_TIMER),
(DWORD)ptmr->nID,
(LONG)ptmr->pfn,
0,
0);
SetWakeBit(pti, QS_POSTMESSAGE | QS_ALLPOSTMESSAGE);
}
/*
* Reset this timer.
*/
ptmr->flags &= ~TMRF_READY;
DecTimerCount(ptmr->pti);
/*
* If there are other timers in the system move this timer
* to the end of the list so other timers in for this queue
* get a chance to go off.
*/
ptmrNext = ptmr->ptmrNext;
if (ptmrNext != NULL) {
/*
* Remove ptmr from its place in the list.
*/
*pptmr = ptmrNext;
/*
* Move to the last TIMER of the list.
*/
while (ptmrNext->ptmrNext != NULL)
ptmrNext = ptmrNext->ptmrNext;
/*
* Insert this timer at the end.
*/
ptmrNext->ptmrNext = ptmr;
ptmr->ptmrNext = NULL;
}
return TRUE;
}
pptmr = &ptmr->ptmrNext;
}
return FALSE;
}
/***************************************************************************\
* DecTimerCount
*
* This routine decrements cTimersReady and clears QS_TIMER if the count
* goes down to zero.
*
* History:
* 21-Jan-1991 DavidPe Created.
\***************************************************************************/
VOID DecTimerCount(
PTHREADINFO pti)
{
CheckCritIn();
if (--pti->cTimersReady == 0)
pti->pcti->fsWakeBits &= ~QS_TIMER;
}
/***************************************************************************\
* JournalTimer
*
*
* History:
* 04-Mar-1991 DavidPe Created.
\***************************************************************************/
LONG JournalTimer(
PWND pwnd,
UINT message,
DWORD wParam,
LONG lParam)
{
PTHREADINFO pti;
DBG_UNREFERENCED_PARAMETER(pwnd);
DBG_UNREFERENCED_PARAMETER(message);
DBG_UNREFERENCED_PARAMETER(lParam);
/*
* We've already entered the critical section.
*/
if (pti = ((PTIMER)lParam)->ptiOptCreator)
WakeSomeone(pti->pq, pti->pq->msgJournal, NULL);
return 0;
}
/***************************************************************************\
* SetJournalTimer
*
* Sets an NT timer that goes off in 'dt' milliseconds and will wake
* up 'pti' at that time. This is used in journal playback code to
* simulate the timing in which events were originally given to the system.
*
* History:
* 04-Mar-1991 DavidPe Created.
\***************************************************************************/
void SetJournalTimer(
DWORD dt,
UINT msgJournal)
{
static UINT idJournal = 0;
PtiCurrent()->pq->msgJournal = msgJournal;
/*
* Remember idJournal - because TMRF_ONESHOT timers stay in the timer
* list - by remembering the idJournal, we always reuse the same timer
* rather than creating new ones always.
*/
idJournal = InternalSetTimer(NULL,
idJournal,
dt,
JournalTimer,
TMRF_RIT | TMRF_ONESHOT);
}
/***************************************************************************\
* StartTimers
*
* Prime the timer pump by starting the cursor restoration timer.
*
* History:
* 02-Apr-1992 DarrinM Created.
\***************************************************************************/
VOID StartTimers(VOID)
{
extern LONG HungAppDemon(PWND pwnd, UINT message, DWORD wParam, LONG lParam);
/*
* TMRF_RIT timers are called directly from ScanTimers -- no nasty
* thread switching for these boys.
*/
InternalSetTimer(NULL, 0, 1000, HungAppDemon, TMRF_RIT);
}