Generate timer messages instead of queueing them
authorGé van Geldorp <ge@gse.nl>
Wed, 29 Dec 2004 19:55:01 +0000 (19:55 +0000)
committerGé van Geldorp <ge@gse.nl>
Wed, 29 Dec 2004 19:55:01 +0000 (19:55 +0000)
svn path=/trunk/; revision=12387

reactos/subsys/win32k/include/msgqueue.h
reactos/subsys/win32k/include/timer.h
reactos/subsys/win32k/main/dllmain.c
reactos/subsys/win32k/ntuser/caret.c
reactos/subsys/win32k/ntuser/message.c
reactos/subsys/win32k/ntuser/msgqueue.c
reactos/subsys/win32k/ntuser/timer.c
reactos/subsys/win32k/ntuser/window.c

index e1bbfd3..ebe140f 100644 (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 */
index 5bb392c..ea99591 100644 (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 */
index 0e3ddf8..ef624d4 100644 (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);
index 6f5b403..4e06ba6 100644 (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;
index 851b9c3..1ce7377 100644 (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);
 
index 20c1b59..e3a153c 100644 (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 */
index 8d4a190..ec26c1f 100644 (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
 
 #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);
+    
+      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 = ++nIDEvent;
-  }
+      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();
-  
-  /* window-less timer? */
-  if((hWnd == NULL) && !SystemTimer)
-  {
-    if(!RtlAreBitsSet(&WindowLessTimersBitMap, uIDEvent - 1, 1))
-    {
-      IntUnLockTimerList();
-      /* bit was not set */
-      /* FIXME: set the last error */
-      return FALSE;
-    }
-    RtlClearBits(&WindowLessTimersBitMap, uIDEvent - 1, 1);
-  }
-  else
-  {
-    WindowObject = IntGetWindowObject(hWnd);
-    if(!WindowObject)
-    {
-      IntUnLockTimerList(); 
-      SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE);
-      return FALSE;
-    }
-    if(WindowObject->OwnerThread != PsGetCurrentThread())
+  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))
     {
-      IntUnLockTimerList();
-      IntReleaseWindowObject(WindowObject);
-      SetLastWin32Error(ERROR_ACCESS_DENIED);
+      DPRINT1("Unable to locate timer in message queue\n");
+      SetLastWin32Error(ERROR_INVALID_PARAMETER);
       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)
+  /* window-less timer? */
+  if ((Wnd == NULL) && ! SystemTimer)
     {
-       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);
+      /* Release the id */
+      IntLockWindowlessTimerBitmap();
 
-    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;
-      }
+      ASSERT(RtlAreBitsSet(&WindowLessTimersBitMap, IDEvent - 1, 1));
+      RtlClearBits(&WindowLessTimersBitMap, IDEvent - 1, 1);
 
+      IntUnlockWindowlessTimerBitmap();
     }
-    
-    
-    //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);
-  }
+  
+  return TRUE;
 }
 
 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;
 }
 
 
index b615e06..2912973 100644 (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)