update for HEAD-2003091401
[reactos.git] / subsys / win32k / ntuser / timer.c
index 6d174a9..3b8e69d 100644 (file)
@@ -1,4 +1,22 @@
 /*
+ *  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
@@ -25,6 +43,8 @@
 
 /* GLOBALS *******************************************************************/
 
+//windows 2000 has room for 32768 handle-less timers
+#define NUM_HANDLE_LESS_TIMERS   1024
 
 static FAST_MUTEX     Mutex;
 static LIST_ENTRY     TimerListHead;
@@ -49,6 +69,7 @@ typedef struct _MSG_TIMER_ENTRY{
 
 
 //return true if the new timer became the first entry
+//must hold mutex while calling this
 BOOL
 FASTCALL
 InsertTimerAscendingOrder(PMSG_TIMER_ENTRY NewTimer)
@@ -81,6 +102,7 @@ InsertTimerAscendingOrder(PMSG_TIMER_ENTRY NewTimer)
 
 }
 
+//must hold mutex while calling this
 PMSG_TIMER_ENTRY
 FASTCALL
 RemoveTimer(HWND hWnd, UINT_PTR IDEvent, HANDLE ThreadID)
@@ -93,143 +115,185 @@ 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
@@ -245,8 +309,10 @@ NtUserSetSystemTimer(
 }
 
 
-static NTSTATUS STDCALL
-TimerThreadMain()
+static VOID STDCALL_FUNC
+TimerThreadMain(
+   PVOID StartContext
+   )
 {
    NTSTATUS Status;
    LARGE_INTEGER CurrentTime;
@@ -266,7 +332,7 @@ TimerThreadMain()
       if (!NT_SUCCESS(Status))
       {
          DPRINT1("Error waiting in TimerThreadMain\n");
-         KeBugCheck(0);
+         KEBUGCHECK(0);
       }
 
       ExAcquireFastMutex(&Mutex);
@@ -285,22 +351,18 @@ TimerThreadMain()
 
             /* 
              * 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);
@@ -318,6 +380,11 @@ TimerThreadMain()
          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);
 
@@ -327,20 +394,22 @@ TimerThreadMain()
 
 
 
-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); 
@@ -355,8 +424,4 @@ InitTimerImpl()
    return Status;
 }
 
-
-
-
-
-
+/* EOF */