With Socket:Dispose from using Thread.Abort to less agressive alternative.
authorRodrigo Kumpera <kumpera@gmail.com>
Wed, 27 Jun 2012 20:36:54 +0000 (17:36 -0300)
committerRodrigo Kumpera <kumpera@gmail.com>
Wed, 27 Jun 2012 22:59:56 +0000 (19:59 -0300)
* Socket_2_1.cs: When closing a socket we need to abort any
pending syscalls to that socket. For most syscalls the kernel
does it for us. But accept/connect on linux requires extra
love.

We previously used Thread.Abort to implement that, but this
is a pretty radical solution with very complicated ramifications.
So instead now we use a new icall cancel_blocking_socket_operation
that makes sure we only abort the syscall and do nothing else.

This reduces the trouble surface a lot given how tricky
Thread.Abort is.

mcs/class/System/System.Net.Sockets/Socket.cs
mcs/class/System/System.Net.Sockets/Socket_2_1.cs

index f45f6f8c0716791d2f14c6b0115fb833826ddf40..b45d52f2468ed158e7a7258f524a8bb59feabce9 100644 (file)
@@ -55,6 +55,7 @@ namespace System.Net.Sockets
        {
                private bool islistening;
                private bool useoverlappedIO;
+               private const int SOCKET_CLOSED = 10004;
 
                static void AddSockets (List<Socket> sockets, IList list, string name)
                {
@@ -543,21 +544,19 @@ namespace System.Net.Sockets
 
                        int error = 0;
                        IntPtr sock = (IntPtr) (-1);
-                       blocking_thread = Thread.CurrentThread;
                        try {
+                               blocking_thread = Thread.CurrentThread;
                                sock = Accept_internal(socket, out error, blocking);
-                       } catch (ThreadAbortException) {
-                               if (disposed) {
-                                       Thread.ResetAbort ();
-                                       error = (int) SocketError.Interrupted;
-                               }
                        } finally {
                                blocking_thread = null;
                        }
 
-                       if (error != 0)
-                               throw new SocketException (error);
-                       
+                       if (error != 0) {
+                               if (closed)
+                                       error = SOCKET_CLOSED;
+                               throw new SocketException(error);
+                       }
+
                        Socket accepted = new Socket(this.AddressFamily, this.SocketType,
                                this.ProtocolType, sock);
 
@@ -573,21 +572,19 @@ namespace System.Net.Sockets
                        
                        int error = 0;
                        IntPtr sock = (IntPtr)(-1);
-                       blocking_thread = Thread.CurrentThread;
                        
                        try {
+                               blocking_thread = Thread.CurrentThread;
                                sock = Accept_internal (socket, out error, blocking);
-                       } catch (ThreadAbortException) {
-                               if (disposed) {
-                                       Thread.ResetAbort ();
-                                       error = (int)SocketError.Interrupted;
-                               }
                        } finally {
                                blocking_thread = null;
                        }
                        
-                       if (error != 0)
+                       if (error != 0) {
+                               if (closed)
+                                       error = SOCKET_CLOSED;
                                throw new SocketException (error);
+                       }
                        
                        acceptSocket.address_family = this.AddressFamily;
                        acceptSocket.socket_type = this.SocketType;
index 0e93212554833ccdf33d3f6bbc24e7714d6e5505..d63f72434c90043af3ef1f06648c20cd66b52bb0 100644 (file)
@@ -1145,6 +1145,8 @@ namespace System.Net.Sockets {
                                        return; */
                        }
                }
+               [MethodImplAttribute(MethodImplOptions.InternalCall)]
+               static extern void cancel_blocking_socket_operation (Thread thread);
 
                protected virtual void Dispose (bool disposing)
                {
@@ -1160,10 +1162,9 @@ namespace System.Net.Sockets {
                                IntPtr x = socket;
                                socket = (IntPtr) (-1);
                                Thread th = blocking_thread;
-                               if (th != null) {
-                                       th.Abort ();
-                                       blocking_thread = null;
-                               }
+                               blocking_thread = null;
+                               if (th != null)
+                                       cancel_blocking_socket_operation (th);
 
                                if (was_connected)
                                        Linger (x);
@@ -1237,14 +1238,9 @@ namespace System.Net.Sockets {
 
                        int error = 0;
 
-                       blocking_thread = Thread.CurrentThread;
                        try {
+                               blocking_thread = Thread.CurrentThread;
                                Connect_internal (socket, serial, out error);
-                       } catch (ThreadAbortException) {
-                               if (disposed) {
-                                       Thread.ResetAbort ();
-                                       error = (int) SocketError.Interrupted;
-                               }
                        } finally {
                                blocking_thread = null;
                        }
@@ -1252,8 +1248,11 @@ namespace System.Net.Sockets {
                        if (error == 0 || error == 10035)
                                seed_endpoint = remoteEP; // Keep the ep around for non-blocking sockets
 
-                       if (error != 0)
+                       if (error != 0) {
+                               if (closed)
+                                       error = SOCKET_CLOSED;
                                throw new SocketException (error);
+                       }
 
 #if !MOONLIGHT
                        if (socket_type == SocketType.Dgram && (ep.Address.Equals (IPAddress.Any) || ep.Address.Equals (IPAddress.IPv6Any)))
@@ -1754,8 +1753,13 @@ namespace System.Net.Sockets {
 
                        // FIXME: this is canceling a synchronous connect, not an async one
                        Socket s = e.ConnectSocket;
-                       if ((s != null) && (s.blocking_thread != null))
-                               s.blocking_thread.Abort ();
+                       if (s != null) {
+                               Thread th = s.blocking_thread;
+                               blocking_thread = null;
+                               if (th != null)
+                                       cancel_blocking_socket_operation (th);
+                               }
+                       }
                }
 #endif
                [MethodImplAttribute (MethodImplOptions.InternalCall)]