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.
 
 
 
 
 
 

640 lines
18 KiB

/* timers.c -- Multiplexed timer routines -- run several timers off one
* Windows timer.
*
* Copyright 1994 by Hilgraeve, Inc. -- Monroe, MI
* All rights reserved
*
* $Revision: 6 $
* $Date: 5/29/02 2:17p $
*/
#include <windows.h>
#pragma hdrstop
#include "stdtyp.h"
#include <tdll\assert.h>
#include "mc.h"
#include "session.h"
#include "timers.h"
typedef struct s_timer ST_TIMER;
typedef struct s_timer_mux ST_TIMER_MUX;
struct s_timer_mux
{
HSESSION hSession;
HWND hWnd;
UINT uiID;
UINT_PTR uiTimer;
UINT uiLastDuration;
int fInMuxProc;
ST_TIMER *pstFirst;
ST_TIMER *pstCurrent;
};
struct s_timer
{
HSESSION hSession;
ST_TIMER *pstNext;
ST_TIMER_MUX *pstTimerMux;
long lInterval;
long lLastFired;
long lFireTime;
void *pvData;
TIMERCALLBACK pfCallback;
};
void TimerInsert(ST_TIMER_MUX *pstTimerMux, ST_TIMER *pstTimer);
int TimerSet(ST_TIMER_MUX *pstTimerMux);
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* FUNCTION: TimerMuxCreate
*
* DESCRIPTION:
* Creates a Timer Multiplexer from which any number of individual timers
* can be created. A Timer Multiplexer uses only one Windows Timer now
* matter how many individual timers are created from it.
*
* ARGUMENTS:
* pHTM -- A pointer to an HTIMERMUX handle. This handle must be used in
* subsequent calls to CreateTimer
*
* RETURNS:
* TIMER_OK if successful
* TIMER_NOMEM if there is insufficient memory
* TIMER_NOWINTIMER if there are no Windows timers available
* TIMER_ERROR if invalid parameters are passed.
*/
int TimerMuxCreate(const HWND hWnd, const UINT uiID, HTIMERMUX * const pHTM, const HSESSION hSession)
{
//
// sessQueryTimerMux() locks the session's TimerMux
// critical section. Call sessReleaseTimerMux() to unlock
// the session's TimerMux critical section. REV: 5/21/2002
//
HTIMERMUX hTM = sessQueryTimerMux(hSession);
int iReturnVal = TIMER_OK;
ST_TIMER_MUX *pstTM;
if (!hWnd || !pHTM)
{
assert(FALSE);
return TIMER_ERROR;
}
if ((pstTM = malloc(sizeof(ST_TIMER_MUX))) == NULL)
{
iReturnVal = TIMER_NOMEM;
}
else
{
pstTM->hSession = hSession;
pstTM->hWnd = hWnd;
pstTM->uiID = uiID;
pstTM->uiTimer = 0;
pstTM->uiLastDuration = 0;
pstTM->pstFirst = (ST_TIMER *)0;
pstTM->pstCurrent = (ST_TIMER *)0;
pstTM->fInMuxProc = FALSE;
iReturnVal = TimerSet(pstTM);
DbgOutStr("TimerMux handle %#lx created.\r\n", pstTM, 0, 0, 0, 0);
}
if (iReturnVal != TIMER_OK)
{
(void)TimerMuxDestroy((HTIMERMUX *)&pstTM, hSession);
}
*pHTM = (HTIMERMUX)pstTM;
//
// Don't forget to call sessReleaseTimerMux() to unlock
// the session's TimerMux critical section locked in
// sessQueryTimerMux(). REV: 5/21/2002
//
sessReleaseTimerMux(hSession);
return iReturnVal;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* FUNCTION: TimerMuxDestroy
*
* DESCRIPTION:
* Destroys a timer multiplexer and any timers still active
*
* ARGUMENTS:
* phTM -- Pointer to a timer mux handle of type HTIMERMUX created by
* an earlier call to TimerMuxCreate. The value pointed to will
* be set to NULL after the TimerMux is destroyed
*
* RETURNS:
* TIMER_OK
*/
int TimerMuxDestroy(HTIMERMUX * const phTM, const HSESSION hSession)
{
//
// sessQueryTimerMux() locks the session's TimerMux
// critical section. Call sessReleaseTimerMux() to unlock
// the session's TimerMux critical section. REV: 5/21/2002
//
HTIMERMUX hTM = sessQueryTimerMux(hSession);
ST_TIMER *pstTimer;
ST_TIMER_MUX *pstTimerMux;
assert(phTM);
pstTimerMux = (ST_TIMER_MUX *)*phTM;
if (pstTimerMux)
{
while (pstTimerMux->pstFirst)
{
pstTimer = pstTimerMux->pstFirst;
(void)TimerDestroy((HTIMER *)&pstTimer);
}
if (pstTimerMux->uiTimer)
{
DbgOutStr("KillTimer (timers.c)\r\n",0,0,0,0,0);
(void)KillTimer(pstTimerMux->hWnd, pstTimerMux->uiID);
}
free(pstTimerMux);
pstTimerMux = NULL;
DbgOutStr("TimerMux handle 0x%lx destroyed.\r\n", pstTimerMux, 0, 0, 0, 0);
}
*phTM = (HTIMERMUX)0;
//
// Don't forget to call sessReleaseTimerMux() to unlock
// the session's TimerMux critical section locked in
// sessQueryTimerMux(). REV: 5/21/2002
//
sessReleaseTimerMux(hSession);
return TIMER_OK;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* FUNCTION: TimerCreate
*
* DESCRIPTION:
* Creates a timer that will call a registered call back function at
* regular intervals specified in milliseconds. Once created, a timer
* can be destroyed by calling TimerDestroy(). TimerDestroy can be called
* from within the timer callback procedure.
*
* ARGUMENTS:
* hTM -- A timer multiplexer handle returned from a call to
* TimerMuxCreate
* phTimer -- Pointer to a variable of type HTIMER to receive the
* handle of the new timer.
* lInterval -- The timer interval in milliseconds. The callback function
* will be called repeatedly at roughly this interval until
* the timer is destroyed. The timer will operate at a minimum
* resolution depending on system capabilities. In Windows 3.x,
* the minimum resolution is 55 msecs. Due to the operation
* of the underlying Windows timer function, any interval may
* be extended an arbitrary amount of time.
* pfCallback -- Pointer to a function to be called after each interval.
* This function should be of type TIMER_CALLBACK. The
* value passed should be the result of calling
* MakeProcInstance on the actual callback function.
*
* This function should accept two arguments: a void ptr
* value which will contain any value supplied in the
* pvData argument described below; and an unsigned long
* which will be set to the actual duration of the most
* recent interval in milliseconds.
* pvData -- Can contain any arbitrary data. This value will be
* associated with the timer created and will be passed as
* an argument when the callback funtion is called.
*
* RETURNS:
* TIMER_OK if timer could be created.
* TIMER_NOMEM if there was insufficient memory
* TIMER_NOWINTIMER if no Windows timers are available
* TIMER_ERROR if parameters were invalid (also generates an assert)
*/
int TimerCreate(const HSESSION hSession,
HTIMER * const phTimer,
long lInterval,
const TIMERCALLBACK pfCallback,
void *pvData)
{
//
// sessQueryTimerMux() locks the session's TimerMux
// critical section. Call sessReleaseTimerMux() to unlock
// the session's TimerMux critical section. REV: 5/21/2002
//
HTIMERMUX hTM = sessQueryTimerMux(hSession);
ST_TIMER_MUX * const pstTimerMux = (ST_TIMER_MUX *)hTM;
ST_TIMER *pstTimer = (ST_TIMER *)0;
int iReturnVal = TIMER_OK;
assert(pstTimerMux && pfCallback);
if (pstTimerMux)
{
// Guard against a zero interval
if (lInterval == 0L)
{
++lInterval;
}
if (!pstTimerMux || !pfCallback)
{
iReturnVal = TIMER_ERROR;
}
else if ((pstTimer = malloc(sizeof(*pstTimer))) == NULL)
{
iReturnVal = TIMER_NOMEM;
}
else
{
pstTimer->hSession = hSession;
pstTimer->pstNext = (ST_TIMER *)0;
pstTimer->pstTimerMux = pstTimerMux;
pstTimer->lInterval = lInterval;
pstTimer->lLastFired = (long)GetTickCount();
pstTimer->lFireTime = pstTimer->lLastFired + lInterval;
pstTimer->pvData = pvData;
pstTimer->pfCallback = pfCallback;
TimerInsert(pstTimerMux, pstTimer);
// Following code caused problems when called from a thread other
// than the main thread
// if ((iReturnVal = TimerSet(pstTimerMux)) != TIMER_OK)
// (void)TimerDestroy((HTIMER *)&pstTimer);
PostMessage(pstTimerMux->hWnd, WM_FAKE_TIMER, 0, 0);
DbgOutStr("Timer handle %#lx (%#lx) created.\r\n", pstTimer, pstTimerMux, 0, 0, 0);
}
if (phTimer)
{
*phTimer = (HTIMER)pstTimer;
}
}
//
// Don't forget to call sessReleaseTimerMux() to unlock
// the session's TimerMux critical section locked in
// sessQueryTimerMux(). REV: 5/21/2002
//
sessReleaseTimerMux(hSession);
return iReturnVal;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* FUNCTION: TimerDestroy
*
* DESCRIPTION:
* Destroys a timer created with TimerCreate. This routine can be called
* to destroy a timer from within its own callback functionl
*
* ARGUMENTS:
* hTimer -- A timer handle returned from a call to TimerCreate.
*
* RETURNS:
* TIMER_OK if the timer is found and destroyed.
* TIMER_ERROR if the handle could not be found.
*/
int TimerDestroy(HTIMER * const phTimer)
{
int iReturnVal = TIMER_OK;
ST_TIMER stDummy;
ST_TIMER *pstTimer = (ST_TIMER *)*phTimer;
ST_TIMER *pstScan;
ST_TIMER *pstFound;
ST_TIMER_MUX *pstTimerMux;
assert(phTimer);
if (pstTimer)
{
//
// sessQueryTimerMux() locks the session's TimerMux
// critical section. Call sessReleaseTimerMux() to unlock
// the session's TimerMux critical section. REV: 5/21/2002
//
HTIMERMUX hTM = sessQueryTimerMux(pstTimer->hSession);
//
// Get the session handle for call to sessReleaseTimerMux() later.
//
const HSESSION hSession = pstTimer->hSession;
// Get pointer to parent struct
pstTimerMux = pstTimer->pstTimerMux;
if (pstTimerMux)
{
// If a timer is being destroyed from within its own callback, it
// has already been removed from the timer chain. Setting
// pstTimerMux->pstCurrent to NULL will prevent it from being
// rescheduled.
if (pstTimer == pstTimerMux->pstCurrent)
{
free(pstTimer);
pstTimer = NULL;
DbgOutStr("Timer destroyed 0x%lx\r\n", (LONG)pstTimer, 0, 0, 0, 0);
*phTimer = (HTIMER)0;
pstTimerMux->pstCurrent = (ST_TIMER *)0;
}
else
{
// Set up dummy node at head of list to avoid a bunch of
// special cases
stDummy.pstNext = pstTimerMux->pstFirst;
pstScan = &stDummy;
// Scan through list for match, maintaining pointer to the
// node BEFORE
while ((pstFound = pstScan->pstNext) != (ST_TIMER *)0)
{
if (pstFound == pstTimer)
{
break;
}
pstScan = pstFound;
}
// pstFound will be NULL if timer was not in list, otherwise
// pstFound is the node to remove and pstScan is the node
// prior to it
if (!pstFound)
{
iReturnVal = TIMER_ERROR;
}
else
{
pstScan->pstNext = pstFound->pstNext;
DbgOutStr("Timer handle 0x%lx destroyed.\r\n", pstFound, 0, 0, 0, 0);
free(pstFound);
pstFound = NULL;
// If we just destroyed a timer from within its own callback,
// leave a sign so the timer proc will know
if (pstFound == pstTimerMux->pstCurrent)
pstTimerMux->pstCurrent = (ST_TIMER *)0;
// Remove dummy node from start of list
pstTimerMux->pstFirst = stDummy.pstNext;
*phTimer = (HTIMER)0;
}
}
}
else
{
iReturnVal = TIMER_ERROR;
}
//
// Don't forget to call sessReleaseTimerMux() to unlock
// the session's TimerMux critical section locked in
// sessQueryTimerMux(). REV: 5/21/2002
//
sessReleaseTimerMux(hSession);
}
return iReturnVal;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* FUNCTION: TimerMuxProc
*
* DESCRIPTION:
* This function should be called by the window proc of the window whose
* handle was passed to TimerMuxCreate when a WM_TIMER message is received.
* It uses one Windows timer to control any number of individual multiplexed
* timers.
*
* ARGUMENTS:
* hSession -- The session to retreive the TimerMux handle from.
*
* RETURNS:
* nothing
*/
void TimerMuxProc(const HSESSION hSession)
{
//
// sessQueryTimerMux() locks the session's TimerMux
// critical section. Call sessReleaseTimerMux() to unlock
// the session's TimerMux critical section. REV: 5/21/2002
//
HTIMERMUX hTM = sessQueryTimerMux(hSession);
ST_TIMER *pstScan;
ST_TIMER_MUX * const pstTimerMux = (ST_TIMER_MUX *)hTM;
long lNow;
TIMERCALLBACK *pfCallback;
// Callbacks to timer procs to the printing routines can take a
// long time because of paper-out, etc. Since the AbortProc in
// the printer routines yields via a message loop, it is possible
// (read probable) that we can recursively enter this routine.
// The fInMuxProc flag guards against such an event. - mrw
if (!pstTimerMux->fInMuxProc)
{
pstTimerMux->fInMuxProc = TRUE;
}
else
{
return;
}
lNow = (long)GetTickCount();
DbgOutStr("%ld ", lNow, 0, 0, 0, 0);
// In the following routine, note that the node associated with the
// current call back is NOT linked into the timer chain during the
// call back. This allows TimerDestroy to be called on a timer from
// within its own call back. It is also OK to call TimerCreate from
// within call backs.
// Since timer ticks can be delayed, more than one event may have expired
pstScan = pstTimerMux->pstFirst;
while (pstScan && lNow > pstScan->lFireTime)
{
// Keep track of which timer is being called
pstTimerMux->pstCurrent = pstScan;
// Remove current node from list (will be added back in later if
// not destroyed)
pstTimerMux->pstFirst = pstScan->pstNext;
pfCallback = &pstScan->pfCallback;
// Give up the critical section while doing the callback so
// a lengthy call back won't delay any other threads
sessReleaseTimerMux(hSession);
// Activate the call back function
(*pfCallback)(pstScan->pvData, lNow - pstScan->lLastFired);
hTM = sessQueryTimerMux(hSession);
assert(pstTimerMux == (ST_TIMER_MUX *)hTM);
lNow = (long)GetTickCount();
DbgOutStr("%ld ", lNow, 0, 0, 0, 0);
// If timer was destroyed during callback, pstTimerMux->pstCurrent will have
// been sent to NULL; otherwise reschedule this timer
if ((pstScan = pstTimerMux->pstCurrent) != (ST_TIMER *)0)
{
DbgOutStr("Reschedule ", 0, 0, 0, 0, 0);
// Reschedule timer
pstScan->lLastFired = lNow;
pstScan->lFireTime = lNow + pstScan->lInterval;
// link this timer back into the list
TimerInsert(pstTimerMux, pstScan);
pstTimerMux->pstCurrent = (ST_TIMER *)0;
}
// First node on list is always the next one due to fire
pstScan = pstTimerMux->pstFirst;
}
(void)TimerSet(pstTimerMux);
pstTimerMux->fInMuxProc = FALSE;
// LeaveCriticalSection(&pstTimerMux->critsec);
//
// Don't forget to call sessReleaseTimerMux() to unlock
// the session's TimerMux critical section locked in
// sessQueryTimerMux(). REV: 5/21/2002
//
sessReleaseTimerMux(hSession);
return;
}
// INTERNAL ROUTINES
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* FUNCTION: TimerInsert
*
* DESCRIPTION:
* Links a timer control node into the linked list of all multiplexed timers.
* The list is maintained in order by when the node is due to fire.
*
* ARGUMENTS:
* pstTimerMux -- Handle to the timer multiplexer.
* pstTimer -- Pointer to a node to be inserted.
*
* RETURNS:
* nothing
*/
void TimerInsert(ST_TIMER_MUX *pstTimerMux,
ST_TIMER *pstTimer)
{
ST_TIMER *pstScan;
pstScan = pstTimerMux->pstFirst;
// If there are no other nodes in the list or if the new timer is
// scheduled before the first one in the list, link new one in first
if (!pstScan || pstTimer->lFireTime < pstScan->lFireTime)
{
pstTimer->pstNext = pstScan;
pstTimerMux->pstFirst = pstTimer;
}
else
{
// Insert sorted by lFireTime
while (pstScan->pstNext &&
pstScan->pstNext->lFireTime < pstTimer->lFireTime)
{
pstScan = pstScan->pstNext;
}
// Link into chain
pstTimer->pstNext = pstScan->pstNext;
pstScan->pstNext = pstTimer;
}
return;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* FUNCTION: TimerSet
*
* DESCRIPTION:
* Sets up a Windows timer using SetTimer to fire when the next multiplexed
* timer needs attention. Since the Window timer operates with an interval
* specified in a USHORT, there are times when the timer may have to be
* set to go off before the next required interval. If there are no
* multiplexed timers to be serviced, the timer is set to its maximum time
* anyway. By keeping one timer whether we need it or not, we guarantee
* that one will be available when we DO need it.
*
* ARGUMENTS:
* none
*
* RETURNS:
* TIMER_OK if successful.
* TIMER_NOWINTIMER if no Windows timers were available
*/
int TimerSet(ST_TIMER_MUX *pstTimerMux)
{
UINT uiDuration = 100000;
int iReturnVal = TIMER_OK;
long lTickCount;
if (pstTimerMux->pstFirst)
{
lTickCount = (long)GetTickCount();
if (pstTimerMux->pstFirst->lFireTime <= lTickCount)
uiDuration = 1; // Timer has already expired
else
uiDuration = (UINT)(pstTimerMux->pstFirst->lFireTime - lTickCount);
}
if (pstTimerMux->uiTimer == 0 || uiDuration != pstTimerMux->uiLastDuration)
{
// if (pstTimerMux->uiTimer != 0)
// {
// DbgOutStr("KillTimer (timers.c)\r\n",0,0,0,0,0);
// fResult = KillTimer(pstTimerMux->hWnd, pstTimerMux->uiID);
// assert(fResult);
// }
pstTimerMux->uiTimer =
SetTimer(pstTimerMux->hWnd, pstTimerMux->uiID, uiDuration, NULL);
DbgOutStr("SetTimer (timers.c)\r\n", 0,0,0,0,0);
if (pstTimerMux->uiTimer == 0)
{
iReturnVal = TIMER_NOWINTIMER;
}
pstTimerMux->uiLastDuration = uiDuration;
}
return (iReturnVal);
}
// End of timers.c