Merge pull request #2274 from esdrubal/udpclientreceive
[mono.git] / mcs / class / System / System.Net.Sockets / SafeSocketHandle.cs
index ceba17bcc0138817bd0c1662f4d6d7646d0002a6..e0f8569969f680c662d71a55e2019a5650015656 100644 (file)
@@ -16,7 +16,11 @@ namespace System.Net.Sockets {
        sealed class SafeSocketHandle : SafeHandleZeroOrMinusOneIsInvalid {
 
                List<Thread> blocking_threads;
+
+               const int SOCKET_CLOSED = 10004;
+
                const int ABORT_RETRIES = 10;
+               static bool THROW_ON_ABORT_RETRIES = Environment.GetEnvironmentVariable("MONO_TESTS_IN_PROGRESS") == "yes";
 
                public SafeSocketHandle (IntPtr preexistingHandle, bool ownsHandle) : base (ownsHandle)
                {
@@ -31,21 +35,35 @@ namespace System.Net.Sockets {
                protected override bool ReleaseHandle ()
                {
                        int error = 0;
-                       
+
                        Socket.Blocking_internal (handle, false, out error);
-       
+#if MOBILE_STATIC
+                       /* It's only for platforms that do not have working syscall abort mechanism, like WatchOS and TvOS */
+                       Socket.Shutdown_internal (handle, SocketShutdown.Both, out error);
+#endif
+
                        if (blocking_threads != null) {
                                int abort_attempts = 0;
                                while (blocking_threads.Count > 0) {
                                        if (abort_attempts++ >= ABORT_RETRIES) {
-#if DEBUG
-                                               throw new Exception ("Could not abort registered blocking threads before closing socket.");
-#else
+                                               if (THROW_ON_ABORT_RETRIES)
+                                                       throw new Exception ("Could not abort registered blocking threads before closing socket.");
+
                                                // Attempts to close the socket safely failed.
                                                // We give up, and close the socket with pending blocking system calls.
                                                // This should not occur, nonetheless if it does this avoids an endless loop.
                                                break;
-#endif
+                                       }
+
+                                       /*
+                                        * This method can be called by the DangerousRelease inside RegisterForBlockingSyscall
+                                        * When this happens blocking_threads contains the current thread.
+                                        * We can safely close the socket and throw SocketException in RegisterForBlockingSyscall
+                                        * before the blocking system call.
+                                        */
+                                       lock (blocking_threads) {
+                                               if (blocking_threads.Count == 1 && blocking_threads[0] == Thread.CurrentThread)
+                                                       break;
                                        }
 
                                        AbortRegisteredThreads ();
@@ -74,6 +92,10 @@ namespace System.Net.Sockets {
                                }
                                if (release)
                                        DangerousRelease ();
+
+                               // Handle can be closed by DangerousRelease
+                               if (IsClosed)
+                                       throw new SocketException (SOCKET_CLOSED);
                        }
                }