[System] Improved "Could not abort" exception.
authorMarcos Henrich <marcos.henrich@xamarin.com>
Tue, 7 Jun 2016 09:38:29 +0000 (10:38 +0100)
committerMarcos Henrich <marcos.henrich@xamarin.com>
Tue, 7 Jun 2016 13:01:00 +0000 (14:01 +0100)
Stack traces of blocked threads are now displayed on the exception.

The threads stacktraces are only stored when MONO_TESTS_IN_PROGRESS env
var is set to yes.

mcs/class/System/System.Net.Sockets/SafeSocketHandle.cs

index 33f83eccec481a4768b360eb7a4552b41b38f936..968038e5c3657b7995306ffa4a6750391770cf0e 100644 (file)
@@ -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,8 @@ namespace System.Net.Sockets {
        sealed class SafeSocketHandle : SafeHandleZeroOrMinusOneIsInvalid {
 
                List<Thread> blocking_threads;
+               Dictionary<Thread, StackTrace> threads_stacktraces;
+
                bool in_cleanup;
 
                const int SOCKET_CLOSED = 10004;
@@ -26,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<Thread, StackTrace> ();
                }
 
                // This is just for marshalling
@@ -48,8 +55,17 @@ namespace System.Net.Sockets {
                                        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.");
+                                                       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.
@@ -94,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 ();
@@ -110,6 +128,9 @@ namespace System.Net.Sockets {
                        //If this NRE, we're in deep problems because Register Must have
                        lock (blocking_threads) {
                                blocking_threads.Remove (Thread.CurrentThread);
+                               if (THROW_ON_ABORT_RETRIES)
+                                       threads_stacktraces.Remove (Thread.CurrentThread);
+
                                if (in_cleanup && blocking_threads.Count == 0)
                                        Monitor.Pulse (blocking_threads);
                        }