/*
+ * ReactOS W32 Subsystem
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+/* $Id$
*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
/* GLOBALS *******************************************************************/
+//windows 2000 has room for 32768 handle-less timers
+#define NUM_HANDLE_LESS_TIMERS 1024
static FAST_MUTEX Mutex;
static LIST_ENTRY TimerListHead;
//return true if the new timer became the first entry
+//must hold mutex while calling this
BOOL
FASTCALL
InsertTimerAscendingOrder(PMSG_TIMER_ENTRY NewTimer)
}
+//must hold mutex while calling this
PMSG_TIMER_ENTRY
FASTCALL
RemoveTimer(HWND hWnd, UINT_PTR IDEvent, HANDLE ThreadID)
while (EnumEntry != &TimerListHead)
{
MsgTimer = CONTAINING_RECORD(EnumEntry, MSG_TIMER_ENTRY, ListEntry);
+ EnumEntry = EnumEntry->Flink;
+
if (MsgTimer->Msg.hwnd == hWnd &&
MsgTimer->Msg.wParam == (WPARAM)IDEvent &&
MsgTimer->ThreadID == ThreadID)
{
- RemoveEntryList(EnumEntry);
+ RemoveEntryList(&MsgTimer->ListEntry);
return MsgTimer;
}
- EnumEntry = EnumEntry->Flink;
}
return NULL;
}
-
-
-NTSTATUS
-STDCALL
-NtUserSetTimer(
- HWND hWnd,
- UINT_PTR * IDEvent,
- UINT Period,
- TIMERPROC TimerFunc
- )
+/*
+ * NOTE: It doesn't kill timers. It just removes them from the list.
+ */
+VOID
+FASTCALL
+RemoveTimersThread(HANDLE ThreadID)
{
- ULONG Index;
- PMSG_TIMER_ENTRY MsgTimer = NULL;
- PMSG_TIMER_ENTRY NewTimer;
- LARGE_INTEGER CurrentTime;
- HANDLE ThreadID;
-
- //FIXME: WINE: window must be owned by the calling thread
-#if 0
- if (hWnd && !(hWnd = WIN_IsCurrentThread(hWnd))
- {
- return STATUS_UNSUCCESSFUL;
- }
-
-#endif
+ PMSG_TIMER_ENTRY MsgTimer;
+ PLIST_ENTRY EnumEntry;
- ThreadID = PsGetCurrentThreadId();
- KeQuerySystemTime(&CurrentTime);
ExAcquireFastMutex(&Mutex);
- if (hWnd == NULL)
- {
- //find a free, handle-less timer id
- Index = RtlFindClearBitsAndSet(&HandleLessTimersBitMap, 1, HintIndex);
- if (Index == -1)
- {
- return STATUS_UNSUCCESSFUL;
- }
-
- *IDEvent = HintIndex = Index + 1;
- }
- else
- {
- //remove timer if allready in the queue
- MsgTimer = RemoveTimer(hWnd, *IDEvent, ThreadID);
- }
-
- if (MsgTimer)
+ EnumEntry = TimerListHead.Flink;
+ while (EnumEntry != &TimerListHead)
{
- //modify existing (removed) timer
- NewTimer = MsgTimer;
+ MsgTimer = CONTAINING_RECORD(EnumEntry, MSG_TIMER_ENTRY, ListEntry);
+ EnumEntry = EnumEntry->Flink;
- NewTimer->Period = Period;
- NewTimer->Timeout.QuadPart = CurrentTime.QuadPart + (Period * 10000);
- NewTimer->Msg.lParam = (LPARAM)TimerFunc;
- }
- else
- {
- //FIXME: use lookaside?
- NewTimer = ExAllocatePool(PagedPool, sizeof(MSG_TIMER_ENTRY));
-
- NewTimer->Msg.hwnd = hWnd;
- NewTimer->Msg.message = WM_TIMER;
- NewTimer->Msg.wParam = (WPARAM)*IDEvent;
- NewTimer->Msg.lParam = (LPARAM)TimerFunc;
- NewTimer->Period = Period;
- NewTimer->Timeout.QuadPart = CurrentTime.QuadPart + (Period * 10000);
- NewTimer->ThreadID = ThreadID;
- }
+ if (MsgTimer->ThreadID == ThreadID)
+ {
+ if (MsgTimer->Msg.hwnd == NULL)
+ {
+ RtlClearBits(&HandleLessTimersBitMap, ((UINT_PTR)MsgTimer->Msg.wParam) - 1, 1);
+ }
- if (InsertTimerAscendingOrder(NewTimer))
- {
- //new timer is first in queue and expires first
- KeSetTimer(&Timer, NewTimer->Timeout, NULL);
+ RemoveEntryList(&MsgTimer->ListEntry);
+ ExFreePool(MsgTimer);
+ }
}
ExReleaseFastMutex(&Mutex);
- return STATUS_SUCCESS;
}
-NTSTATUS
+
+UINT_PTR
STDCALL
-NtUserKillTimer(
- HWND hWnd,
- UINT_PTR IDEvent)
+NtUserSetTimer
+(
+ HWND hWnd,
+ UINT_PTR nIDEvent,
+ UINT uElapse,
+ TIMERPROC lpTimerFunc
+)
{
- PMSG_TIMER_ENTRY MsgTimer;
-
- ExAcquireFastMutex(&Mutex);
+ ULONG Index;
+ PMSG_TIMER_ENTRY MsgTimer = NULL;
+ PMSG_TIMER_ENTRY NewTimer;
+ LARGE_INTEGER CurrentTime;
+ HANDLE ThreadID;
- //handle-less timer?
- if (hWnd == NULL)
- {
- if (!RtlAreBitsSet(&HandleLessTimersBitMap, IDEvent - 1, 1))
- {
- //bit was not set
- ExReleaseFastMutex(&Mutex);
- return STATUS_UNSUCCESSFUL;
- }
+ //FIXME: WINE: window must be owned by the calling thread
+#if 0
+ if(hWnd && !(hWnd = WIN_IsCurrentThread(hWnd))
+ {
+ return STATUS_UNSUCCESSFUL;
+ }
+#endif
- RtlClearBits(&HandleLessTimersBitMap, IDEvent - 1, 1);
- }
+ ThreadID = PsGetCurrentThreadId();
+ KeQuerySystemTime(&CurrentTime);
+ ExAcquireFastMutex(&Mutex);
- MsgTimer = RemoveTimer(hWnd, IDEvent, PsGetCurrentThreadId());
+ if(hWnd == NULL)
+ {
+ /* find a free, handle-less timer id */
+ Index = RtlFindClearBitsAndSet(&HandleLessTimersBitMap, 1, HintIndex);
+ if(Index == (ULONG) -1)
+ {
+ /* FIXME: set the last error */
ExReleaseFastMutex(&Mutex);
-
- if (MsgTimer == NULL)
- {
- //didn't find timer
- return STATUS_UNSUCCESSFUL;
- }
-
- //FIXME: use lookaside?
- ExFreePool(MsgTimer);
-
- return STATUS_SUCCESS;
+ return 0;
+ }
+
+ ++ Index;
+ HintIndex = Index;
+ ExReleaseFastMutex(&Mutex);
+ return Index;
+ }
+ else
+ {
+ /* remove timer if already in the queue */
+ MsgTimer = RemoveTimer(hWnd, nIDEvent, ThreadID);
+ }
+
+ if(MsgTimer)
+ {
+ /* modify existing (removed) timer */
+ NewTimer = MsgTimer;
+
+ NewTimer->Period = uElapse;
+ NewTimer->Timeout.QuadPart = CurrentTime.QuadPart + (uElapse * 10000);
+ NewTimer->Msg.lParam = (LPARAM)lpTimerFunc;
+ }
+ else
+ {
+ /* FIXME: use lookaside? */
+ NewTimer = ExAllocatePool(PagedPool, sizeof(MSG_TIMER_ENTRY));
+
+ NewTimer->Msg.hwnd = hWnd;
+ NewTimer->Msg.message = WM_TIMER;
+ NewTimer->Msg.wParam = (WPARAM)nIDEvent;
+ NewTimer->Msg.lParam = (LPARAM)lpTimerFunc;
+ NewTimer->Period = uElapse;
+ NewTimer->Timeout.QuadPart = CurrentTime.QuadPart + (uElapse * 10000);
+ NewTimer->ThreadID = ThreadID;
+ }
+
+ if(InsertTimerAscendingOrder(NewTimer))
+ {
+ /* new timer is first in queue and expires first */
+ KeSetTimer(&Timer, NewTimer->Timeout, NULL);
+ }
+
+ ExReleaseFastMutex(&Mutex);
+
+ return 1;
}
-
+BOOL
+STDCALL
+NtUserKillTimer
+(
+ HWND hWnd,
+ UINT_PTR uIDEvent
+)
+{
+ PMSG_TIMER_ENTRY MsgTimer;
+
+ ExAcquireFastMutex(&Mutex);
+
+ /* handle-less timer? */
+ if(hWnd == NULL)
+ {
+ if(!RtlAreBitsSet(&HandleLessTimersBitMap, uIDEvent - 1, 1))
+ {
+ /* bit was not set */
+ /* FIXME: set the last error */
+ ExReleaseFastMutex(&Mutex);
+ return FALSE;
+ }
+
+ RtlClearBits(&HandleLessTimersBitMap, uIDEvent - 1, 1);
+ }
+
+ MsgTimer = RemoveTimer(hWnd, uIDEvent, PsGetCurrentThreadId());
+
+ ExReleaseFastMutex(&Mutex);
+
+ if(MsgTimer == NULL)
+ {
+ /* didn't find timer */
+ /* FIXME: set the last error */
+ return FALSE;
+ }
+
+ /* FIXME: use lookaside? */
+ ExFreePool(MsgTimer);
+
+ return TRUE;
+}
DWORD
STDCALL
}
-static NTSTATUS STDCALL
-TimerThreadMain()
+static VOID STDCALL_FUNC
+TimerThreadMain(
+ PVOID StartContext
+ )
{
NTSTATUS Status;
LARGE_INTEGER CurrentTime;
if (!NT_SUCCESS(Status))
{
DPRINT1("Error waiting in TimerThreadMain\n");
- KeBugCheck(0);
+ KEBUGCHECK(0);
}
ExAcquireFastMutex(&Mutex);
/*
* FIXME: 1) Find a faster way of getting the thread message queue? (lookup by id is slow)
- * 2) Kill all timers for thread when the thread exits?
*/
if (!NT_SUCCESS(PsLookupThreadByThreadId(MsgTimer->ThreadID, &Thread)))
{
- //FIXME: remove all other timers for this thread also?
ExFreePool(MsgTimer);
continue;
}
- /*
- * FIXME: small window here, where the thread can exit between the thread lookup
- * and the message posting (missing ref count?)
- */
MsqPostMessage(((PW32THREAD)Thread->Win32Thread)->MessageQueue, MsqCreateMessage(&MsgTimer->Msg));
+ ObDereferenceObject(Thread);
+
//set up next periodic timeout
MsgTimer->Timeout.QuadPart += (MsgTimer->Period * 10000);
InsertTimerAscendingOrder(MsgTimer);
MsgTimer = CONTAINING_RECORD( TimerListHead.Flink, MSG_TIMER_ENTRY, ListEntry);
KeSetTimer(&Timer, MsgTimer->Timeout, NULL);
}
+ else
+ {
+ /* Reinitialize the timer, this reset the state of the timer event on which we wait */
+ KeInitializeTimer(&Timer);
+ }
ExReleaseFastMutex(&Mutex);
-NTSTATUS
-InitTimerImpl()
+NTSTATUS FASTCALL
+InitTimerImpl(VOID)
{
NTSTATUS Status;
+ ULONG BitmapBytes;
+
+ BitmapBytes = ROUND_UP(NUM_HANDLE_LESS_TIMERS, sizeof(ULONG) * 8) / 8;
InitializeListHead(&TimerListHead);
KeInitializeTimer(&Timer);
ExInitializeFastMutex(&Mutex);
- //windows 2000 has room for 32768 handle-less timers
- HandleLessTimersBitMapBuffer = ExAllocatePool(PagedPool, PAGE_SIZE);
+ HandleLessTimersBitMapBuffer = ExAllocatePool(PagedPool, BitmapBytes);
RtlInitializeBitMap(&HandleLessTimersBitMap,
HandleLessTimersBitMapBuffer,
- PAGE_SIZE * sizeof(ULONG));
+ BitmapBytes * 8);
//yes we need this, since ExAllocatePool isn't supposed to zero out allocated memory
RtlClearAllBits(&HandleLessTimersBitMap);
return Status;
}
-
-
-
-
-
+/* EOF */