Generate timer messages instead of queueing them

svn path=/trunk/; revision=12387
This commit is contained in:
Gé van Geldorp 2004-12-29 19:55:01 +00:00
parent 3149ec5ad0
commit cffcbe1d74
8 changed files with 468 additions and 397 deletions

View file

@ -40,6 +40,16 @@ typedef struct _USER_SENT_MESSAGE_NOTIFY
LIST_ENTRY ListEntry;
} USER_SENT_MESSAGE_NOTIFY, *PUSER_SENT_MESSAGE_NOTIFY;
typedef struct _TIMER_ENTRY{
LIST_ENTRY ListEntry;
LARGE_INTEGER ExpiryTime;
HWND Wnd;
UINT_PTR IDEvent;
UINT Period;
TIMERPROC TimerFunc;
UINT Msg;
} TIMER_ENTRY, *PTIMER_ENTRY;
typedef struct _USER_MESSAGE_QUEUE
{
/* Reference counter, only access this variable with interlocked functions! */
@ -55,6 +65,8 @@ typedef struct _USER_MESSAGE_QUEUE
LIST_ENTRY NotifyMessagesListHead;
/* Queue for hardware messages for the queue. */
LIST_ENTRY HardwareMessagesListHead;
/* List of timers, sorted on expiry time (earliest first) */
LIST_ENTRY TimerListHead;
/* Lock for the hardware message list. */
KMUTEX HardwareLock;
/* Lock for the queue. */
@ -144,13 +156,12 @@ MsqDestroyMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue);
PUSER_MESSAGE_QUEUE FASTCALL
MsqGetHardwareMessageQueue(VOID);
NTSTATUS FASTCALL
MsqWaitForNewMessage(PUSER_MESSAGE_QUEUE MessageQueue);
NTSTATUS FASTCALL
MsqInitializeImpl(VOID);
BOOLEAN FASTCALL
MsqDispatchOneSentMessage(PUSER_MESSAGE_QUEUE MessageQueue);
NTSTATUS FASTCALL
MsqWaitForNewMessages(PUSER_MESSAGE_QUEUE MessageQueue);
MsqWaitForNewMessages(PUSER_MESSAGE_QUEUE MessageQueue, HWND WndFilter,
UINT MsgFilterMin, UINT MsgFilterMax);
VOID FASTCALL
MsqSendNotifyMessage(PUSER_MESSAGE_QUEUE MessageQueue,
PUSER_SENT_MESSAGE_NOTIFY NotifyMessage);
@ -248,6 +259,24 @@ IntMsqSetWakeMask(DWORD WakeMask);
BOOL FASTCALL
IntMsqClearWakeMask(VOID);
BOOLEAN FASTCALL
MsqSetTimer(PUSER_MESSAGE_QUEUE MessageQueue, HWND Wnd,
UINT_PTR IDEvent, UINT Period, TIMERPROC TimerFunc,
UINT Msg);
BOOLEAN FASTCALL
MsqKillTimer(PUSER_MESSAGE_QUEUE MessageQueue, HWND Wnd,
UINT_PTR IDEvent, UINT Msg);
BOOLEAN FASTCALL
MsqGetTimerMessage(PUSER_MESSAGE_QUEUE MessageQueue,
HWND WndFilter, UINT MsgFilterMin, UINT MsgFilterMax,
MSG *Msg, BOOLEAN Restart);
BOOLEAN FASTCALL
MsqGetFirstTimerExpiry(PUSER_MESSAGE_QUEUE MessageQueue,
HWND WndFilter, UINT MsgFilterMin, UINT MsgFilterMax,
PLARGE_INTEGER FirstTimerExpiry);
VOID FASTCALL
MsqRemoveTimersWindow(PUSER_MESSAGE_QUEUE MessageQueue, HWND Wnd);
#endif /* _WIN32K_MSGQUEUE_H */
/* EOF */

View file

@ -1,18 +1,8 @@
#ifndef _WIN32K_TIMER_H
#define _WIN32K_TIMER_H
typedef struct _MSG_TIMER_ENTRY{
LIST_ENTRY ListEntry;
LARGE_INTEGER Timeout;
HANDLE ThreadID;
UINT Period;
MSG Msg;
} MSG_TIMER_ENTRY, *PMSG_TIMER_ENTRY;
NTSTATUS FASTCALL InitTimerImpl(VOID);
VOID FASTCALL RemoveTimersThread(HANDLE ThreadID);
VOID FASTCALL RemoveTimersWindow(HWND hWnd);
PMSG_TIMER_ENTRY FASTCALL IntRemoveTimer(HWND hWnd, UINT_PTR IDEvent, HANDLE ThreadID, BOOL SysTimer);
UINT_PTR FASTCALL IntSetTimer(HWND hWnd, UINT_PTR nIDEvent, UINT uElapse, TIMERPROC lpTimerFunc, BOOL SystemTimer);
BOOL FASTCALL IntKillTimer(HWND Wnd, UINT_PTR IDEvent, BOOL SystemTimer);
UINT_PTR FASTCALL IntSetTimer(HWND Wnd, UINT_PTR IDEvent, UINT Elapse, TIMERPROC TimerFunc, BOOL SystemTimer);
#endif /* _WIN32K_TIMER_H */

