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)
35 protected override bool ReleaseHandle ()
39 Socket.Blocking_internal (handle, false, out error);
41 /* It's only for platforms that do not have working syscall abort mechanism, like WatchOS and TvOS */
42 Socket.Shutdown_internal (handle, SocketShutdown.Both, out error);
45 if (blocking_threads != null) {
46 int abort_attempts = 0;
47 while (blocking_threads.Count > 0) {
48 if (abort_attempts++ >= ABORT_RETRIES) {
49 if (THROW_ON_ABORT_RETRIES)
50 throw new Exception ("Could not abort registered blocking threads before closing socket.");
52 // Attempts to close the socket safely failed.
53 // We give up, and close the socket with pending blocking system calls.
54 // This should not occur, nonetheless if it does this avoids an endless loop.
59 * This method can be called by the DangerousRelease inside RegisterForBlockingSyscall
60 * When this happens blocking_threads contains the current thread.
61 * We can safely close the socket and throw SocketException in RegisterForBlockingSyscall
62 * before the blocking system call.
64 lock (blocking_threads) {
65 if (blocking_threads.Count == 1 && blocking_threads[0] == Thread.CurrentThread)
69 AbortRegisteredThreads ();
70 // Sleep so other threads can resume
75 Socket.Close_internal (handle, out error);
80 public void RegisterForBlockingSyscall ()
82 if (blocking_threads == null)
83 Interlocked.CompareExchange (ref blocking_threads, new List<Thread> (), null);
87 DangerousAddRef (ref release);
89 /* We must use a finally block here to make this atomic. */
90 lock (blocking_threads) {
91 blocking_threads.Add (Thread.CurrentThread);
96 // Handle can be closed by DangerousRelease
98 throw new SocketException (SOCKET_CLOSED);
102 /* This must be called from a finally block! */
103 public void UnRegisterForBlockingSyscall ()
105 //If this NRE, we're in deep problems because Register Must have
106 lock (blocking_threads) {
107 blocking_threads.Remove (Thread.CurrentThread);
111 void AbortRegisteredThreads () {
112 if (blocking_threads == null)
115 lock (blocking_threads) {
116 foreach (var t in blocking_threads)
117 Socket.cancel_blocking_socket_operation (t);