Explorar o código

Merge pull request #2113 from ludovic-henry/coop-socket-abort

[socket] Make blocking syscall abortable on signal-less platforms
Rodrigo Kumpera %!s(int64=10) %!d(string=hai) anos
pai
achega
714ace0119

+ 1 - 0
mcs/class/System/System.Net.Sockets/SafeSocketHandle.cs

@@ -37,6 +37,7 @@ namespace System.Net.Sockets {
 			int error = 0;
 
 			Socket.Blocking_internal (handle, false, out error);
+			Socket.Shutdown_internal (handle, SocketShutdown.Both, out error);
 
 			if (blocking_threads != null) {
 				int abort_attempts = 0;

+ 25 - 28
mcs/class/System/System.Net.Sockets/Socket.cs

@@ -201,11 +201,9 @@ namespace System.Net.Sockets
 			this.address_family = addressFamily;
 			this.socket_type = socketType;
 			this.protocol_type = protocolType;
-			
-			int error;
-			var handle = Socket_internal (addressFamily, socketType, protocolType, out error);
 
-			this.safe_handle = new SafeSocketHandle (handle, true);
+			int error;
+			this.safe_handle = new SafeSocketHandle (Socket_internal (addressFamily, socketType, protocolType, out error), true);
 
 			if (error != 0)
 				throw new SocketException (error);
@@ -953,7 +951,7 @@ namespace System.Net.Sockets
 
 			InitSocketAsyncEventArgs (e, AcceptAsyncCallback, e, SocketOperation.Accept);
 
-			QueueIOSelectorJob (readQ, e.socket_async_result.handle, new IOSelectorJob (IOOperation.Read, BeginAcceptCallback, e.socket_async_result));
+			QueueIOSelectorJob (readQ, e.socket_async_result.Handle, new IOSelectorJob (IOOperation.Read, BeginAcceptCallback, e.socket_async_result));
 
 			return true;
 		}
@@ -986,7 +984,7 @@ namespace System.Net.Sockets
 
 			SocketAsyncResult sockares = new SocketAsyncResult (this, callback, state, SocketOperation.Accept);
 
-			QueueIOSelectorJob (readQ, sockares.handle, new IOSelectorJob (IOOperation.Read, BeginAcceptCallback, sockares));
+			QueueIOSelectorJob (readQ, sockares.Handle, new IOSelectorJob (IOOperation.Read, BeginAcceptCallback, sockares));
 
 			return sockares;
 		}
@@ -1019,7 +1017,7 @@ namespace System.Net.Sockets
 				SockFlags = SocketFlags.None,
 			};
 
-			QueueIOSelectorJob (readQ, sockares.handle, new IOSelectorJob (IOOperation.Read, BeginAcceptReceiveCallback, sockares));
+			QueueIOSelectorJob (readQ, sockares.Handle, new IOSelectorJob (IOOperation.Read, BeginAcceptReceiveCallback, sockares));
 
 			return sockares;
 		}
@@ -1054,7 +1052,7 @@ namespace System.Net.Sockets
 				AcceptSocket = acceptSocket,
 			};
 
-			QueueIOSelectorJob (readQ, sockares.handle, new IOSelectorJob (IOOperation.Read, BeginAcceptReceiveCallback, sockares));
+			QueueIOSelectorJob (readQ, sockares.Handle, new IOSelectorJob (IOOperation.Read, BeginAcceptReceiveCallback, sockares));
 
 			return sockares;
 		}
@@ -1420,8 +1418,7 @@ namespace System.Net.Sockets
 				// an error. Better to just close the socket and move on.
 				connect_in_progress = false;
 				safe_handle.Dispose ();
-				var handle = Socket_internal (address_family, socket_type, protocol_type, out error);
-				safe_handle = new SafeSocketHandle (handle, true);
+				safe_handle = new SafeSocketHandle (Socket_internal (address_family, socket_type, protocol_type, out error), true);
 				if (error != 0)
 					throw new SocketException (error);
 			}
@@ -1454,7 +1451,7 @@ namespace System.Net.Sockets
 			is_bound = false;
 			connect_in_progress = true;
 
