Merge pull request #1325 from madrang/CryptoToolsCtorCheck
[mono.git] / mcs / class / System / System.Net.Sockets / SafeSocketHandle.cs
1 //
2 // System.Net.Sockets.SafeSocketHandle
3 //
4 // Authors:
5 //      Marcos Henrich  <marcos.henrich@xamarin.com>
6 //
7
8 using System;
9 using System.IO;
10 using System.Threading;
11 using System.Collections.Generic;
12 using Microsoft.Win32.SafeHandles;
13
14 namespace System.Net.Sockets {
15
16         sealed class SafeSocketHandle : SafeHandleZeroOrMinusOneIsInvalid {
17
18                 List<Thread> blocking_threads;
19
20                 const int SOCKET_CLOSED = 10004;
21
22                 const int ABORT_RETRIES = 10;
23                 static bool THROW_ON_ABORT_RETRIES = Environment.GetEnvironmentVariable("MONO_TESTS_IN_PROGRESS") == "yes";
24
25                 public SafeSocketHandle (IntPtr preexistingHandle, bool ownsHandle) : base (ownsHandle)
26                 {
27                         SetHandle (preexistingHandle);
28                 }
29
30                 // This is just for marshalling
31                 internal SafeSocketHandle () : base (true)
32                 {
33                 }
34
35                 protected override bool ReleaseHandle ()
36                 {
37                         int error = 0;
38
39                         Socket.Blocking_internal (handle, false, out error);
40
41                         if (blocking_threads != null) {
42                                 int abort_attempts = 0;
43                                 while (blocking_threads.Count > 0) {
44                                         if (abort_attempts++ >= ABORT_RETRIES) {
45                                                 if (THROW_ON_ABORT_RETRIES)
46                                                         throw new Exception ("Could not abort registered blocking threads before closing socket.");
47
48                                                 // Attempts to close the socket safely failed.
49                                                 // We give up, and close the socket with pending blocking system calls.
50                                                 // This should not occur, nonetheless if it does this avoids an endless loop.
51                                                 break;
52                                         }
53
54                                         /*
55                                          * This method can be called by the DangerousRelease inside RegisterForBlockingSyscall
56                                          * When this happens blocking_threads contains the current thread.
57                                          * We can safely close the socket and throw SocketException in RegisterForBlockingSyscall
58                                          * before the blocking system call.
59                                          */
60                                         lock (blocking_threads) {
61                                                 if (blocking_threads.Count == 1 && blocking_threads[0] == Thread.CurrentThread)
62                                                         break;
63                                         }
64
65                                         AbortRegisteredThreads ();
66                                         // Sleep so other threads can resume
67                                         Thread.Sleep (1);
68                                 }
69                         }
70
71                         Socket.Close_internal (handle, out error);
72
73                         return error == 0;
74                 }
75
76                 public void RegisterForBlockingSyscall ()
77                 {
78                         if (blocking_threads == null)
79                                 Interlocked.CompareExchange (ref blocking_threads, new List<Thread> (), null);
80                         
81                         bool release = false;
82                         try {
83                                 DangerousAddRef (ref release);
84                         } finally {
85                                 /* We must use a finally block here to make this atomic. */
86                                 lock (blocking_threads) {
87                                         blocking_threads.Add (Thread.CurrentThread);
88                                 }
89                                 if (release)
90                                         DangerousRelease ();
91
92                                 // Handle can be closed by DangerousRelease
93                                 if (IsClosed)
94                                         throw new SocketException (SOCKET_CLOSED);
95                         }
96                 }
97
98                 /* This must be called from a finally block! */
99                 public void UnRegisterForBlockingSyscall ()
100                 {
101                         //If this NRE, we're in deep problems because Register Must have
102                         lock (blocking_threads) {
103                                 blocking_threads.Remove (Thread.CurrentThread);
104                         }
105                 }
106
107                 void AbortRegisteredThreads () {
108                         if (blocking_threads == null)
109                                 return;
110
111                         lock (blocking_threads) {
112                                 foreach (var t in blocking_threads)
113                                         Socket.cancel_blocking_socket_operation (t);
114                         }
115                 }
116         }
117 }
118