3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * PURPOSE: Window timers messages
6 * FILE: subsys/win32k/ntuser/timer.c
12 /* INCLUDES ******************************************************************/
14 #include <ddk/ntddk.h>
15 #include <win32k/win32k.h>
16 #include <win32k/ntuser.h>
17 #include <internal/ntoskrnl.h>
18 #include <internal/ps.h>
19 #include <include/msgqueue.h>
21 #include <napi/win32.h>
26 /* GLOBALS *******************************************************************/
29 static FAST_MUTEX Mutex;
30 static LIST_ENTRY TimerListHead;
32 static RTL_BITMAP HandleLessTimersBitMap;
33 static PVOID HandleLessTimersBitMapBuffer;
34 static ULONG HintIndex = 0;
35 static HANDLE MsgTimerThreadHandle;
36 static CLIENT_ID MsgTimerThreadId;
39 typedef struct _MSG_TIMER_ENTRY{
41 LARGE_INTEGER Timeout;
45 } MSG_TIMER_ENTRY, *PMSG_TIMER_ENTRY;
48 /* FUNCTIONS *****************************************************************/
51 //return true if the new timer became the first entry
54 InsertTimerAscendingOrder(PMSG_TIMER_ENTRY NewTimer)
56 PLIST_ENTRY EnumEntry, InsertAfter;
57 PMSG_TIMER_ENTRY MsgTimer;
61 EnumEntry = TimerListHead.Flink;
62 while (EnumEntry != &TimerListHead)
64 MsgTimer = CONTAINING_RECORD(EnumEntry, MSG_TIMER_ENTRY, ListEntry);
65 if (NewTimer->Timeout.QuadPart > MsgTimer->Timeout.QuadPart)
67 InsertAfter = EnumEntry;
69 EnumEntry = EnumEntry->Flink;
74 InsertTailList(InsertAfter, &NewTimer->ListEntry);
78 //insert as first entry
79 InsertHeadList(&TimerListHead, &NewTimer->ListEntry);
86 RemoveTimer(HWND hWnd, UINT_PTR IDEvent, HANDLE ThreadID)
88 PMSG_TIMER_ENTRY MsgTimer;
89 PLIST_ENTRY EnumEntry;
91 //remove timer if allready in the queue
92 EnumEntry = TimerListHead.Flink;
93 while (EnumEntry != &TimerListHead)
95 MsgTimer = CONTAINING_RECORD(EnumEntry, MSG_TIMER_ENTRY, ListEntry);
96 if (MsgTimer->Msg.hwnd == hWnd &&
97 MsgTimer->Msg.wParam == (WPARAM)IDEvent &&
98 MsgTimer->ThreadID == ThreadID)
100 RemoveEntryList(EnumEntry);
103 EnumEntry = EnumEntry->Flink;
122 PMSG_TIMER_ENTRY MsgTimer = NULL;
123 PMSG_TIMER_ENTRY NewTimer;
124 LARGE_INTEGER CurrentTime;
127 //FIXME: WINE: window must be owned by the calling thread
129 if (hWnd && !(hWnd = WIN_IsCurrentThread(hWnd))
131 return STATUS_UNSUCCESSFUL;
136 ThreadID = PsGetCurrentThreadId();
137 KeQuerySystemTime(&CurrentTime);
138 ExAcquireFastMutex(&Mutex);
142 //find a free, handle-less timer id
143 Index = RtlFindClearBitsAndSet(&HandleLessTimersBitMap, 1, HintIndex);
146 return STATUS_UNSUCCESSFUL;
149 *IDEvent = HintIndex = Index + 1;
153 //remove timer if allready in the queue
154 MsgTimer = RemoveTimer(hWnd, *IDEvent, ThreadID);
159 //modify existing (removed) timer
162 NewTimer->Period = Period;
163 NewTimer->Timeout.QuadPart = CurrentTime.QuadPart + (Period * 10000);
164 NewTimer->Msg.lParam = (LPARAM)TimerFunc;
168 //FIXME: use lookaside?
169 NewTimer = ExAllocatePool(PagedPool, sizeof(MSG_TIMER_ENTRY));
171 NewTimer->Msg.hwnd = hWnd;
172 NewTimer->Msg.message = WM_TIMER;
173 NewTimer->Msg.wParam = (WPARAM)*IDEvent;
174 NewTimer->Msg.lParam = (LPARAM)TimerFunc;
175 NewTimer->Period = Period;
176 NewTimer->Timeout.QuadPart = CurrentTime.QuadPart + (Period * 10000);
177 NewTimer->ThreadID = ThreadID;
180 if (InsertTimerAscendingOrder(NewTimer))
182 //new timer is first in queue and expires first
183 KeSetTimer(&Timer, NewTimer->Timeout, NULL);
186 ExReleaseFastMutex(&Mutex);
188 return STATUS_SUCCESS;
198 PMSG_TIMER_ENTRY MsgTimer;
200 ExAcquireFastMutex(&Mutex);
205 if (!RtlAreBitsSet(&HandleLessTimersBitMap, IDEvent - 1, 1))
208 ExReleaseFastMutex(&Mutex);
209 return STATUS_UNSUCCESSFUL;
212 RtlClearBits(&HandleLessTimersBitMap, IDEvent - 1, 1);
215 MsgTimer = RemoveTimer(hWnd, IDEvent, PsGetCurrentThreadId());
217 ExReleaseFastMutex(&Mutex);
219 if (MsgTimer == NULL)
222 return STATUS_UNSUCCESSFUL;
225 //FIXME: use lookaside?
226 ExFreePool(MsgTimer);
228 return STATUS_SUCCESS;
236 NtUserSetSystemTimer(
248 static NTSTATUS STDCALL
252 LARGE_INTEGER CurrentTime;
253 PLIST_ENTRY EnumEntry;
254 PMSG_TIMER_ENTRY MsgTimer;
260 Status = KeWaitForSingleObject( &Timer,
266 if (!NT_SUCCESS(Status))
268 DPRINT1("Error waiting in TimerThreadMain\n");
272 ExAcquireFastMutex(&Mutex);
274 KeQuerySystemTime(&CurrentTime);
276 EnumEntry = TimerListHead.Flink;
277 while (EnumEntry != &TimerListHead)
279 MsgTimer = CONTAINING_RECORD(EnumEntry, MSG_TIMER_ENTRY, ListEntry);
280 EnumEntry = EnumEntry->Flink;
282 if (CurrentTime.QuadPart >= MsgTimer->Timeout.QuadPart)
284 RemoveEntryList(&MsgTimer->ListEntry);
287 * FIXME: 1) Find a faster way of getting the thread message queue? (lookup by id is slow)
288 * 2) Kill all timers for thread when the thread exits?
291 if (!NT_SUCCESS(PsLookupThreadByThreadId(MsgTimer->ThreadID, &Thread)))
293 //FIXME: remove all other timers for this thread also?
294 ExFreePool(MsgTimer);
299 * FIXME: small window here, where the thread can exit between the thread lookup
300 * and the message posting (missing ref count?)
302 MsqPostMessage(((PW32THREAD)Thread->Win32Thread)->MessageQueue, MsqCreateMessage(&MsgTimer->Msg));
304 //set up next periodic timeout
305 MsgTimer->Timeout.QuadPart += (MsgTimer->Period * 10000);
306 InsertTimerAscendingOrder(MsgTimer);
315 //set up next timeout from first entry (if any)
316 if (!IsListEmpty(&TimerListHead))
318 MsgTimer = CONTAINING_RECORD( TimerListHead.Flink, MSG_TIMER_ENTRY, ListEntry);
319 KeSetTimer(&Timer, MsgTimer->Timeout, NULL);
322 ExReleaseFastMutex(&Mutex);
335 InitializeListHead(&TimerListHead);
336 KeInitializeTimer(&Timer);
337 ExInitializeFastMutex(&Mutex);
339 //windows 2000 has room for 32768 handle-less timers
340 HandleLessTimersBitMapBuffer = ExAllocatePool(PagedPool, PAGE_SIZE);
341 RtlInitializeBitMap(&HandleLessTimersBitMap,
342 HandleLessTimersBitMapBuffer,
343 PAGE_SIZE * sizeof(ULONG));
345 //yes we need this, since ExAllocatePool isn't supposed to zero out allocated memory
346 RtlClearAllBits(&HandleLessTimersBitMap);
348 Status = PsCreateSystemThread(&MsgTimerThreadHandle,