Added timeout to critical section waiting.
[wine/multimedia.git] / windows / timer.c
blob2124c475c18408087b40edb04c179696fbe83353
1 /*
2 * Timer functions
4 * Copyright 1993 Alexandre Julliard
5 */
7 #include "winuser.h"
8 #include "queue.h"
9 #include "task.h"
10 #include "winproc.h"
11 #include "debug.h"
14 typedef struct tagTIMER
16 HWND hwnd;
17 HQUEUE16 hq;
18 UINT16 msg; /* WM_TIMER or WM_SYSTIMER */
19 UINT id;
20 UINT timeout;
21 struct tagTIMER *next;
22 DWORD expires; /* Next expiration, or 0 if already expired */
23 HWINDOWPROC proc;
24 } TIMER;
26 #define NB_TIMERS 34
27 #define NB_RESERVED_TIMERS 2 /* for SetSystemTimer */
29 static TIMER TimersArray[NB_TIMERS];
31 static TIMER * pNextTimer = NULL; /* Next timer to expire */
33 static CRITICAL_SECTION csTimer;
35 /* Duration from 'time' until expiration of the timer */
36 #define EXPIRE_TIME(pTimer,time) \
37 (((pTimer)->expires <= (time)) ? 0 : (pTimer)->expires - (time))
40 /***********************************************************************
41 * TIMER_Init
43 * Initialize critical section for the timer.
45 BOOL TIMER_Init( void )
47 InitializeCriticalSection( &csTimer );
48 MakeCriticalSectionGlobal( &csTimer );
50 return TRUE;
54 /***********************************************************************
55 * TIMER_InsertTimer
57 * Insert the timer at its place in the chain.
59 static void TIMER_InsertTimer( TIMER * pTimer )
61 EnterCriticalSection( &csTimer );
63 if (!pNextTimer || (pTimer->expires < pNextTimer->expires))
65 pTimer->next = pNextTimer;
66 pNextTimer = pTimer;
68 else
70 TIMER * ptr = pNextTimer;
71 while (ptr->next && (pTimer->expires >= ptr->next->expires))
72 ptr = ptr->next;
73 pTimer->next = ptr->next;
74 ptr->next = pTimer;
77 LeaveCriticalSection( &csTimer );
81 /***********************************************************************
82 * TIMER_RemoveTimer
84 * Remove the timer from the chain.
86 static void TIMER_RemoveTimer( TIMER * pTimer )
88 TIMER **ppTimer = &pNextTimer;
90 EnterCriticalSection( &csTimer );
92 while (*ppTimer && (*ppTimer != pTimer)) ppTimer = &(*ppTimer)->next;
93 if (*ppTimer) *ppTimer = pTimer->next;
94 pTimer->next = NULL;
96 LeaveCriticalSection( &csTimer );
98 if (!pTimer->expires) QUEUE_DecTimerCount( pTimer->hq );
102 /***********************************************************************
103 * TIMER_ClearTimer
105 * Clear and remove a timer.
107 static void TIMER_ClearTimer( TIMER * pTimer )
109 TIMER_RemoveTimer( pTimer );
110 pTimer->hwnd = 0;
111 pTimer->msg = 0;
112 pTimer->id = 0;
113 pTimer->timeout = 0;
114 WINPROC_FreeProc( pTimer->proc, WIN_PROC_TIMER );
118 /***********************************************************************
119 * TIMER_SwitchQueue
121 void TIMER_SwitchQueue( HQUEUE16 old, HQUEUE16 new )
123 TIMER * pT;
125 EnterCriticalSection( &csTimer );
127 pT = pNextTimer;
128 while (pT)
130 if (pT->hq == old) pT->hq = new;
131 pT = pT->next;
134 LeaveCriticalSection( &csTimer );
138 /***********************************************************************
139 * TIMER_RemoveWindowTimers
141 * Remove all timers for a given window.
143 void TIMER_RemoveWindowTimers( HWND hwnd )
145 int i;
146 TIMER *pTimer;
148 EnterCriticalSection( &csTimer );
150 for (i = NB_TIMERS, pTimer = TimersArray; i > 0; i--, pTimer++)
151 if ((pTimer->hwnd == hwnd) && pTimer->timeout)
152 TIMER_ClearTimer( pTimer );
154 LeaveCriticalSection( &csTimer );
158 /***********************************************************************
159 * TIMER_RemoveQueueTimers
161 * Remove all timers for a given queue.
163 void TIMER_RemoveQueueTimers( HQUEUE16 hqueue )
165 int i;
166 TIMER *pTimer;
168 EnterCriticalSection( &csTimer );
170 for (i = NB_TIMERS, pTimer = TimersArray; i > 0; i--, pTimer++)
171 if ((pTimer->hq == hqueue) && pTimer->timeout)
172 TIMER_ClearTimer( pTimer );
174 LeaveCriticalSection( &csTimer );
178 /***********************************************************************
179 * TIMER_RestartTimers
181 * Restart an expired timer.
183 static void TIMER_RestartTimer( TIMER * pTimer, DWORD curTime )
185 TIMER_RemoveTimer( pTimer );
186 pTimer->expires = curTime + pTimer->timeout;
187 TIMER_InsertTimer( pTimer );
191 /***********************************************************************
192 * TIMER_GetNextExpiration
194 * Return next timer expiration time, or -1 if none.
196 LONG TIMER_GetNextExpiration(void)
198 TIMER *pTimer;
199 LONG retValue;
201 EnterCriticalSection( &csTimer );
203 pTimer = pNextTimer;
205 while (pTimer && !pTimer->expires) /* Skip already expired timers */
206 pTimer = pTimer->next;
208 retValue = pTimer ? EXPIRE_TIME( pTimer, GetTickCount() ) : -1;
209 LeaveCriticalSection( &csTimer );
211 return retValue;
215 /***********************************************************************
216 * TIMER_ExpireTimers
218 * Mark expired timers and wake the appropriate queues.
220 void TIMER_ExpireTimers(void)
222 TIMER *pTimer;
223 DWORD curTime = GetTickCount();
225 EnterCriticalSection( &csTimer );
227 pTimer = pNextTimer;
229 while (pTimer && !pTimer->expires) /* Skip already expired timers */
230 pTimer = pTimer->next;
231 while (pTimer && (pTimer->expires <= curTime))
233 pTimer->expires = 0;
234 QUEUE_IncTimerCount( pTimer->hq );
235 pTimer = pTimer->next;
238 LeaveCriticalSection( &csTimer );
242 /***********************************************************************
243 * TIMER_GetTimerMsg
245 * Build a message for an expired timer.
247 BOOL TIMER_GetTimerMsg( MSG *msg, HWND hwnd,
248 HQUEUE16 hQueue, BOOL remove )
250 TIMER *pTimer;
251 DWORD curTime = GetTickCount();
253 EnterCriticalSection( &csTimer );
255 pTimer = pNextTimer;
257 if (hwnd) /* Find first timer for this window */
258 while (pTimer && (pTimer->hwnd != hwnd)) pTimer = pTimer->next;
259 else /* Find first timer for this queue */
260 while (pTimer && (pTimer->hq != hQueue)) pTimer = pTimer->next;
262 if (!pTimer || (pTimer->expires > curTime))
264 LeaveCriticalSection( &csTimer );
265 return FALSE; /* No timer */
268 if (remove) TIMER_RestartTimer( pTimer, curTime ); /* Restart it */
270 TRACE(timer, "Timer expired: %04x, %04x, %04x, %08lx\n",
271 pTimer->hwnd, pTimer->msg, pTimer->id, (DWORD)pTimer->proc);
273 /* Build the message */
274 msg->hwnd = pTimer->hwnd;
275 msg->message = pTimer->msg;
276 msg->wParam = pTimer->id;
277 msg->lParam = (LONG)pTimer->proc;
278 msg->time = curTime;
280 LeaveCriticalSection( &csTimer );
282 return TRUE;
286 /***********************************************************************
287 * TIMER_SetTimer
289 static UINT TIMER_SetTimer( HWND hwnd, UINT id, UINT timeout,
290 WNDPROC16 proc, WINDOWPROCTYPE type, BOOL sys )
292 int i;
293 TIMER * pTimer;
295 if (!timeout) return 0;
297 EnterCriticalSection( &csTimer );
299 /* Check if there's already a timer with the same hwnd and id */
301 for (i = 0, pTimer = TimersArray; i < NB_TIMERS; i++, pTimer++)
302 if ((pTimer->hwnd == hwnd) && (pTimer->id == id) &&
303 (pTimer->timeout != 0))
305 /* Got one: set new values and return */
306 TIMER_RemoveTimer( pTimer );
307 pTimer->timeout = timeout;
308 WINPROC_FreeProc( pTimer->proc, WIN_PROC_TIMER );
309 pTimer->proc = (HWINDOWPROC)0;
310 if (proc) WINPROC_SetProc( &pTimer->proc, proc,
311 type, WIN_PROC_TIMER );
312 pTimer->expires = GetTickCount() + timeout;
313 TIMER_InsertTimer( pTimer );
314 LeaveCriticalSection( &csTimer );
315 return id;
318 /* Find a free timer */
320 for (i = 0, pTimer = TimersArray; i < NB_TIMERS; i++, pTimer++)
321 if (!pTimer->timeout) break;
323 if ( (i >= NB_TIMERS) ||
324 (!sys && (i >= NB_TIMERS-NB_RESERVED_TIMERS)) )
326 LeaveCriticalSection( &csTimer );
327 return 0;
330 if (!hwnd) id = i + 1;
332 /* Add the timer */
334 pTimer->hwnd = hwnd;
335 pTimer->hq = (hwnd) ? GetThreadQueue16( GetWindowThreadProcessId( hwnd, NULL ) )
336 : GetFastQueue16( );
337 pTimer->msg = sys ? WM_SYSTIMER : WM_TIMER;
338 pTimer->id = id;
339 pTimer->timeout = timeout;
340 pTimer->expires = GetTickCount() + timeout;
341 pTimer->proc = (HWINDOWPROC)0;
342 if (proc) WINPROC_SetProc( &pTimer->proc, proc, type, WIN_PROC_TIMER );
343 TRACE(timer, "Timer added: %p, %04x, %04x, %04x, %08lx\n",
344 pTimer, pTimer->hwnd, pTimer->msg, pTimer->id,
345 (DWORD)pTimer->proc );
346 TIMER_InsertTimer( pTimer );
348 LeaveCriticalSection( &csTimer );
350 if (!id) return TRUE;
351 else return id;
355 /***********************************************************************
356 * TIMER_KillTimer
358 static BOOL TIMER_KillTimer( HWND hwnd, UINT id, BOOL sys )
360 int i;
361 TIMER * pTimer;
363 EnterCriticalSection( &csTimer );
365 /* Find the timer */
367 for (i = 0, pTimer = TimersArray; i < NB_TIMERS; i++, pTimer++)
368 if ((pTimer->hwnd == hwnd) && (pTimer->id == id) &&
369 (pTimer->timeout != 0)) break;
371 if ( (i >= NB_TIMERS) ||
372 (!sys && (i >= NB_TIMERS-NB_RESERVED_TIMERS)) ||
373 (!sys && (pTimer->msg != WM_TIMER)) ||
374 (sys && (pTimer->msg != WM_SYSTIMER)) )
376 LeaveCriticalSection( &csTimer );
377 return FALSE;
380 /* Delete the timer */
382 TIMER_ClearTimer( pTimer );
384 LeaveCriticalSection( &csTimer );
386 return TRUE;
390 /***********************************************************************
391 * SetTimer16 (USER.10)
393 UINT16 WINAPI SetTimer16( HWND16 hwnd, UINT16 id, UINT16 timeout,
394 TIMERPROC16 proc )
396 TRACE(timer, "%04x %d %d %08lx\n",
397 hwnd, id, timeout, (LONG)proc );
398 return TIMER_SetTimer( hwnd, id, timeout, (WNDPROC16)proc,
399 WIN_PROC_16, FALSE );
403 /***********************************************************************
404 * SetTimer32 (USER32.511)
406 UINT WINAPI SetTimer( HWND hwnd, UINT id, UINT timeout,
407 TIMERPROC proc )
409 TRACE(timer, "%04x %d %d %08lx\n",
410 hwnd, id, timeout, (LONG)proc );
411 return TIMER_SetTimer( hwnd, id, timeout, (WNDPROC16)proc,
412 WIN_PROC_32A, FALSE );
416 /***********************************************************************
417 * SetSystemTimer16 (USER.11)
419 UINT16 WINAPI SetSystemTimer16( HWND16 hwnd, UINT16 id, UINT16 timeout,
420 TIMERPROC16 proc )
422 TRACE(timer, "%04x %d %d %08lx\n",
423 hwnd, id, timeout, (LONG)proc );
424 return TIMER_SetTimer( hwnd, id, timeout, (WNDPROC16)proc,
425 WIN_PROC_16, TRUE );
429 /***********************************************************************
430 * SetSystemTimer32 (USER32.509)
432 UINT WINAPI SetSystemTimer( HWND hwnd, UINT id, UINT timeout,
433 TIMERPROC proc )
435 TRACE(timer, "%04x %d %d %08lx\n",
436 hwnd, id, timeout, (LONG)proc );
437 return TIMER_SetTimer( hwnd, id, timeout, (WNDPROC16)proc,
438 WIN_PROC_32A, TRUE );
442 /***********************************************************************
443 * KillTimer16 (USER.12)
445 BOOL16 WINAPI KillTimer16( HWND16 hwnd, UINT16 id )
447 TRACE(timer, "%04x %d\n", hwnd, id );
448 return TIMER_KillTimer( hwnd, id, FALSE );
452 /***********************************************************************
453 * KillTimer32 (USER32.354)
455 BOOL WINAPI KillTimer( HWND hwnd, UINT id )
457 TRACE(timer, "%04x %d\n", hwnd, id );
458 return TIMER_KillTimer( hwnd, id, FALSE );
462 /***********************************************************************
463 * KillSystemTimer16 (USER.182)
465 BOOL16 WINAPI KillSystemTimer16( HWND16 hwnd, UINT16 id )
467 TRACE(timer, "%04x %d\n", hwnd, id );
468 return TIMER_KillTimer( hwnd, id, TRUE );
472 /***********************************************************************
473 * KillSystemTimer32 (USER32.353)
475 BOOL WINAPI KillSystemTimer( HWND hwnd, UINT id )
477 TRACE(timer, "%04x %d\n", hwnd, id );
478 return TIMER_KillTimer( hwnd, id, TRUE );