-			IOSelector.Add (sockares.handle, new IOSelectorJob (IOOperation.Write, BeginConnectCallback, sockares));
+			IOSelector.Add (sockares.Handle, new IOSelectorJob (IOOperation.Write, BeginConnectCallback, sockares));
 
 			return sockares;
 		}
@@ -1649,7 +1646,7 @@ namespace System.Net.Sockets
 
 			InitSocketAsyncEventArgs (e, DisconnectAsyncCallback, e, SocketOperation.Disconnect);
 
-			IOSelector.Add (e.socket_async_result.handle, new IOSelectorJob (IOOperation.Write, BeginDisconnectCallback, e.socket_async_result));
+			IOSelector.Add (e.socket_async_result.Handle, new IOSelectorJob (IOOperation.Write, BeginDisconnectCallback, e.socket_async_result));
 
 			return true;
 		}
@@ -1679,7 +1676,7 @@ namespace System.Net.Sockets
 				ReuseSocket = reuseSocket,
 			};
 
-			IOSelector.Add (sockares.handle, new IOSelectorJob (IOOperation.Write, BeginDisconnectCallback, sockares));
+			IOSelector.Add (sockares.Handle, new IOSelectorJob (IOOperation.Write, BeginDisconnectCallback, sockares));
 
 			return sockares;
 		}
@@ -1879,7 +1876,7 @@ namespace System.Net.Sockets
 
 				e.socket_async_result.Buffers = e.BufferList;
 
-				QueueIOSelectorJob (readQ, e.socket_async_result.handle, new IOSelectorJob (IOOperation.Read, BeginReceiveGenericCallback, e.socket_async_result));
+				QueueIOSelectorJob (readQ, e.socket_async_result.Handle, new IOSelectorJob (IOOperation.Read, BeginReceiveGenericCallback, e.socket_async_result));
 			} else {
 				InitSocketAsyncEventArgs (e, ReceiveAsyncCallback, e, SocketOperation.Receive);
 
@@ -1887,7 +1884,7 @@ namespace System.Net.Sockets
 				e.socket_async_result.Offset = e.Offset;
 				e.socket_async_result.Size = e.Count;
 
-				QueueIOSelectorJob (readQ, e.socket_async_result.handle, new IOSelectorJob (IOOperation.Read, BeginReceiveCallback, e.socket_async_result));
+				QueueIOSelectorJob (readQ, e.socket_async_result.Handle, new IOSelectorJob (IOOperation.Read, BeginReceiveCallback, e.socket_async_result));
 			}
 
 			return true;
@@ -1923,7 +1920,7 @@ namespace System.Net.Sockets
 				SockFlags = socket_flags,
 			};
 
-			QueueIOSelectorJob (readQ, sockares.handle, new IOSelectorJob (IOOperation.Read, BeginReceiveCallback, sockares));
+			QueueIOSelectorJob (readQ, sockares.Handle, new IOSelectorJob (IOOperation.Read, BeginReceiveCallback, sockares));
 
 			return sockares;
 		}
@@ -1964,7 +1961,7 @@ namespace System.Net.Sockets
 				SockFlags = socketFlags,
 			};
 
-			QueueIOSelectorJob (readQ, sockares.handle, new IOSelectorJob (IOOperation.Read, BeginReceiveGenericCallback, sockares));
+			QueueIOSelectorJob (readQ, sockares.Handle, new IOSelectorJob (IOOperation.Read, BeginReceiveGenericCallback, sockares));
 
 			return sockares;
 		}
@@ -2124,7 +2121,7 @@ namespace System.Net.Sockets
 			e.socket_async_result.EndPoint = e.RemoteEndPoint;
 			e.socket_async_result.SockFlags = e.SocketFlags;
 
-			QueueIOSelectorJob (readQ, e.socket_async_result.handle, new IOSelectorJob (IOOperation.Read, BeginReceiveFromCallback, e.socket_async_result));
+			QueueIOSelectorJob (readQ, e.socket_async_result.Handle, new IOSelectorJob (IOOperation.Read, BeginReceiveFromCallback, e.socket_async_result));
 
 			return true;
 		}
@@ -2163,7 +2160,7 @@ namespace System.Net.Sockets
 				EndPoint = remote_end,
 			};
 
