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;
21 const int SOCKET_CLOSED = 10004;
23 const int ABORT_RETRIES = 10;
24 static bool THROW_ON_ABORT_RETRIES = Environment.GetEnvironmentVariable("MONO_TESTS_IN_PROGRESS") == "yes";
26 public SafeSocketHandle (IntPtr preexistingHandle, bool ownsHandle) : base (ownsHandle)
28 SetHandle (preexistingHandle);
31 // This is just for marshalling
32 internal SafeSocketHandle () : base (true)
36 protected override bool ReleaseHandle ()
40 Socket.Blocking_internal (handle, false, out error);
42 /* It's only for platforms that do not have working syscall abort mechanism, like WatchOS and TvOS */
43 Socket.Shutdown_internal (handle, SocketShutdown.Both, out error);
46 if (blocking_threads != null) {
47 lock (blocking_threads) {
48 int abort_attempts = 0;
49 while (blocking_threads.Count > 0) {
50 if (abort_attempts++ >= ABORT_RETRIES) {
51 if (THROW_ON_ABORT_RETRIES)
52 throw new Exception ("Could not abort registered blocking threads before closing socket.");
54 // Attempts to close the socket safely failed.
55 // We give up, and close the socket with pending blocking system calls.
56 // This should not occur, nonetheless if it does this avoids an endless loop.
61 * This method can be called by the DangerousRelease inside RegisterForBlockingSyscall
62 * When this happens blocking_threads contains the current thread.
63 * We can safely close the socket and throw SocketException in RegisterForBlockingSyscall
64 * before the blocking system call.
66 if (blocking_threads.Count == 1 && blocking_threads[0] == Thread.CurrentThread)
69 // abort registered threads
70 foreach (var t in blocking_threads)
71 Socket.cancel_blocking_socket_operation (t);
73 // Sleep so other threads can resume
75 Monitor.Wait (blocking_threads, 100);
80 Socket.Close_internal (handle, out error);
85 public void RegisterForBlockingSyscall ()
87 if (blocking_threads == null)
88 Interlocked.CompareExchange (ref blocking_threads, new List<Thread> (), null);
92 DangerousAddRef (ref release);
94 /* We must use a finally block here to make this atomic. */
95 lock (blocking_threads) {
96 blocking_threads.Add (Thread.CurrentThread);
101 // Handle can be closed by DangerousRelease
103 throw new SocketException (SOCKET_CLOSED);
107 /* This must be called from a finally block! */
108 public void UnRegisterForBlockingSyscall ()
110 //If this NRE, we're in deep problems because Register Must have
111 lock (blocking_threads) {
112 blocking_threads.Remove (Thread.CurrentThread);
113 if (in_cleanup && blocking_threads.Count == 0)
114 Monitor.Pulse (blocking_threads);