Author: toshok
Date: 2006-11-16 00:45:58 -0500 (Thu, 16 Nov 2006)
New Revision: 67972

Modified:
   trunk/mcs/class/Managed.Windows.Forms/System.Windows.Forms/ChangeLog
   trunk/mcs/class/Managed.Windows.Forms/System.Windows.Forms/Hwnd.cs
   trunk/mcs/class/Managed.Windows.Forms/System.Windows.Forms/XEventQueue.cs
   trunk/mcs/class/Managed.Windows.Forms/System.Windows.Forms/XplatUIX11.cs
Log:
2006-11-16  Chris Toshok  <[EMAIL PROTECTED]>

        * XplatUIX11.cs: nasty, nasty, nasty changes required because of
        the broken multithreaded event handling we have in here.  File
        this entry under "Why we should move to the new X11 backend".

        Any thread can make it into UpdateMessageQueue, which gets events
        from the X socket - some of which could belong to hwnds being
        managed by a different thread.  We can also have multiple threads
        in UpdateMessageQueue at the same time, with each one reading from
        the X socket.  This leads to many problems, with the following
        solutions:

        We can't use hwnd.Queue.Enqueue anywhere in here and must use
        EnqueueLocked.

        The MotionNotify compression we do can't work across threads
        (without locking the entire queue, perhaps) since we call
        hwnd.Queue.Peek, so we just punt and don't compress motion events
        unless the owning thread is the one which got the X event.

        ConfigureNotify is another fun one, since it modifies the hwnd's
        bounds and then enqueues the event.  We add a lock to Hwnd which
        is held when setting configure_pending to true (and enqueuing the
        event).

        There is a race wrt the wake socket.  we need to make sure that
        only 1 thread is waiting on that socket, or else a thread could
        sleep waiting for data that never comes.  It's difficult (but not
        impossible) to make happen, because it seems to require something
        like the following:

            1. Thread 1 polls on wake_receive
        
            2. poll returns saying there's data to be read on
               wake_receive.
        
            3. Thread 2 polls on wake_receive and immediately returns
               saying there's data to be read.

            4. Thread 2 reads the wakeup byte from wake_receive

            5. Thread 1 attempts to read the wakeup byte from
               wake_receive.

            6. Thread 2 exits (due to a form closing, perhaps).

            7. Thread 1 blocks forever.
        
        Fun, eh?

        Fixing the Expose handling isn't done yet, and the races inherent
        in that piece of code are responsible for the drawing mistakes you
        see when generating expose events in a MT app (like NPlot).  This
        one is the likely to be the hardest to bandaid, and it doesn't
        appear to cause anything but drawing problems.  The other issues
        caused apps to exit or hang.

        * XEventQueue.cs: output some spew when Dequeue/Enqueue/Peek are
        called from a different thread than the one that should be calling
        these functions.

        * Hwnd.cs: add some locks to be used by the XplatUIX11 code.



Modified: trunk/mcs/class/Managed.Windows.Forms/System.Windows.Forms/ChangeLog
===================================================================
--- trunk/mcs/class/Managed.Windows.Forms/System.Windows.Forms/ChangeLog        
2006-11-16 01:54:29 UTC (rev 67971)
+++ trunk/mcs/class/Managed.Windows.Forms/System.Windows.Forms/ChangeLog        
2006-11-16 05:45:58 UTC (rev 67972)
@@ -1,3 +1,67 @@
+2006-11-16  Chris Toshok  <[EMAIL PROTECTED]>
+
+       * XplatUIX11.cs: nasty, nasty, nasty changes required because of
+       the broken multithreaded event handling we have in here.  File
+       this entry under "Why we should move to the new X11 backend".
+
+       Any thread can make it into UpdateMessageQueue, which gets events
+       from the X socket - some of which could belong to hwnds being
+       managed by a different thread.  We can also have multiple threads
+       in UpdateMessageQueue at the same time, with each one reading from
+       the X socket.  This leads to many problems, with the following
+       solutions:
+
+       We can't use hwnd.Queue.Enqueue anywhere in here and must use
+       EnqueueLocked.
+
+       The MotionNotify compression we do can't work across threads
+       (without locking the entire queue, perhaps) since we call
+       hwnd.Queue.Peek, so we just punt and don't compress motion events
+       unless the owning thread is the one which got the X event.
+
+       ConfigureNotify is another fun one, since it modifies the hwnd's
+       bounds and then enqueues the event.  We add a lock to Hwnd which
+       is held when setting configure_pending to true (and enqueuing the
+       event).
+
+       There is a race wrt the wake socket.  we need to make sure that
+       only 1 thread is waiting on that socket, or else a thread could
+       sleep waiting for data that never comes.  It's difficult (but not
+       impossible) to make happen, because it seems to require something
+       like the following:
+
+           1. Thread 1 polls on wake_receive
+       
+           2. poll returns saying there's data to be read on
+              wake_receive.
+       
+           3. Thread 2 polls on wake_receive and immediately returns
+              saying there's data to be read.
+
+           4. Thread 2 reads the wakeup byte from wake_receive
+
+           5. Thread 1 attempts to read the wakeup byte from
+              wake_receive.
+
+           6. Thread 2 exits (due to a form closing, perhaps).
+
+           7. Thread 1 blocks forever.
+       
+       Fun, eh?
+
+       Fixing the Expose handling isn't done yet, and the races inherent
+       in that piece of code are responsible for the drawing mistakes you
+       see when generating expose events in a MT app (like NPlot).  This
+       one is the likely to be the hardest to bandaid, and it doesn't
+       appear to cause anything but drawing problems.  The other issues
+       caused apps to exit or hang.
+
+       * XEventQueue.cs: output some spew when Dequeue/Enqueue/Peek are
+       called from a different thread than the one that should be calling
+       these functions.
+
+       * Hwnd.cs: add some locks to be used by the XplatUIX11 code.
+
 2006-11-15  Chris Toshok  <[EMAIL PROTECTED]>
 
        * Application.cs: null out the context's MainForm when we exit

