Timers are now handled in a second thread and post messages into the main threads...
authorJackson Harper <jackson@novell.com>
Tue, 14 Sep 2004 00:13:29 +0000 (00:13 -0000)
committerJackson Harper <jackson@novell.com>
Tue, 14 Sep 2004 00:13:29 +0000 (00:13 -0000)
svn path=/trunk/mcs/; revision=33838

mcs/class/Managed.Windows.Forms/System.Windows.Forms/Timer.cs
mcs/class/Managed.Windows.Forms/System.Windows.Forms/X11Structs.cs
mcs/class/Managed.Windows.Forms/System.Windows.Forms/XplatUIX11.cs

index 34644171eeb01ae9d956e7ee26368ea6f7b747d4..e35df4b03c4137c4958d6b866b95d68863078814 100644 (file)
@@ -36,6 +36,8 @@ namespace System.Windows.Forms {
                private int interval = 100;
                private DateTime expires;
 
+               internal static readonly int Minimum = 15;
+
                public Timer ()
                {
                        enabled = false;
@@ -69,7 +71,7 @@ namespace System.Windows.Forms {
                        set {
                                interval = value;
                                // Use AddTicks so we get some rounding
-                               expires = DateTime.Now.AddMilliseconds (interval);
+                               expires = DateTime.Now.AddMilliseconds (interval > Minimum ? interval : Minimum);
                        }
                }
 
@@ -96,23 +98,22 @@ namespace System.Windows.Forms {
                        return base.ToString () + ", Interval: " + Interval;
                }
 
-               internal void Update ()
+               internal void Update (DateTime update)
                {
-                       expires = DateTime.Now.AddMilliseconds (interval);
+                       expires = update.AddMilliseconds (interval > Minimum ? interval : Minimum);
                }
 
                internal void FireTick ()
                {
                        OnTick (EventArgs.Empty);
+                       Update (DateTime.Now);
                }
 
 
                protected virtual void OnTick (EventArgs e)
                {
-                       lock (this) {
-                               if (Tick != null)
-                                       Tick (this, e);
-                       }
+                       if (Tick != null)
+                               Tick (this, e);
                }
 
                protected override void Dispose (bool disposing)
@@ -120,7 +121,10 @@ namespace System.Windows.Forms {
                        Enabled = false;
                }
 
-               private void TickHandler (object sender, EventArgs e)
+               private bool has_last_fire = false;
+               private DateTime last_fire;
+
+               internal void TickHandler (object sender, EventArgs e)
                {
                        OnTick (e);
                }
index 548ac3feb7dc2f8e686588cf85e7402956b53964..bc7de0fb44ce5e6471998ff7c7a53a12890c5c76 100644 (file)
 //     Peter Bartok    pbartok@novell.com
 //
 //
-// $Revision: 1.10 $
+// $Revision: 1.11 $
 // $Modtime: $
 // $Log: X11Structs.cs,v $
+// Revision 1.11  2004/09/14 00:13:29  jackson
+// Timers are now handled in a second thread and post messages into the main threads message queue. This makes timing much more consistent. Both win2K and XP have a minimum timer value of 15 milliseconds, so we now do this too.
+//
 // Revision 1.10  2004/09/13 21:18:32  pbartok
 // - Added Z-Ordering methods
 //
@@ -510,6 +513,12 @@ namespace System.Windows.Forms {
                internal byte           minor_code;
        }
 
+       [StructLayout(LayoutKind.Sequential)]
+       internal struct XTimerNotifyEvent {
+               internal XEventName     type;
+               internal EventHandler   handler;
+       }
+
        [StructLayout(LayoutKind.Sequential)]
        internal struct XEventPad {
                internal int pad0;
@@ -572,6 +581,7 @@ namespace System.Windows.Forms {
                [ FieldOffset(0) ] internal XMappingEvent MappingEvent;
                [ FieldOffset(0) ] internal XErrorEvent ErrorEvent;
                [ FieldOffset(0) ] internal XKeymapEvent KeymapEvent;
+               [ FieldOffset(0) ] internal XTimerNotifyEvent TimerNotifyEvent;
 
                //[MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst=24)]
                //[ FieldOffset(0) ] internal int[] pad;
@@ -652,9 +662,11 @@ namespace System.Windows.Forms {
                SelectionRequest        = 30,
                SelectionNotify         = 31,
                ColormapNotify          = 32,
-               ClientMessage           = 33,
-               MappingNotify           = 34,
-               LASTEvent               = 35
+               ClientMessage           = 33,
+               MappingNotify           = 34,
+               TimerNotify             = 100,
+
+               LASTEvent
        }
 
        internal enum XWindowAttribute {
index 53052b3858cb2f35b787133de33dc1bef8f69409..28a740631c78e07269508414e96f5ade101806a0 100644 (file)
@@ -59,7 +59,10 @@ namespace System.Windows.Forms {
 
                private static Hashtable        handle_data;
                private Queue message_queue;
+
                private ArrayList timer_list;
+               private Thread timer_thread;
+               private AutoResetEvent timer_wait;
 
                private static readonly EventMask  SelectInputMask = EventMask.ButtonPressMask | 
                                EventMask.ButtonReleaseMask | 
@@ -111,7 +114,8 @@ namespace System.Windows.Forms {
                        ref_count=0;
 
                        message_queue = new Queue ();
-                       timer_list = new ArrayList ();
+                       ArrayList timers = new ArrayList ();
+                       timer_list = ArrayList.Synchronized (timers);
 
                        // Now regular initialization
                        SetDisplay(XOpenDisplay(IntPtr.Zero));
@@ -553,22 +557,20 @@ namespace System.Windows.Forms {
                        return (IntPtr)result;
                }
 
+                // The message_queue should be locked when this is called
                private void UpdateMessageQueue ()
                {
-                       lock (timer_list) {
-                               for (int i = 0; i < timer_list.Count; i++) {
-                                       Timer timer = (Timer) timer_list [i];
-                                       if (timer.Enabled && timer.Expires <= DateTime.Now) {
-                                               timer.FireTick ();
-                                               timer.Update ();
-                                       }
-                               }
+                       int pending;
+                       lock (this) {
+                               pending = XPending (DisplayHandle);
                        }
-
-                       while (XPending (DisplayHandle) > 0) {
+                       
+                       while (pending > 0) {
                                XEvent xevent = new XEvent ();
 
-                               XNextEvent (DisplayHandle, ref xevent);
+                               lock (this) {
+                                       XNextEvent (DisplayHandle, ref xevent);
+                               }
 
                                switch (xevent.type) {
                                case XEventName.Expose:
@@ -590,11 +592,16 @@ namespace System.Windows.Forms {
                                        message_queue.Enqueue (xevent);
                                        break;
                                }
+
+                               lock (this) {
+                                       pending = XPending (DisplayHandle);
+                               }
                        }
+
                }
 
                internal override bool GetMessage(ref MSG msg, IntPtr hWnd, int wFilterMin, int wFilterMax) {
-                       XEvent  xevent = new XEvent();
+                       XEvent  xevent;
 
                        lock (message_queue) {
                                if (message_queue.Count > 0) {
@@ -604,6 +611,7 @@ namespace System.Windows.Forms {
                                        if (message_queue.Count > 0) {
                                                xevent = (XEvent) message_queue.Dequeue ();
                                        } else {
+                                               msg.hwnd= IntPtr.Zero;
                                                msg.message = Msg.WM_ENTERIDLE;
                                                return true;
                                        }
@@ -770,7 +778,13 @@ namespace System.Windows.Forms {
                                        }
                                        break;
                                }
-                               default: {
+
+                        case XEventName.TimerNotify: {
+                                xevent.TimerNotifyEvent.handler (this, EventArgs.Empty);
+                                break;
+                        }
+                                
+                        default: {
                                        msg.message = Msg.WM_NULL;
                                        break;
                                }
@@ -991,18 +1005,61 @@ namespace System.Windows.Forms {
                        }
                }
 
+               internal void TimerProc ()
+               {
+                       while (true) {
+                               int next_fire = -1;
+
+                               ArrayList fire_list = new ArrayList ();
+                               DateTime now = DateTime.Now;
+                               for (int i = 0; i < timer_list.Count; i++) {
+                                       Timer timer = (Timer) timer_list [i];
+                                               
+                                       if (timer.Enabled && timer.Expires <= now) {
+                                               XEvent xevent = new XEvent ();
+                                               xevent.type = XEventName.TimerNotify;
+                                               xevent.AnyEvent.window = IntPtr.Zero;
+                                               xevent.TimerNotifyEvent.handler = new EventHandler (timer.TickHandler);
+
+                                               fire_list.Add (xevent);
+                                       }
+                                       int next = (int) (timer.Expires.AddMilliseconds (timer.Interval) - now).TotalMilliseconds;
+                                       if (i == 0 || next < next_fire)
+                                               next_fire = next;
+                               }
+
+                               // Everything is copied to a list and then to the queue so we
+                               // don't lock the queue while calculating all the times
+                               if (fire_list.Count > 0) {
+                                       lock (message_queue) {
+                                               foreach (object obj in fire_list) {
+                                                       message_queue.Enqueue (obj);
+                                               }
+                                       }
+                               }
+                               if (next_fire < Timer.Minimum)
+                                       next_fire = Timer.Minimum;
+                               timer_wait.WaitOne (next_fire, true);
+                       }
+               }
+
                internal override void SetTimer (Timer timer)
                {
-                       lock (timer_list) {
-                               timer_list.Add (timer);
+                       timer_list.Add (timer);
+
+                       if (timer_thread == null) {
+                               ThreadStart thread_start = new ThreadStart (TimerProc); 
+                               timer_thread = new Thread (thread_start);
+                               timer_thread.IsBackground = true;
+
+                               timer_wait = new AutoResetEvent (true);
+                               timer_thread.Start();
                        }
                }
 
                internal override void KillTimer (Timer timer)
                {
-                       lock (timer_list) {
-                               timer_list.Remove (timer);
-                       }
+                       timer_list.Remove (timer);
                }
 
                // Santa's little helper