2 // System.Net.Sockets.SafeSocketHandle
5 // Marcos Henrich <marcos.henrich@xamarin.com>
10 using System.Threading;
11 using System.Collections.Generic;
12 using Microsoft.Win32.SafeHandles;
14 namespace System.Net.Sockets {
16 sealed class SafeSocketHandle : SafeHandleZeroOrMinusOneIsInvalid {
18 List<Thread> blocking_threads;
20 const int SOCKET_CLOSED = 10004;
22 const int ABORT_RETRIES = 10;
23 static bool THROW_ON_ABORT_RETRIES = Environment.GetEnvironmentVariable("MONO_TESTS_IN_PROGRESS") == "yes";
25 public SafeSocketHandle (IntPtr preexistingHandle, bool ownsHandle) : base (ownsHandle)
27 SetHandle (preexistingHandle);
30 // This is just for marshalling
31 internal SafeSocketHandle () : base (true)
37 /*protected override void Dispose (bool disposing)
44 Socket.Blocking_internal (handle, false, out error);
45 //AbortRegisteredThreads ();
46 Socket.Close_internal (handle, out error);
47 //Console.Error.WriteLine ("Closed "+ handle);
50 base.Dispose (disposing);
53 protected override bool ReleaseHandle ()
57 Socket.Blocking_internal (handle, false, out error);
59 if (blocking_threads != null) {
60 int abort_attempts = 0;
61 while (blocking_threads.Count > 0) {
62 if (abort_attempts++ >= ABORT_RETRIES) {
63 if (THROW_ON_ABORT_RETRIES)
64 throw new Exception ("Could not abort registered blocking threads before closing socket.");
66 // Attempts to close the socket safely failed.
67 // We give up, and close the socket with pending blocking system calls.
68 // This should not occur, nonetheless if it does this avoids an endless loop.
73 * This method can be called by the DangerousRelease inside RegisterForBlockingSyscall
74 * When this happens blocking_threads contains the current thread.
75 * We can safely close the socket and throw SocketException in RegisterForBlockingSyscall
76 * before the blocking system call.
78 lock (blocking_threads) {
79 if (blocking_threads.Count == 1 && blocking_threads[0] == Thread.CurrentThread)
83 AbortRegisteredThreads ();
84 // Sleep so other threads can resume
89 Socket.Close_internal (handle, out error);
94 public void RegisterForBlockingSyscall ()
96 if (blocking_threads == null)
97 Interlocked.CompareExchange (ref blocking_threads, new List<Thread> (), null);
101 DangerousAddRef (ref release);
103 /* We must use a finally block here to make this atomic. */
104 lock (blocking_threads) {
105 blocking_threads.Add (Thread.CurrentThread);
110 // Handle can be closed by DangerousRelease
112 throw new SocketException (SOCKET_CLOSED);
116 /* This must be called from a finally block! */
117 public void UnRegisterForBlockingSyscall ()
119 //If this NRE, we're in deep problems because Register Must have
120 lock (blocking_threads) {
121 blocking_threads.Remove (Thread.CurrentThread);
125 void AbortRegisteredThreads () {
126 if (blocking_threads == null)
129 lock (blocking_threads) {
130 foreach (var t in blocking_threads)
131 Socket.cancel_blocking_socket_operation (t);