X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fclass%2FSystem%2FSystem.Net.Sockets%2FSafeSocketHandle.cs;h=87f5e3aab3ed390bea59585f89103b17ea725c81;hb=256e3ee192da85cf7c09a3890c06f7bc448ac817;hp=9c9d8d751280f29fb9e163b4ffdc332051ff0863;hpb=811674bc6331c98d33134e2a37a7c7dd66402227;p=mono.git diff --git a/mcs/class/System/System.Net.Sockets/SafeSocketHandle.cs b/mcs/class/System/System.Net.Sockets/SafeSocketHandle.cs index 9c9d8d75128..87f5e3aab3e 100644 --- a/mcs/class/System/System.Net.Sockets/SafeSocketHandle.cs +++ b/mcs/class/System/System.Net.Sockets/SafeSocketHandle.cs @@ -7,7 +7,9 @@ using System; using System.IO; +using System.Text; using System.Threading; +using System.Diagnostics; using System.Collections.Generic; using Microsoft.Win32.SafeHandles; @@ -16,6 +18,9 @@ namespace System.Net.Sockets { sealed class SafeSocketHandle : SafeHandleZeroOrMinusOneIsInvalid { List blocking_threads; + Dictionary threads_stacktraces; + + bool in_cleanup; const int SOCKET_CLOSED = 10004; @@ -25,6 +30,9 @@ namespace System.Net.Sockets { public SafeSocketHandle (IntPtr preexistingHandle, bool ownsHandle) : base (ownsHandle) { SetHandle (preexistingHandle); + + if (THROW_ON_ABORT_RETRIES) + threads_stacktraces = new Dictionary (); } // This is just for marshalling @@ -32,57 +40,56 @@ namespace System.Net.Sockets { { } - bool closii; - - /*protected override void Dispose (bool disposing) - { - lock (this) { - if (!closii) { - - closii = true; - int error = 0; - Socket.Blocking_internal (handle, false, out error); - //AbortRegisteredThreads (); - Socket.Close_internal (handle, out error); - //Console.Error.WriteLine ("Closed "+ handle); - } - } - base.Dispose (disposing); - }*/ - protected override bool ReleaseHandle () { int error = 0; Socket.Blocking_internal (handle, false, out error); +#if AOT_ONLY_DESKTOP + /* 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 (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; - } - - /* - * 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) { + lock (blocking_threads) { + int abort_attempts = 0; + while (blocking_threads.Count > 0) { + if (abort_attempts++ >= ABORT_RETRIES) { + if (THROW_ON_ABORT_RETRIES) { + StringBuilder sb = new StringBuilder (); + sb.AppendLine ("Could not abort registered blocking threads before closing socket."); + foreach (var thread in blocking_threads) { + sb.AppendLine ("Thread StackTrace:"); + sb.AppendLine (threads_stacktraces[thread].ToString ()); + } + sb.AppendLine (); + + throw new Exception (sb.ToString ()); + } + + // 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; + } + + /* + * 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. + */ if (blocking_threads.Count == 1 && blocking_threads[0] == Thread.CurrentThread) break; - } - AbortRegisteredThreads (); - // Sleep so other threads can resume - Thread.Sleep (1); + // abort registered threads + foreach (var t in blocking_threads) + Socket.cancel_blocking_socket_operation (t); + + // Sleep so other threads can resume + in_cleanup = true; + Monitor.Wait (blocking_threads, 100); + } } } @@ -103,6 +110,8 @@ namespace System.Net.Sockets { /* We must use a finally block here to make this atomic. */ lock (blocking_threads) { blocking_threads.Add (Thread.CurrentThread); + if (THROW_ON_ABORT_RETRIES) + threads_stacktraces.Add (Thread.CurrentThread, new StackTrace (true)); } if (release) DangerousRelease (); @@ -118,17 +127,15 @@ namespace System.Net.Sockets { { //If this NRE, we're in deep problems because Register Must have lock (blocking_threads) { - blocking_threads.Remove (Thread.CurrentThread); - } - } - - void AbortRegisteredThreads () { - if (blocking_threads == null) - return; + var current = Thread.CurrentThread; + blocking_threads.Remove (current); + if (THROW_ON_ABORT_RETRIES) { + if (blocking_threads.IndexOf (current) == -1) + threads_stacktraces.Remove (current); + } - lock (blocking_threads) { - foreach (var t in blocking_threads) - Socket.cancel_blocking_socket_operation (t); + if (in_cleanup && blocking_threads.Count == 0) + Monitor.Pulse (blocking_threads); } } }