-			QueueIOSelectorJob (readQ, sockares.handle, new IOSelectorJob (IOOperation.Read, BeginReceiveFromCallback, sockares));
+			QueueIOSelectorJob (readQ, sockares.Handle, new IOSelectorJob (IOOperation.Read, BeginReceiveFromCallback, sockares));
 
 			return sockares;
 		}
@@ -2483,7 +2480,7 @@ namespace System.Net.Sockets
 
 				e.socket_async_result.Buffers = e.BufferList;
 
-				QueueIOSelectorJob (writeQ, e.socket_async_result.handle, new IOSelectorJob (IOOperation.Write, BeginSendGenericCallback, e.socket_async_result));
+				QueueIOSelectorJob (writeQ, e.socket_async_result.Handle, new IOSelectorJob (IOOperation.Write, BeginSendGenericCallback, e.socket_async_result));
 			} else {
 				InitSocketAsyncEventArgs (e, SendAsyncCallback, e, SocketOperation.Send);
 
@@ -2491,7 +2488,7 @@ namespace System.Net.Sockets
 				e.socket_async_result.Offset = e.Offset;
 				e.socket_async_result.Size = e.Count;
 
-				QueueIOSelectorJob (writeQ, e.socket_async_result.handle, new IOSelectorJob (IOOperation.Write, s => BeginSendCallback ((SocketAsyncResult) s, 0), e.socket_async_result));
+				QueueIOSelectorJob (writeQ, e.socket_async_result.Handle, new IOSelectorJob (IOOperation.Write, s => BeginSendCallback ((SocketAsyncResult) s, 0), e.socket_async_result));
 			}
 
 			return true;
@@ -2541,7 +2538,7 @@ namespace System.Net.Sockets
 				SockFlags = socket_flags,
 			};
 
-			QueueIOSelectorJob (writeQ, sockares.handle, new IOSelectorJob (IOOperation.Write, s => BeginSendCallback ((SocketAsyncResult) s, 0), sockares));
+			QueueIOSelectorJob (writeQ, sockares.Handle, new IOSelectorJob (IOOperation.Write, s => BeginSendCallback ((SocketAsyncResult) s, 0), sockares));
 
 			return sockares;
 		}
@@ -2568,7 +2565,7 @@ namespace System.Net.Sockets
 				}
 
 				if (sockares.Size > 0) {
-					IOSelector.Add (sockares.handle, new IOSelectorJob (IOOperation.Write, s => BeginSendCallback ((SocketAsyncResult) s, sent_so_far), sockares));
+					IOSelector.Add (sockares.Handle, new IOSelectorJob (IOOperation.Write, s => BeginSendCallback ((SocketAsyncResult) s, sent_so_far), sockares));
 					return; // Have to finish writing everything. See bug #74475.
 				}
 
@@ -2592,7 +2589,7 @@ namespace System.Net.Sockets
 				SockFlags = socketFlags,
 			};
 
-			QueueIOSelectorJob (writeQ, sockares.handle, new IOSelectorJob (IOOperation.Write, BeginSendGenericCallback, sockares));
+			QueueIOSelectorJob (writeQ, sockares.Handle, new IOSelectorJob (IOOperation.Write, BeginSendGenericCallback, sockares));
 
 			return sockares;
 		}
@@ -2738,7 +2735,7 @@ namespace System.Net.Sockets
 			e.socket_async_result.SockFlags = e.SocketFlags;
 			e.socket_async_result.EndPoint = e.RemoteEndPoint;
 
-			QueueIOSelectorJob (writeQ, e.socket_async_result.handle, new IOSelectorJob (IOOperation.Write, s => BeginSendToCallback ((SocketAsyncResult) s, 0), e.socket_async_result));
+			QueueIOSelectorJob (writeQ, e.socket_async_result.Handle, new IOSelectorJob (IOOperation.Write, s => BeginSendToCallback ((SocketAsyncResult) s, 0), e.socket_async_result));
 
 			return true;
 		}
@@ -2774,7 +2771,7 @@ namespace System.Net.Sockets
 				EndPoint = remote_end,
 			};
 
-			QueueIOSelectorJob (writeQ, sockares.handle, new IOSelectorJob (IOOperation.Write, s => BeginSendToCallback ((SocketAsyncResult) s, 0), sockares));
+			QueueIOSelectorJob (writeQ, sockares.Handle, new IOSelectorJob (IOOperation.Write, s => BeginSendToCallback ((SocketAsyncResult) s, 0), sockares));
 
 			return sockares;
 		}
