Use separate service for each windows timer.
diff --git a/windows/timer.c b/windows/timer.c
index 5e68ba4..34fa262 100644
--- a/windows/timer.c
+++ b/windows/timer.c
@@ -17,13 +17,13 @@
typedef struct tagTIMER
{
HWND hwnd;
- HQUEUE16 hq;
- UINT16 msg; /* WM_TIMER or WM_SYSTIMER */
+ HQUEUE16 hq;
+ UINT16 msg; /* WM_TIMER or WM_SYSTIMER */
UINT id;
UINT timeout;
- struct tagTIMER *next;
- DWORD expires; /* Next expiration, or 0 if already expired */
- HWINDOWPROC proc;
+ HANDLE hService;
+ BOOL expired;
+ HWINDOWPROC proc;
} TIMER;
#define NB_TIMERS 34
@@ -31,14 +31,8 @@
static TIMER TimersArray[NB_TIMERS];
-static TIMER * pNextTimer = NULL; /* Next timer to expire */
-
static CRITICAL_SECTION csTimer;
- /* Duration from 'time' until expiration of the timer */
-#define EXPIRE_TIME(pTimer,time) \
- (((pTimer)->expires <= (time)) ? 0 : (pTimer)->expires - (time))
-
/***********************************************************************
* TIMER_Init
@@ -55,61 +49,24 @@
/***********************************************************************
- * TIMER_InsertTimer
- *
- * Insert the timer at its place in the chain.
- */
-static void TIMER_InsertTimer( TIMER * pTimer )
-{
- EnterCriticalSection( &csTimer );
-
- if (!pNextTimer || (pTimer->expires < pNextTimer->expires))
- {
- pTimer->next = pNextTimer;
- pNextTimer = pTimer;
- }
- else
- {
- TIMER * ptr = pNextTimer;
- while (ptr->next && (pTimer->expires >= ptr->next->expires))
- ptr = ptr->next;
- pTimer->next = ptr->next;
- ptr->next = pTimer;
- }
-
- LeaveCriticalSection( &csTimer );
-}
-
-
-/***********************************************************************
- * TIMER_RemoveTimer
- *
- * Remove the timer from the chain.
- */
-static void TIMER_RemoveTimer( TIMER * pTimer )
-{
- TIMER **ppTimer = &pNextTimer;
-
- EnterCriticalSection( &csTimer );
-
- while (*ppTimer && (*ppTimer != pTimer)) ppTimer = &(*ppTimer)->next;
- if (*ppTimer) *ppTimer = pTimer->next;
- pTimer->next = NULL;
-
- LeaveCriticalSection( &csTimer );
-
- if (!pTimer->expires) QUEUE_DecTimerCount( pTimer->hq );
-}
-
-
-/***********************************************************************
* TIMER_ClearTimer
*
* Clear and remove a timer.
*/
static void TIMER_ClearTimer( TIMER * pTimer )
{
- TIMER_RemoveTimer( pTimer );
+ if ( pTimer->hService != INVALID_HANDLE_VALUE )
+ {
+ SERVICE_Delete( pTimer->hService );
+ pTimer->hService = INVALID_HANDLE_VALUE;
+ }
+
+ if ( pTimer->expired )
+ {
+ QUEUE_DecTimerCount( pTimer->hq );
+ pTimer->expired = FALSE;
+ }
+
pTimer->hwnd = 0;
pTimer->msg = 0;
pTimer->id = 0;
@@ -159,77 +116,38 @@
/***********************************************************************
- * TIMER_RestartTimers
- *
- * Restart an expired timer.
+ * TIMER_CheckTimer
*/
-static void TIMER_RestartTimer( TIMER * pTimer, DWORD curTime )
+static void CALLBACK TIMER_CheckTimer( ULONG_PTR timer_ptr )
{
- TIMER_RemoveTimer( pTimer );
- pTimer->expires = curTime + pTimer->timeout;
- TIMER_InsertTimer( pTimer );
-}
-
-/***********************************************************************
- * TIMER_CheckTimers
- *
- * Mark expired timers and wake the appropriate queues.
- */
-static void CALLBACK TIMER_CheckTimers( ULONG_PTR forceTimer )
-{
- static HANDLE ServiceHandle = INVALID_HANDLE_VALUE;
- static LONG ServiceTimeout = 0;
-
- TIMER *pTimer;
- DWORD curTime = GetTickCount();
+ TIMER *pTimer = (TIMER *)timer_ptr;
+ HQUEUE16 wakeQueue = 0;
EnterCriticalSection( &csTimer );
- TRACE(timer, "Called at %ld (%s)\n", curTime, forceTimer? "manual" : "auto" );
-
- pTimer = pNextTimer;
-
- while (pTimer && !pTimer->expires) /* Skip already expired timers */
- pTimer = pTimer->next;
- while (pTimer && (pTimer->expires <= curTime))
+ /* Paranoid check to prevent a race condition ... */
+ if ( !pTimer->timeout )
{
- pTimer->expires = 0;
- QUEUE_IncTimerCount( pTimer->hq );
- pTimer = pTimer->next;
+ LeaveCriticalSection( &csTimer );
+ return;
}
- /* Install service callback with appropriate timeout, so that
- we get called again once the next timer has expired */
-
- if (pTimer)
+ if ( !pTimer->expired )
{
- LONG timeout = pTimer->expires - curTime;
+ TRACE(timer, "Timer expired: %04x, %04x, %04x, %08lx\n",
+ pTimer->hwnd, pTimer->msg, pTimer->id, (DWORD)pTimer->proc);
- if ( forceTimer || timeout != ServiceTimeout )
- {
- if ( ServiceHandle != INVALID_HANDLE_VALUE )
- SERVICE_Delete( ServiceHandle );
-
- ServiceHandle = SERVICE_AddTimer( timeout * 1000L,
- TIMER_CheckTimers, FALSE );
- ServiceTimeout = timeout;
-
- TRACE(timer, "Installed service callback with timeout %ld\n", timeout );
- }
+ pTimer->expired = TRUE;
+ wakeQueue = pTimer->hq;
}
- else
- {
- if ( ServiceHandle != INVALID_HANDLE_VALUE )
- {
- SERVICE_Delete( ServiceHandle );
- ServiceHandle = INVALID_HANDLE_VALUE;
- ServiceTimeout = 0;
- TRACE(timer, "Deleted service callback\n" );
- }
- }
-
LeaveCriticalSection( &csTimer );
+
+ /* Note: This has to be done outside the csTimer critical section,
+ otherwise we'll get deadlocks. */
+
+ if ( wakeQueue )
+ QUEUE_IncTimerCount( wakeQueue );
}
@@ -242,38 +160,33 @@
HQUEUE16 hQueue, BOOL remove )
{
TIMER *pTimer;
- DWORD curTime = GetTickCount();
+ int i;
EnterCriticalSection( &csTimer );
- pTimer = pNextTimer;
-
- if (hwnd) /* Find first timer for this window */
- while (pTimer && (pTimer->hwnd != hwnd)) pTimer = pTimer->next;
- else /* Find first timer for this queue */
- while (pTimer && (pTimer->hq != hQueue)) pTimer = pTimer->next;
+ for (i = 0, pTimer = TimersArray; i < NB_TIMERS; i++, pTimer++)
+ if ( pTimer->timeout != 0 && pTimer->expired
+ && (hwnd? (pTimer->hwnd == hwnd) : (pTimer->hq == hQueue)) )
+ break;
- if (!pTimer || (pTimer->expires > curTime))
+ if ( i == NB_TIMERS )
{
LeaveCriticalSection( &csTimer );
return FALSE; /* No timer */
}
- TRACE(timer, "Timer expired: %04x, %04x, %04x, %08lx\n",
+ TRACE(timer, "Timer got message: %04x, %04x, %04x, %08lx\n",
pTimer->hwnd, pTimer->msg, pTimer->id, (DWORD)pTimer->proc);
if (remove)
- {
- TIMER_RestartTimer( pTimer, curTime ); /* Restart it */
- TIMER_CheckTimers( TRUE );
- }
+ pTimer->expired = FALSE;
/* Build the message */
msg->hwnd = pTimer->hwnd;
msg->message = pTimer->msg;
msg->wParam = pTimer->id;
msg->lParam = (LONG)pTimer->proc;
- msg->time = curTime;
+ msg->time = GetTickCount();
LeaveCriticalSection( &csTimer );
@@ -300,32 +213,25 @@
if ((pTimer->hwnd == hwnd) && (pTimer->id == id) &&
(pTimer->timeout != 0))
{
- /* Got one: set new values and return */
- TIMER_RemoveTimer( pTimer );
- pTimer->timeout = timeout;
- WINPROC_FreeProc( pTimer->proc, WIN_PROC_TIMER );
- pTimer->proc = (HWINDOWPROC)0;
- if (proc) WINPROC_SetProc( &pTimer->proc, proc,
- type, WIN_PROC_TIMER );
- pTimer->expires = GetTickCount() + timeout;
- TIMER_InsertTimer( pTimer );
- TIMER_CheckTimers( TRUE );
- LeaveCriticalSection( &csTimer );
- return id;
+ TIMER_ClearTimer( pTimer );
+ break;
}
- /* Find a free timer */
-
- for (i = 0, pTimer = TimersArray; i < NB_TIMERS; i++, pTimer++)
- if (!pTimer->timeout) break;
-
- if ( (i >= NB_TIMERS) ||
- (!sys && (i >= NB_TIMERS-NB_RESERVED_TIMERS)) )
+ if ( i == NB_TIMERS )
{
- LeaveCriticalSection( &csTimer );
- return 0;
- }
+ /* Find a free timer */
+ for (i = 0, pTimer = TimersArray; i < NB_TIMERS; i++, pTimer++)
+ if (!pTimer->timeout) break;
+
+ if ( (i >= NB_TIMERS) ||
+ (!sys && (i >= NB_TIMERS-NB_RESERVED_TIMERS)) )
+ {
+ LeaveCriticalSection( &csTimer );
+ return 0;
+ }
+ }
+
if (!hwnd) id = i + 1;
/* Add the timer */
@@ -336,15 +242,17 @@
pTimer->msg = sys ? WM_SYSTIMER : WM_TIMER;
pTimer->id = id;
pTimer->timeout = timeout;
- pTimer->expires = GetTickCount() + timeout;
pTimer->proc = (HWINDOWPROC)0;
if (proc) WINPROC_SetProc( &pTimer->proc, proc, type, WIN_PROC_TIMER );
+
+ pTimer->expired = FALSE;
+ pTimer->hService = SERVICE_AddTimer( timeout * 1000L,
+ TIMER_CheckTimer, (ULONG_PTR)pTimer );
+
TRACE(timer, "Timer added: %p, %04x, %04x, %04x, %08lx\n",
pTimer, pTimer->hwnd, pTimer->msg, pTimer->id,
(DWORD)pTimer->proc );
- TIMER_InsertTimer( pTimer );
- TIMER_CheckTimers( TRUE );
-
+
LeaveCriticalSection( &csTimer );
if (!id) return TRUE;