Add upper limit for the number of pending timer interrupts.
authorJukka Heinonen <jhei@iki.fi>
Mon, 1 Dec 2003 22:37:15 +0000 (22:37 +0000)
committerAlexandre Julliard <julliard@winehq.org>
Mon, 1 Dec 2003 22:37:15 +0000 (22:37 +0000)
Check if timer has been stuck and issue an error to user.
Fix handling of timer period zero.

dlls/winedos/timer.c

index 1200b24903598cd0464cecd66e1744ece26a5de9..a177b25f5fb6d9b0addfec79c428109c075c583a 100644 (file)
@@ -35,11 +35,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(int);
  *        This should also make it possible to
  *        emulate really fast timers.
  * FIXME: Support special timer modes in addition to periodic mode.
- * FIXME: Make sure that there are only limited number
- *        of pending timer IRQ events queued. This makes sure that
- *        timer handling does not eat all available memory even
- *        if IRQ handling stops for some reason (suspended process?). 
- *        This is easy to do by using DOSRELAY parameter.
  * FIXME: Use timeSetEvent, NtSetEvent or timer thread for more precise
  *        timing.
  * FIXME: Move Win16 timer emulation code here.
@@ -48,6 +43,9 @@ WINE_DEFAULT_DEBUG_CHANNEL(int);
 /* The PC clocks ticks at 1193180 Hz. */
 #define TIMER_FREQ 1193180
 
+/* How many timer IRQs can be pending at any time. */
+#define TIMER_MAX_PENDING 20
+
 /* Unique system timer identifier. */
 static UINT_PTR TIMER_id = 0;
 
@@ -57,6 +55,22 @@ static DWORD TIMER_stamp = 0;
 /* Timer ticks between timer IRQs. */
 static UINT TIMER_ticks = 0;
 
+/* Number of pending timer IRQs. */
+static LONG TIMER_pending = 0;
+
+
+/*********************************************************************** 
+ *              TIMER_Relay
+ *
+ * Decrement the number of pending IRQs after IRQ handler has been 
+ * called. This function will be called even if application has its 
+ * own IRQ handler that does not jump to builtin IRQ handler.
+ */
+static void TIMER_Relay( CONTEXT86 *context, void *data )
+{
+    InterlockedDecrement( &TIMER_pending );
+}
+
 
 /***********************************************************************
  *              TIMER_TimerProc
@@ -66,8 +80,26 @@ static void CALLBACK TIMER_TimerProc( HWND     hwnd,
                                       UINT_PTR idEvent,
                                       DWORD    dwTime )
 {
-    TIMER_stamp = dwTime;
-    DOSVM_QueueEvent( 0, DOS_PRIORITY_REALTIME, NULL, NULL );
+    LONG pending = InterlockedIncrement( &TIMER_pending );
+
+    if (pending >= TIMER_MAX_PENDING)
+    {
+        DWORD delta = (dwTime >= TIMER_stamp) ? 
+            (dwTime - TIMER_stamp) : (0xffffffff - (TIMER_stamp - dwTime));
+
+        if (delta >= 60000)
+        {
+            ERR( "DOS timer has been stuck for 60 seconds...\n" );
+            TIMER_stamp = dwTime;
+        }
+
+        InterlockedDecrement( &TIMER_pending );
+    }
+    else
+    {
+        TIMER_stamp = dwTime;
+        DOSVM_QueueEvent( 0, DOS_PRIORITY_REALTIME, TIMER_Relay, NULL );
+    }
 }
 
 
@@ -119,6 +151,10 @@ UINT WINAPI DOSVM_GetTimer( void )
  */
 void WINAPI DOSVM_SetTimer( UINT ticks )
 {
+    /* PIT interprets zero as a maximum length delay. */
+    if (ticks == 0)
+        ticks = 0x10000;
+
     if (!DOSVM_IsWin16())
         MZ_RunInThread( TIMER_DoSetTimer, ticks );
 }