@@ -2792,7 +2789,7 @@ namespace System.Net.Sockets
 				}
 
 				if (sockares.Size > 0) {
-					IOSelector.Add (sockares.handle, new IOSelectorJob (IOOperation.Write, s => BeginSendToCallback ((SocketAsyncResult) s, sent_so_far), sockares));
+					IOSelector.Add (sockares.Handle, new IOSelectorJob (IOOperation.Write, s => BeginSendToCallback ((SocketAsyncResult) s, sent_so_far), sockares));
 					return; // Have to finish writing everything. See bug #74475.
 				}
 
@@ -3286,7 +3283,7 @@ namespace System.Net.Sockets
 		}
 
 		[MethodImplAttribute (MethodImplOptions.InternalCall)]
-		extern static void Shutdown_internal (IntPtr socket, SocketShutdown how, out int error);
+		internal extern static void Shutdown_internal (IntPtr socket, SocketShutdown how, out int error);
 
 #endregion
 

+ 4 - 3
mcs/class/System/System.Net.Sockets/SocketAsyncResult.cs

@@ -38,7 +38,6 @@ namespace System.Net.Sockets
 	internal sealed class SocketAsyncResult: IOAsyncResult
 	{
 		public Socket socket;
-		public IntPtr handle;
 		public SocketOperation operation;
 
 		Exception DelayedException;
@@ -62,6 +61,10 @@ namespace System.Net.Sockets
 
 		public int EndCalled;
 
+		public IntPtr Handle {
+			get { return socket != null ? socket.Handle : IntPtr.Zero; }
+		}
+
 		/* Used by SocketAsyncEventArgs */
 		public SocketAsyncResult ()
 			: base ()
@@ -73,7 +76,6 @@ namespace System.Net.Sockets
 			base.Init (callback, state);
 
 			this.socket = socket;
-			this.handle = socket != null ? socket.Handle : IntPtr.Zero;
 			this.operation = operation;
 
 			DelayedException = null;
@@ -102,7 +104,6 @@ namespace System.Net.Sockets
 			: base (callback, state)
 		{
 			this.socket = socket;
-			this.handle = socket != null ? socket.Handle : IntPtr.Zero;
 			this.operation = operation;
 		}
 

+ 22 - 15
mcs/class/System/Test/System.Net.Sockets/UdpClientTest.cs

@@ -9,6 +9,7 @@ using System;
 using System.Net;
 using System.Net.Sockets;
 using System.Threading;
+using System.Threading.Tasks;
 
 using NUnit.Framework;
 
@@ -830,31 +831,37 @@ namespace MonoTests.System.Net.Sockets {
 		{
 			UdpClient client = null;
 			var rnd = new Random ();
-			for (int i = 0; i < 5; i++) {
+			for (int i = 0, max = 5; i < max; i++) {
 				int port = rnd.Next (1025, 65534);
 				try {
 					client = new UdpClient (port);
 					break;
 				} catch (Exception) {
-					if (i == 5)
+					if (i == max - 1)
 						throw;
 				}
 			}
 
-			new Thread(delegate() {
-				Thread.Sleep(2000);
-				client.Close();
-				}).Start();
-
+			ManualResetEvent ready = new ManualResetEvent (false);
 			bool got_exc = false;
-			IPEndPoint ep = new IPEndPoint (IPAddress.Any, 0);
-			try {
-				client.Receive(ref ep);
-			} catch (SocketException) {
-				got_exc = true;
-			} finally {
-				client.Close ();
-			}
+
+			Task receive_task = Task.Factory.StartNew (() => {
+				IPEndPoint ep = new IPEndPoint (IPAddress.Any, 0);
+				try {
+					ready.Set ();
+					client.Receive(ref ep);
+				} catch (SocketException) {
+					got_exc = true;
+				} finally {
+					client.Close ();
+				}
+			});
+
+			ready.WaitOne (2000);
+			Thread.Sleep (20);
+			client.Close();
+
+			Assert.IsTrue (receive_task.Wait (1000));
 			Assert.IsTrue (got_exc);
 		}
 

+ 26 - 15
mcs/class/System/Test/System.Net/SocketResponder.cs

@@ -43,13 +43,18 @@ namespace MonoTests.System.Net
 		private TcpListener tcpListener;
 		private readonly IPEndPoint _localEndPoint;
 		private Thread listenThread;
+		private Socket listenSocket;
 		private SocketRequestHandler _requestHandler;
-		private bool _stopped = true;
+		private int _state = 0;
 		private readonly object _syncRoot = new object ();
 
 		private const int SOCKET_CLOSED = 10004;
 		private const int SOCKET_INVALID_ARGS = 10022;
 
+		private const int STATE_UNINITIALIZED = 0;
+		private const int STATE_RUNNING = 1;
+		private const int STATE_STOPPED = 2;
+
 		public SocketResponder (IPEndPoint localEP, SocketRequestHandler requestHandler)
 		{
 			_localEndPoint = localEP;
@@ -71,7 +76,7 @@ namespace MonoTests.System.Net
 			get
 			{
 				lock (_syncRoot) {
-					return _stopped;
+					return _state != STATE_RUNNING;
 				}
 			}
 		}
@@ -79,9 +84,9 @@ namespace MonoTests.System.Net
 		public void Start ()
 		{
 			lock (_syncRoot) {
-				if (!_stopped)
-					return;
-				_stopped = false;
+				if (_state != STATE_UNINITIALIZED)
+					throw new InvalidOperationException ("cannot restart SocketResponder");
+				_state = STATE_RUNNING;
 				tcpListener = new TcpListener (LocalEndPoint);
 				tcpListener.Start ();
 				listenThread = new Thread (new ThreadStart (Listen));
@@ -92,12 +97,14 @@ namespace MonoTests.System.Net
 		public void Stop ()
 		{
 			lock (_syncRoot) {
-				if (_stopped)
+				if (_state != STATE_RUNNING)
 					return;
-				_stopped = true;
+				_state = STATE_STOPPED;
 				if (tcpListener != null) {
 					tcpListener.Stop ();
 					tcpListener = null;
+					if (listenSocket != null)
+						listenSocket.Close ();
 					listenThread.Abort ();
 					listenThread.Join ();
 					listenThread = null;
@@ -108,20 +115,24 @@ namespace MonoTests.System.Net
 
 		private void Listen ()
 		{
-			while (!_stopped) {
-				Socket socket = null;
+			while (_state == STATE_RUNNING) {
+				listenSocket = null;
 				try {
-					socket = tcpListener.AcceptSocket ();
-					socket.Send (_requestHandler (socket));
+					listenSocket = tcpListener.AcceptSocket ();
+					listenSocket.Send (_requestHandler (listenSocket));
 					try {
-						socket.Shutdown (SocketShutdown.Receive);
-						socket.Shutdown (SocketShutdown.Send);
+						listenSocket.Shutdown (SocketShutdown.Receive);
+						listenSocket.Shutdown (SocketShutdown.Send);
 					} catch {
 					}
 				} catch (SocketException ex) {
 					// ignore interruption of blocking call
 					if (ex.ErrorCode != SOCKET_CLOSED && ex.ErrorCode != SOCKET_INVALID_ARGS)
 						throw;
+				} catch (ObjectDisposedException ex) {
+					Console.WriteLine (ex);
+					if (_state != STATE_STOPPED)
+						throw;
 #if MOBILE
 				} catch (InvalidOperationException ex) {
 					// This breaks some tests running on Android. The problem is that the stack trace
@@ -132,8 +143,8 @@ namespace MonoTests.System.Net
 #endif
 				} finally {
 					Thread.Sleep (500);
-					if (socket != null)
-						socket.Close ();
+					if (listenSocket != null)
+						listenSocket.Close ();
 				}
 			}
 		}

+ 1 - 1
mono/utils/mono-threads.c

@@ -1018,7 +1018,7 @@ mono_thread_info_abort_socket_syscall_for_close (MonoNativeThreadId tid)
 	if (!info)
 		return;
 
-	if (mono_thread_info_run_state (info) > STATE_RUNNING) {
+	if (mono_thread_info_run_state (info) == STATE_DETACHED) {
 		mono_hazard_pointer_clear (hp, 1);
 		return;
 	}