Modified: trunk/mcs/class/Managed.Windows.Forms/System.Windows.Forms/Hwnd.cs
===================================================================
--- trunk/mcs/class/Managed.Windows.Forms/System.Windows.Forms/Hwnd.cs  
2006-11-16 01:54:29 UTC (rev 67971)
+++ trunk/mcs/class/Managed.Windows.Forms/System.Windows.Forms/Hwnd.cs  
2006-11-16 05:45:58 UTC (rev 67972)
@@ -79,6 +79,10 @@
                internal bool           no_activate;    // For Win32, popup 
windows will not steal focus
                #endregion      // Local Variables
 
+               // locks for some operations (used in XplatUIX11.cs)
+               internal object configure_lock = new object ();
+               internal object expose_lock = new object ();
+
                #region Constructors and destructors
                public Hwnd() {
                        x = 0;

Modified: 
trunk/mcs/class/Managed.Windows.Forms/System.Windows.Forms/XEventQueue.cs
===================================================================
--- trunk/mcs/class/Managed.Windows.Forms/System.Windows.Forms/XEventQueue.cs   
2006-11-16 01:54:29 UTC (rev 67971)
+++ trunk/mcs/class/Managed.Windows.Forms/System.Windows.Forms/XEventQueue.cs   
2006-11-16 05:45:58 UTC (rev 67972)
@@ -78,6 +78,11 @@
 
                public void Enqueue (XEvent xevent)
                {
+                       if (Thread.CurrentThread != thread) {
+                               Console.WriteLine ("Hwnd.Queue.Enqueue called 
from a different thread without locking.");
+                               Console.WriteLine (Environment.StackTrace);
+                       }
+
                        xqueue.Enqueue (xevent);
                }
 
@@ -90,6 +95,11 @@
 
                public XEvent Dequeue ()
                {
+                       if (Thread.CurrentThread != thread) {
+                               Console.WriteLine ("Hwnd.Queue.Dequeue called 
from a different thread without locking.");
+                               Console.WriteLine (Environment.StackTrace);
+                       }
+
                        if (xqueue.Count == 0) {
                                lock (lqueue) {
                                        return lqueue.Dequeue ();
@@ -100,6 +110,11 @@
 
                public XEvent Peek()
                {
+                       if (Thread.CurrentThread != thread) {
+                               Console.WriteLine ("Hwnd.Queue.Peek called from 
a different thread without locking.");
+                               Console.WriteLine (Environment.StackTrace);
+                       }
+
                        if (xqueue.Count == 0) {
                                lock (lqueue) {
                                        return lqueue.Peek ();

Modified: 
trunk/mcs/class/Managed.Windows.Forms/System.Windows.Forms/XplatUIX11.cs
===================================================================
--- trunk/mcs/class/Managed.Windows.Forms/System.Windows.Forms/XplatUIX11.cs    
2006-11-16 01:54:29 UTC (rev 67971)
+++ trunk/mcs/class/Managed.Windows.Forms/System.Windows.Forms/XplatUIX11.cs    
2006-11-16 05:45:58 UTC (rev 67972)
@@ -100,6 +100,8 @@
                private static Hashtable        MessageQueues;          // 
Holds our thread-specific XEventQueues
                #if __MonoCS__                                          //
                private static Pollfd[]         pollfds;                // For 
watching the X11 socket
+               private static bool wake_waiting;
+               private static object wake_waiting_lock = new object ();
                #endif                                                  //
                private static X11Keyboard      Keyboard;               //
                private static X11Dnd           Dnd;
@@ -1238,13 +1240,17 @@
                                                }
                                        }
                                }
+
+                               // XXX this sucks.  this isn't thread safe
                                hwnd.width = xevent.ConfigureEvent.width;
                                hwnd.height = xevent.ConfigureEvent.height;
                                hwnd.ClientRect = Rectangle.Empty;
 
-                               if (!hwnd.configure_pending) {
-                                       hwnd.Queue.Enqueue(xevent);
-                                       hwnd.configure_pending = true;
+                               lock (hwnd.configure_lock) {
+                                       if (!hwnd.configure_pending) {
+                                               hwnd.Queue.EnqueueLocked 
(xevent);
+                                               hwnd.configure_pending = true;
+                                       }
                                }
                        }
                        // We drop configure events for Client windows
@@ -1367,10 +1373,19 @@
 
                                if (timeout > 0) {
                                        #if __MonoCS__
-                                       Syscall.poll (pollfds, (uint) 
pollfds.Length, timeout);
+                                       int length = pollfds.Length - 1;
+                                       lock (wake_waiting_lock) {
+                                               if (wake_waiting == false) {
+                                                       length ++;
+                                                       wake_waiting = true;
+                                               }
+                                       }
+
+                                       Syscall.poll (pollfds, (uint)length, 
timeout);
                                        // Clean out buffer, so we're not 
busy-looping on the same data
                                        if (pollfds[1].revents != 0) {
                                                
wake_receive.Receive(network_buffer, 0, 1, SocketFlags.None);
+                                               wake_waiting = false;
                                        }
                                        #endif
                                        lock (XlibLock) {
@@ -1551,7 +1566,8 @@
                                case XEventName.MotionNotify: {
                                        XEvent peek;
 
-                                       if (hwnd.Queue.Count > 0) {
+                                       /* we can't do motion compression 
across threads, so just punt if we don't match up */
+                                       if (Thread.CurrentThread == 
hwnd.Queue.Thread && hwnd.Queue.Count > 0) {
                                                peek = hwnd.Queue.Peek();
                                                if (peek.AnyEvent.type == 
XEventName.MotionNotify) {
                                                        continue;
@@ -1571,7 +1587,7 @@
                                case XEventName.FocusOut:
                                case XEventName.ClientMessage:
                                case XEventName.ReparentNotify:
-                                       hwnd.Queue.Enqueue (xevent);
+                                       hwnd.Queue.EnqueueLocked (xevent);
                                        break;
 
                                case XEventName.ConfigureNotify:
@@ -3564,8 +3580,10 @@
                                                        
Console.WriteLine("GetMessage(): Window {0:X} ConfigureNotify x={1} y={2} 
width={3} height={4}", hwnd.client_window.ToInt32(), xevent.ConfigureEvent.x, 
xevent.ConfigureEvent.y, xevent.ConfigureEvent.width, 
xevent.ConfigureEvent.height);
                                                #endif
 //                                             if ((hwnd.x != 
xevent.ConfigureEvent.x) || (hwnd.y != xevent.ConfigureEvent.y) || (hwnd.width 
!= xevent.ConfigureEvent.width) || (hwnd.height != 
xevent.ConfigureEvent.height)) {
-                                                       SendMessage(msg.hwnd, 
Msg.WM_WINDOWPOSCHANGED, IntPtr.Zero, IntPtr.Zero);
-                                                       hwnd.configure_pending 
= false;
+                                                       lock 
(hwnd.configure_lock) {
+                                                               
SendMessage(msg.hwnd, Msg.WM_WINDOWPOSCHANGED, IntPtr.Zero, IntPtr.Zero);
+                                                               
hwnd.configure_pending = false;
+                                                       }
 
                                                        // We need to adjust 
our client window to track the resize of whole_window
                                                        if (hwnd.whole_window 
!= hwnd.client_window)
@@ -4121,7 +4139,7 @@
                        xevent.ClientMessageEvent.ptr3 = wparam;
                        xevent.ClientMessageEvent.ptr4 = lparam;
 
-                       hwnd.Queue.Enqueue (xevent);
+                       hwnd.Queue.EnqueueLocked (xevent);
 
                        return true;
                }
@@ -4510,7 +4528,7 @@
                                        xevent.MotionEvent.y_root = root_y;
                                        xevent.MotionEvent.state = mask;
 
-                                       child_hwnd.Queue.Enqueue (xevent);
+                                       child_hwnd.Queue.EnqueueLocked (xevent);
                                }
                        } else {
                                Hwnd    hwnd;

_______________________________________________
Mono-patches maillist  -  [email protected]
http://lists.ximian.com/mailman/listinfo/mono-patches

Reply via email to