View file

@ -16,7 +16,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* $Id: dllmain.c,v 1.85 2004/12/24 17:45:58 weiden Exp $
/* $Id: dllmain.c,v 1.86 2004/12/29 19:55:01 gvg Exp $
*
* Entry Point for win32k.sys
*/
@ -184,7 +184,6 @@ Win32kThreadCallback (struct _ETHREAD *Thread,
Win32Thread->IsExiting = TRUE;
HOOK_DestroyThreadHooks(Thread);
RemoveTimersThread(Win32Thread->MessageQueue);
UnregisterThreadHotKeys(Thread);
DestroyThreadWindows(Thread);
IntBlockInput(Win32Thread, FALSE);

View file

@ -1,4 +1,4 @@
/* $Id: caret.c,v 1.16 2004/12/25 22:59:10 navaraf Exp $
/* $Id: caret.c,v 1.17 2004/12/29 19:55:01 gvg Exp $
*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
@ -240,7 +240,7 @@ NtUserCreateCaret(
return FALSE;
}
IntRemoveTimer(hWnd, IDCARETTIMER, PsGetCurrentThreadId(), TRUE);
IntKillTimer(hWnd, IDCARETTIMER, TRUE);
ThreadQueue = (PUSER_MESSAGE_QUEUE)PsGetWin32Thread()->MessageQueue;
@ -325,7 +325,7 @@ NtUserHideCaret(
if(ThreadQueue->CaretInfo->Visible)
{
IntRemoveTimer(hWnd, IDCARETTIMER, PsGetCurrentThreadId(), TRUE);
IntKillTimer(hWnd, IDCARETTIMER, TRUE);
IntHideCaret(ThreadQueue->CaretInfo);
ThreadQueue->CaretInfo->Visible = 0;

View file

@ -16,7 +16,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* $Id: message.c,v 1.78 2004/12/25 22:59:10 navaraf Exp $
/* $Id: message.c,v 1.79 2004/12/29 19:55:01 gvg Exp $
*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
@ -690,7 +690,14 @@ IntPeekMessage(PUSER_MESSAGE Msg,
return TRUE;
}
/* FIXME - get WM_(SYS)TIMER messages */
/* Check for WM_(SYS)TIMER messages */
Present = MsqGetTimerMessage(ThreadQueue, Wnd, MsgFilterMin, MsgFilterMax,
&Msg->Msg, RemoveMessages);
if (Present)
{
Msg->FreeLParam = FALSE;
goto MessageFound;
}
if(Present)
{
@ -845,8 +852,8 @@ NtUserPeekMessage(PNTUSERGETMESSAGEINFO UnsafeInfo,
static BOOL FASTCALL
IntWaitMessage(HWND Wnd,
UINT MsgFilterMin,
UINT MsgFilterMax)
UINT MsgFilterMin,
UINT MsgFilterMax)
{
PUSER_MESSAGE_QUEUE ThreadQueue;
NTSTATUS Status;
@ -862,9 +869,9 @@ IntWaitMessage(HWND Wnd,
}
/* Nothing found. Wait for new messages. */
Status = MsqWaitForNewMessages(ThreadQueue);
Status = MsqWaitForNewMessages(ThreadQueue, Wnd, MsgFilterMin, MsgFilterMax);
}
while (STATUS_WAIT_0 <= Status && Status <= STATUS_WAIT_63);
while ((STATUS_WAIT_0 <= Status && Status <= STATUS_WAIT_63) || STATUS_TIMEOUT == Status);
SetLastNtError(Status);

View file

@ -16,7 +16,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* $Id: msgqueue.c,v 1.111 2004/12/25 22:59:10 navaraf Exp $
/* $Id: msgqueue.c,v 1.112 2004/12/29 19:55:01 gvg Exp $
*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
@ -53,6 +53,7 @@ static KMUTEX HardwareMessageQueueLock;
static KEVENT HardwareMessageEvent;
static PAGED_LOOKASIDE_LIST MessageLookasideList;
static PAGED_LOOKASIDE_LIST TimerLookasideList;
#define IntLockSystemMessageQueue(OldIrql) \
KeAcquireSpinLock(&SystemMessageQueueLock, &OldIrql)
@ -145,6 +146,13 @@ MsqInitializeImpl(VOID)
sizeof(USER_MESSAGE),
0,
256);
ExInitializePagedLookasideList(&TimerLookasideList,
NULL,
NULL,
0,
sizeof(TIMER_ENTRY),
0,
64);
return(STATUS_SUCCESS);
}
@ -1195,16 +1203,29 @@ MsqFindMessage(IN PUSER_MESSAGE_QUEUE MessageQueue,
}
NTSTATUS FASTCALL
MsqWaitForNewMessages(PUSER_MESSAGE_QUEUE MessageQueue)
MsqWaitForNewMessages(PUSER_MESSAGE_QUEUE MessageQueue, HWND WndFilter,
UINT MsgFilterMin, UINT MsgFilterMax)
{
PVOID WaitObjects[2] = {MessageQueue->NewMessages, &HardwareMessageEvent};
LARGE_INTEGER TimerExpiry;
PLARGE_INTEGER Timeout;
if (MsqGetFirstTimerExpiry(MessageQueue, WndFilter, MsgFilterMin, MsgFilterMax, &TimerExpiry))
{
Timeout = &TimerExpiry;
}
else
{
Timeout = NULL;
}
return(KeWaitForMultipleObjects(2,
WaitObjects,
WaitAny,
Executive,
UserMode,
FALSE,
NULL,
Timeout,
NULL));
}
@ -1228,6 +1249,7 @@ MsqInitializeMessageQueue(struct _ETHREAD *Thread, PUSER_MESSAGE_QUEUE MessageQu
InitializeListHead(&MessageQueue->PostedMessagesListHead);
InitializeListHead(&MessageQueue->SentMessagesListHead);
InitializeListHead(&MessageQueue->HardwareMessagesListHead);
InitializeListHead(&MessageQueue->TimerListHead);
InitializeListHead(&MessageQueue->DispatchingMessagesHead);
InitializeListHead(&MessageQueue->LocalDispatchingMessagesHead);
KeInitializeMutex(&MessageQueue->HardwareLock, 0);
@ -1267,6 +1289,7 @@ MsqCleanupMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue)
{
PLIST_ENTRY CurrentEntry;
PUSER_MESSAGE CurrentMessage;
PTIMER_ENTRY CurrentTimer;
PUSER_SENT_MESSAGE CurrentSentMessage;
IntLockMessageQueue(MessageQueue);
@ -1311,6 +1334,14 @@ MsqCleanupMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue)
ExFreePool(CurrentSentMessage);
}
/* cleanup timers */
while (! IsListEmpty(&MessageQueue->TimerListHead))
{
CurrentEntry = RemoveHeadList(&MessageQueue->TimerListHead);
CurrentTimer = CONTAINING_RECORD(CurrentEntry, TIMER_ENTRY, ListEntry);
ExFreeToPagedLookasideList(&TimerLookasideList, CurrentTimer);
}
/* notify senders of dispatching messages. This needs to be cleaned up if e.g.
ExitThread() was called in a SendMessage() umode callback */
while (!IsListEmpty(&MessageQueue->LocalDispatchingMessagesHead))
@ -1476,4 +1507,300 @@ MsqSetStateWindow(PUSER_MESSAGE_QUEUE MessageQueue, ULONG Type, HWND hWnd)
return NULL;
}
#ifndef NDEBUG
static VOID FASTCALL
DumpTimerList(PUSER_MESSAGE_QUEUE MessageQueue)
{
PLIST_ENTRY Current;
PTIMER_ENTRY Timer;
Current = MessageQueue->TimerListHead.Flink;
if (Current == &MessageQueue->TimerListHead)
{
DPRINT("timer list is empty for queue %p\n", MessageQueue);
}
while (Current != &MessageQueue->TimerListHead)
{
Timer = CONTAINING_RECORD(Current, TIMER_ENTRY, ListEntry);
DPRINT("queue %p timer %p expiry %I64d wnd %x id %p period %u timerproc %p msg %u\n",
MessageQueue, Timer, Timer->ExpiryTime.QuadPart, Timer->Wnd, Timer->IDEvent,
Timer->Period, Timer->TimerFunc, Timer->Msg);
Current = Current->Flink;
}
}
#endif /* ! defined(NDEBUG) */
/* Must have the message queue locked while calling this */
static VOID FASTCALL
InsertTimer(PUSER_MESSAGE_QUEUE MessageQueue, PTIMER_ENTRY NewTimer)
{
PLIST_ENTRY Current;
Current = MessageQueue->TimerListHead.Flink;
while (Current != &MessageQueue->TimerListHead)
{
if (NewTimer->ExpiryTime.QuadPart <
CONTAINING_RECORD(Current, TIMER_ENTRY, ListEntry)->ExpiryTime.QuadPart)
{
break;
}
Current = Current->Flink;
}
InsertTailList(Current, &NewTimer->ListEntry);
}
/* Must have the message queue locked while calling this */
static PTIMER_ENTRY FASTCALL
RemoveTimer(PUSER_MESSAGE_QUEUE MessageQueue, HWND Wnd, UINT_PTR IDEvent, UINT Msg)
{
PTIMER_ENTRY Timer;
PLIST_ENTRY EnumEntry;
/* Remove timer if already in the queue */
EnumEntry = MessageQueue->TimerListHead.Flink;
while (EnumEntry != &MessageQueue->TimerListHead)
{
Timer = CONTAINING_RECORD(EnumEntry, TIMER_ENTRY, ListEntry);
EnumEntry = EnumEntry->Flink;
if (Timer->Wnd == Wnd &&
Timer->IDEvent == IDEvent &&
Timer->Msg == Msg)
{
RemoveEntryList(&Timer->ListEntry);
return Timer;
}
}
return NULL;
}
BOOLEAN FASTCALL
MsqSetTimer(PUSER_MESSAGE_QUEUE MessageQueue, HWND Wnd,
UINT_PTR IDEvent, UINT Period, TIMERPROC TimerFunc,
UINT Msg)
{
PTIMER_ENTRY Timer;
LARGE_INTEGER CurrentTime;
DPRINT("MsqSetTimer queue %p wnd %x id %p period %u timerproc %p msg %d\n",
MessageQueue, Wnd, IDEvent, Period, TimerFunc, Msg);
IntLockMessageQueue(MessageQueue);
Timer = RemoveTimer(MessageQueue, Wnd, IDEvent, Msg);
if (NULL == Timer)
{
Timer = ExAllocateFromPagedLookasideList(&TimerLookasideList);
if (NULL == Timer)
{
IntUnLockMessageQueue(MessageQueue);
DPRINT1("Failed to allocate timer entry\n");
return FALSE;
}
DPRINT("Allocated new timer entry %p\n", Timer);
Timer->Wnd = Wnd;
Timer->IDEvent = IDEvent;
Timer->Msg = Msg;
}
else
{
DPRINT("Updating existing timer entry %p\n", Timer);
}
KeQuerySystemTime(&CurrentTime);
Timer->ExpiryTime.QuadPart = CurrentTime.QuadPart +
(ULONGLONG) Period * (ULONGLONG) 10000;
Timer->Period = Period;
Timer->TimerFunc = TimerFunc;
DPRINT("Insert timer now %I64d expiry %I64d\n", CurrentTime.QuadPart,
Timer->ExpiryTime.QuadPart);
InsertTimer(MessageQueue, Timer);
#ifndef NDEBUG
DumpTimerList(MessageQueue);
#endif /* ! defined(NDEBUG) */
IntUnLockMessageQueue(MessageQueue);
return TRUE;
}
BOOLEAN FASTCALL
MsqKillTimer(PUSER_MESSAGE_QUEUE MessageQueue, HWND Wnd,
UINT_PTR IDEvent, UINT Msg)
{
PTIMER_ENTRY Timer;
DPRINT("MsqKillTimer queue %p wnd %x id %p msg %d\n",
MessageQueue, Wnd, IDEvent, Msg);
IntLockMessageQueue(MessageQueue);
Timer = RemoveTimer(MessageQueue, Wnd, IDEvent, Msg);
if (NULL == Timer)
{
DPRINT("Failed to remove timer from list, not found\n");
}
else
{
ExFreeToPagedLookasideList(&TimerLookasideList, Timer);
}
#ifndef NDEBUG
DumpTimerList(MessageQueue);
#endif /* ! defined(NDEBUG) */
IntUnLockMessageQueue(MessageQueue);
return NULL != Timer;
}
BOOLEAN FASTCALL
MsqGetTimerMessage(PUSER_MESSAGE_QUEUE MessageQueue,
HWND WndFilter, UINT MsgFilterMin, UINT MsgFilterMax,
MSG *Msg, BOOLEAN Restart)
{
PTIMER_ENTRY Timer;
LARGE_INTEGER CurrentTime;
PLIST_ENTRY EnumEntry;
BOOLEAN GotMessage;
DPRINT("MsqGetTimerMessage queue %p msg %p restart %s\n",
MessageQueue, Msg, Restart ? "TRUE" : "FALSE");
IntLockMessageQueue(MessageQueue);
KeQuerySystemTime(&CurrentTime);
DPRINT("Current time %I64d\n", CurrentTime.QuadPart);
EnumEntry = MessageQueue->TimerListHead.Flink;
GotMessage = FALSE;
while (EnumEntry != &MessageQueue->TimerListHead)
{
Timer = CONTAINING_RECORD(MessageQueue->TimerListHead.Flink,
TIMER_ENTRY, ListEntry);
DPRINT("Checking timer %p wnd %x expiry %I64d\n", Timer, Timer->wnd,
Timer->ExpiryTime.QuadPart);
EnumEntry = EnumEntry->Flink;
if ((NULL == WndFilter || Timer->Wnd == WndFilter) &&
((MsgFilterMin == 0 && MsgFilterMax == 0) ||
(MsgFilterMin <= Timer->Msg &&
Timer->Msg <= MsgFilterMax)))
{
if (Timer->ExpiryTime.QuadPart <= CurrentTime.QuadPart)
{
DPRINT("Timer is expired\n");
GotMessage = TRUE;
break;
}
else
{
DPRINT("No need to check later timers\n");
break;
}
}
else
{
DPRINT("timer %p (wnd %x msg %d) failed filter wnd %x msgmin %d msgmax %d\n",
Timer, Timer->Wnd, Timer->Msg, WndFilter, MsgFilterMin, MsgFilterMax);
}
}
if (! GotMessage)
{
DPRINT("No timer pending\n");
IntUnLockMessageQueue(MessageQueue);
return FALSE;
}
Msg->hwnd = Timer->Wnd;
Msg->message = Timer->Msg;
Msg->wParam = (WPARAM) Timer->IDEvent;
Msg->lParam = (LPARAM) Timer->TimerFunc;
if (Restart)
{
RemoveEntryList(&Timer->ListEntry);
Timer->ExpiryTime.QuadPart = CurrentTime.QuadPart +
(ULONGLONG) Timer->Period * (ULONGLONG) 10000;
DPRINT("Restarting timer %p expires %I64d\n", Timer, Timer->ExpiryTime.QuadPart);
InsertTimer(MessageQueue, Timer);
#ifndef NDEBUG
DumpTimerList(MessageQueue);
#endif /* ! defined(NDEBUG) */
}
IntUnLockMessageQueue(MessageQueue);
DPRINT("Created message wnd %x msg %d wParam %u lParam %u\n", Msg->hwnd, Msg->message,
Msg->wParam, Msg->lParam);
return TRUE;
}
VOID FASTCALL
MsqRemoveTimersWindow(PUSER_MESSAGE_QUEUE MessageQueue, HWND Wnd)
{
PTIMER_ENTRY Timer;
PLIST_ENTRY EnumEntry;
DPRINT("MsqRemoveTimersWindow queue %p wnd %x\n", MessageQueue, Wnd);
IntLockMessageQueue(MessageQueue);
EnumEntry = MessageQueue->TimerListHead.Flink;
while (EnumEntry != &MessageQueue->TimerListHead)
{
Timer = CONTAINING_RECORD(EnumEntry, TIMER_ENTRY, ListEntry);
EnumEntry = EnumEntry->Flink;
if (Timer->Wnd == Wnd)
{
DPRINT("Removing timer %p because its window is going away\n", Timer);
RemoveEntryList(&Timer->ListEntry);
ExFreeToPagedLookasideList(&TimerLookasideList, Timer);
}
}
#ifndef NDEBUG
DumpTimerList(MessageQueue);
#endif /* ! defined(NDEBUG) */
IntUnLockMessageQueue(MessageQueue);
}
BOOLEAN FASTCALL
MsqGetFirstTimerExpiry(PUSER_MESSAGE_QUEUE MessageQueue,
HWND WndFilter, UINT MsgFilterMin, UINT MsgFilterMax,
PLARGE_INTEGER FirstTimerExpiry)
{
PTIMER_ENTRY Timer;
PLIST_ENTRY EnumEntry;
DPRINT("MsqGetFirstTimerExpiry queue %p wndfilter %x msgfiltermin %d msgfiltermax %d expiry %p\n",
MessageQueue, WndFilter, MsgFilterMin, MsgFilterMax, FirstTimerExpiry);
IntLockMessageQueue(MessageQueue);
EnumEntry = MessageQueue->TimerListHead.Flink;
while (EnumEntry != &MessageQueue->TimerListHead)
{
Timer = CONTAINING_RECORD(MessageQueue->TimerListHead.Flink,
TIMER_ENTRY, ListEntry);
EnumEntry = EnumEntry->Flink;
if ((NULL == WndFilter || Timer->Wnd == WndFilter) &&
((MsgFilterMin == 0 && MsgFilterMax == 0) ||
(MsgFilterMin <= Timer->Msg &&
Timer->Msg <= MsgFilterMax)))
{
*FirstTimerExpiry = Timer->ExpiryTime;
DPRINT("First timer expires %I64d\n", Timer->ExpiryTime);
IntUnLockMessageQueue(MessageQueue);
return TRUE;
}
}
IntUnLockMessageQueue(MessageQueue);
return FALSE;
}
/* EOF */

View file

@ -16,7 +16,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* $Id: timer.c,v 1.37 2004/12/25 22:59:10 navaraf Exp $
/* $Id: timer.c,v 1.38 2004/12/29 19:55:01 gvg Exp $
*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
@ -32,446 +32,165 @@
#include <w32k.h>
#define NDEBUG
#undef NDEBUG
#include <debug.h>
/* GLOBALS *******************************************************************/
//windows 2000 has room for 32768 window-less timers
/* Windows 2000 has room for 32768 window-less timers */
#define NUM_WINDOW_LESS_TIMERS 1024
static FAST_MUTEX Mutex;
static LIST_ENTRY TimerListHead;
static KTIMER Timer;
static RTL_BITMAP WindowLessTimersBitMap;
static PVOID WindowLessTimersBitMapBuffer;
static ULONG HintIndex = 0;
static HANDLE MsgTimerThreadHandle;
static CLIENT_ID MsgTimerThreadId;
#define IntLockTimerList() \
#define IntLockWindowlessTimerBitmap() \
ExAcquireFastMutex(&Mutex)
#define IntUnLockTimerList() \
#define IntUnlockWindowlessTimerBitmap() \
ExReleaseFastMutex(&Mutex)
/* FUNCTIONS *****************************************************************/
//return true if the new timer became the first entry
//must hold mutex while calling this
BOOL FASTCALL
IntInsertTimerAscendingOrder(PMSG_TIMER_ENTRY NewTimer)
{
PLIST_ENTRY current;
current = TimerListHead.Flink;
while (current != &TimerListHead)
{
if (CONTAINING_RECORD(current, MSG_TIMER_ENTRY, ListEntry)->Timeout.QuadPart >=\
NewTimer->Timeout.QuadPart)
{
break;
}
current = current->Flink;
}
InsertTailList(current, &NewTimer->ListEntry);
return TimerListHead.Flink == &NewTimer->ListEntry;
}
//must hold mutex while calling this
PMSG_TIMER_ENTRY FASTCALL
IntRemoveTimer(HWND hWnd, UINT_PTR IDEvent, HANDLE ThreadID, BOOL SysTimer)
{
PMSG_TIMER_ENTRY MsgTimer;
PLIST_ENTRY EnumEntry;
//remove timer if already in the queue
EnumEntry = TimerListHead.Flink;
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 &&
(MsgTimer->Msg.message == WM_SYSTIMER) == SysTimer)
{
RemoveEntryList(&MsgTimer->ListEntry);
return MsgTimer;
}
}
return NULL;
}
/*
* NOTE: It doesn't kill the timer. It just removes them from the list.
*/
VOID FASTCALL
RemoveTimersThread(HANDLE ThreadID)
{
PMSG_TIMER_ENTRY MsgTimer;
PLIST_ENTRY EnumEntry;
IntLockTimerList();
EnumEntry = TimerListHead.Flink;
while (EnumEntry != &TimerListHead)
{
MsgTimer = CONTAINING_RECORD(EnumEntry, MSG_TIMER_ENTRY, ListEntry);
EnumEntry = EnumEntry->Flink;
if (MsgTimer->ThreadID == ThreadID)
{
if (MsgTimer->Msg.hwnd == NULL)
{
RtlClearBits(&WindowLessTimersBitMap, ((UINT_PTR)MsgTimer->Msg.wParam) - 1, 1);
}
RemoveEntryList(&MsgTimer->ListEntry);
ExFreePool(MsgTimer);
}
}
IntUnLockTimerList();
}
/*
* NOTE: It doesn't kill the timer. It just removes them from the list.
*/
VOID FASTCALL
RemoveTimersWindow(HWND Wnd)
{
PMSG_TIMER_ENTRY MsgTimer;
PLIST_ENTRY EnumEntry;
IntLockTimerList();
EnumEntry = TimerListHead.Flink;
while (EnumEntry != &TimerListHead)
{
MsgTimer = CONTAINING_RECORD(EnumEntry, MSG_TIMER_ENTRY, ListEntry);
EnumEntry = EnumEntry->Flink;
if (MsgTimer->Msg.hwnd == Wnd)
{
RemoveEntryList(&MsgTimer->ListEntry);
ExFreePool(MsgTimer);
}
}
IntUnLockTimerList();
}
UINT_PTR FASTCALL
IntSetTimer(HWND hWnd, UINT_PTR nIDEvent, UINT uElapse, TIMERPROC lpTimerFunc, BOOL SystemTimer)
IntSetTimer(HWND Wnd, UINT_PTR IDEvent, UINT Elapse, TIMERPROC TimerFunc, BOOL SystemTimer)
{
PMSG_TIMER_ENTRY MsgTimer = NULL;
PMSG_TIMER_ENTRY NewTimer;
LARGE_INTEGER CurrentTime;
PWINDOW_OBJECT WindowObject;
HANDLE ThreadID;
UINT_PTR Ret = 0;
ThreadID = PsGetCurrentThreadId();
KeQuerySystemTime(&CurrentTime);
IntLockTimerList();
DPRINT("IntSetTimer wnd %x id %p elapse %u timerproc %p systemtimer %s\n",
Wnd, IDEvent, Elapse, TimerFunc, SystemTimer ? "TRUE" : "FALSE");
if((hWnd == NULL) && !SystemTimer)
{
/* find a free, window-less timer id */
nIDEvent = RtlFindClearBitsAndSet(&WindowLessTimersBitMap, 1, HintIndex);
if(nIDEvent == (UINT_PTR) -1)
if ((Wnd == NULL) && ! SystemTimer)
{
IntUnLockTimerList();
return 0;
}
DPRINT("Window-less timer\n");
/* find a free, window-less timer id */
IntLockWindowlessTimerBitmap();
IDEvent = RtlFindClearBitsAndSet(&WindowLessTimersBitMap, 1, HintIndex);
HintIndex = ++nIDEvent;
}
if (IDEvent == (UINT_PTR) -1)
{
IntUnlockWindowlessTimerBitmap();
DPRINT1("Unable to find a free window-less timer id\n");
SetLastWin32Error(ERROR_NO_SYSTEM_RESOURCES);
return 0;
}
HintIndex = ++IDEvent;
IntUnlockWindowlessTimerBitmap();
Ret = IDEvent;
}
else
{
WindowObject = IntGetWindowObject(hWnd);
if(!WindowObject)
{
IntUnLockTimerList();
SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE);
return 0;
}
WindowObject = IntGetWindowObject(Wnd);
if (! WindowObject)
{
DPRINT1("Invalid window handle\n");
SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE);
return 0;
}
if(WindowObject->OwnerThread != PsGetCurrentThread())
{
IntUnLockTimerList();
if (WindowObject->OwnerThread != PsGetCurrentThread())
{
IntReleaseWindowObject(WindowObject);
DPRINT1("Trying to set timer for window in another thread (shatter attack?)\n");
SetLastWin32Error(ERROR_ACCESS_DENIED);
return 0;
}
IntReleaseWindowObject(WindowObject);
SetLastWin32Error(ERROR_ACCESS_DENIED);
return 0;
Ret = 1;
}
IntReleaseWindowObject(WindowObject);
/* remove timer if already in the queue */
MsgTimer = IntRemoveTimer(hWnd, nIDEvent, ThreadID, SystemTimer);
}
#if 1
/* Win NT/2k/XP */
if(uElapse > 0x7fffffff)
uElapse = 1;
if (Elapse > 0x7fffffff)
{
DPRINT("Adjusting uElapse\n");
Elapse = 1;
}
#else
/* Win Server 2003 */
if(uElapse > 0x7fffffff)
uElapse = 0x7fffffff;
if (Elapse > 0x7fffffff)
{
DPRINT("Adjusting uElapse\n");
Elapse = 0x7fffffff;
}
#endif
/* Win 2k/XP */
if(uElapse < 10)
uElapse = 10;
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 = ExAllocatePoolWithTag(PagedPool, sizeof(MSG_TIMER_ENTRY), TAG_TIMER);
if(!NewTimer)
if (Elapse < 10)
{
IntUnLockTimerList();
SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
DPRINT("Adjusting uElapse\n");
Elapse = 10;
}
if (! MsqSetTimer(PsGetWin32Thread()->MessageQueue, Wnd,
IDEvent, Elapse, TimerFunc,
SystemTimer ? WM_SYSTIMER : WM_TIMER))
{
DPRINT1("Failed to set timer in message queue\n");
SetLastWin32Error(ERROR_NO_SYSTEM_RESOURCES);
return 0;
}
NewTimer->Msg.hwnd = hWnd;
NewTimer->Msg.message = (SystemTimer ? WM_SYSTIMER : 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;
}
Ret = nIDEvent; // FIXME - return lpTimerProc if it's not a system timer
if(IntInsertTimerAscendingOrder(NewTimer))
{
/* new timer is first in queue and expires first */
KeSetTimer(&Timer, NewTimer->Timeout, NULL);
}
IntUnLockTimerList();
return Ret;
}
BOOL FASTCALL
IntKillTimer(HWND hWnd, UINT_PTR uIDEvent, BOOL SystemTimer)
IntKillTimer(HWND Wnd, UINT_PTR IDEvent, BOOL SystemTimer)
{
PMSG_TIMER_ENTRY MsgTimer;
PWINDOW_OBJECT WindowObject;
IntLockTimerList();
DPRINT("IntKillTimer wnd %x id %p systemtimer %s\n",
Wnd, IDEvent, SystemTimer ? "TRUE" : "FALSE");
if (! MsqKillTimer(PsGetWin32Thread()->MessageQueue, Wnd,
IDEvent, SystemTimer ? WM_SYSTIMER : WM_TIMER))
{
DPRINT1("Unable to locate timer in message queue\n");
SetLastWin32Error(ERROR_INVALID_PARAMETER);
return FALSE;
}
/* window-less timer? */
if((hWnd == NULL) && !SystemTimer)
{
if(!RtlAreBitsSet(&WindowLessTimersBitMap, uIDEvent - 1, 1))
if ((Wnd == NULL) && ! SystemTimer)
{
IntUnLockTimerList();
/* bit was not set */
/* FIXME: set the last error */
return FALSE;
/* Release the id */
IntLockWindowlessTimerBitmap();
ASSERT(RtlAreBitsSet(&WindowLessTimersBitMap, IDEvent - 1, 1));
RtlClearBits(&WindowLessTimersBitMap, IDEvent - 1, 1);
IntUnlockWindowlessTimerBitmap();
}
RtlClearBits(&WindowLessTimersBitMap, uIDEvent - 1, 1);
}
else
{
WindowObject = IntGetWindowObject(hWnd);
if(!WindowObject)
{
IntUnLockTimerList();
SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE);
return FALSE;
}
if(WindowObject->OwnerThread != PsGetCurrentThread())
{
IntUnLockTimerList();
IntReleaseWindowObject(WindowObject);
SetLastWin32Error(ERROR_ACCESS_DENIED);
return FALSE;
}
IntReleaseWindowObject(WindowObject);
}
MsgTimer = IntRemoveTimer(hWnd, uIDEvent, PsGetCurrentThreadId(), SystemTimer);
IntUnLockTimerList();
if(MsgTimer == NULL)
{
/* didn't find timer */
/* FIXME: set the last error */
return FALSE;
}
/* FIXME: use lookaside? */
ExFreePool(MsgTimer);
return TRUE;
}
static VOID STDCALL
TimerThreadMain(PVOID StartContext)
{
NTSTATUS Status;
LARGE_INTEGER CurrentTime;
PLIST_ENTRY EnumEntry;
PMSG_TIMER_ENTRY MsgTimer;
PETHREAD Thread;
PETHREAD *ThreadsToDereference;
ULONG ThreadsToDereferenceCount, ThreadsToDereferencePos, i;
for(;;)
{
Status = KeWaitForSingleObject(&Timer,
Executive,
KernelMode,
FALSE,
NULL);
if (!NT_SUCCESS(Status))
{
DPRINT1("Error waiting in TimerThreadMain\n");
KEBUGCHECK(0);
}
ThreadsToDereferenceCount = ThreadsToDereferencePos = 0;
IntLockTimerList();
KeQuerySystemTime(&CurrentTime);
for (EnumEntry = TimerListHead.Flink;
EnumEntry != &TimerListHead;
EnumEntry = EnumEntry->Flink)
{
MsgTimer = CONTAINING_RECORD(EnumEntry, MSG_TIMER_ENTRY, ListEntry);
if (CurrentTime.QuadPart >= MsgTimer->Timeout.QuadPart)
++ThreadsToDereferenceCount;
else
break;
}
ThreadsToDereference = (PETHREAD *)ExAllocatePoolWithTag(
NonPagedPool, ThreadsToDereferenceCount * sizeof(PETHREAD), TAG_TIMERTD);
EnumEntry = TimerListHead.Flink;
while (EnumEntry != &TimerListHead)
{
MsgTimer = CONTAINING_RECORD(EnumEntry, MSG_TIMER_ENTRY, ListEntry);
if (CurrentTime.QuadPart >= MsgTimer->Timeout.QuadPart)
{
EnumEntry = EnumEntry->Flink;
RemoveEntryList(&MsgTimer->ListEntry);
/*
* FIXME: 1) Find a faster way of getting the thread message queue? (lookup by id is slow)
*/
if (!NT_SUCCESS(PsLookupThreadByThreadId(MsgTimer->ThreadID, &Thread)))
{
ExFreePool(MsgTimer);
continue;
}
MsqPostMessage(Thread->Tcb.Win32Thread->MessageQueue, &MsgTimer->Msg,
FALSE, QS_TIMER);
ThreadsToDereference[ThreadsToDereferencePos] = Thread;
++ThreadsToDereferencePos;
//set up next periodic timeout
//FIXME: is this calculation really necesary (and correct)? -Gunnar
do
{
MsgTimer->Timeout.QuadPart += (MsgTimer->Period * 10000);
}
while (MsgTimer->Timeout.QuadPart <= CurrentTime.QuadPart);
IntInsertTimerAscendingOrder(MsgTimer);
}
else
{
break;
}
}
//set up next timeout from first entry (if any)
if (!IsListEmpty(&TimerListHead))
{
MsgTimer = CONTAINING_RECORD( TimerListHead.Flink, MSG_TIMER_ENTRY, ListEntry);
KeSetTimer(&Timer, MsgTimer->Timeout, NULL);
}
IntUnLockTimerList();
for (i = 0; i < ThreadsToDereferencePos; i++)
ObDereferenceObject(ThreadsToDereference[i]);
ExFreePool(ThreadsToDereference);
}
}
NTSTATUS FASTCALL
InitTimerImpl(VOID)
{
NTSTATUS Status;
ULONG BitmapBytes;
BitmapBytes = ROUND_UP(NUM_WINDOW_LESS_TIMERS, sizeof(ULONG) * 8) / 8;
InitializeListHead(&TimerListHead);
KeInitializeTimerEx(&Timer, SynchronizationTimer);
ExInitializeFastMutex(&Mutex);
BitmapBytes = ROUND_UP(NUM_WINDOW_LESS_TIMERS, sizeof(ULONG) * 8) / 8;
WindowLessTimersBitMapBuffer = ExAllocatePoolWithTag(PagedPool, BitmapBytes, TAG_TIMERBMP);
RtlInitializeBitMap(&WindowLessTimersBitMap,
WindowLessTimersBitMapBuffer,
BitmapBytes * 8);
//yes we need this, since ExAllocatePool isn't supposed to zero out allocated memory
/* yes we need this, since ExAllocatePool isn't supposed to zero out allocated memory */
RtlClearAllBits(&WindowLessTimersBitMap);
Status = PsCreateSystemThread(&MsgTimerThreadHandle,
THREAD_ALL_ACCESS,
NULL,
NULL,
&MsgTimerThreadId,
TimerThreadMain,
NULL);
return Status;
return STATUS_SUCCESS;
}

View file

@ -16,7 +16,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* $Id: window.c,v 1.258 2004/12/25 20:30:50 navaraf Exp $
/* $Id: window.c,v 1.259 2004/12/29 19:55:01 gvg Exp $
*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
@ -268,7 +268,7 @@ static LRESULT IntDestroyWindow(PWINDOW_OBJECT Window,
ASSERT(Window);
RemoveTimersWindow(Window->Self);
MsqRemoveTimersWindow(ThreadData->MessageQueue, Window->Self);
IntLockThreadWindows(Window->OwnerThread->Tcb.Win32Thread);
if(Window->Status & WINDOWSTATUS_DESTROYING)