Check for missing Assembly.Location
[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                 bool closii;
36
37                 /*protected override void Dispose (bool disposing)
38                 {
39                         lock (this) {
40                                 if (!closii) {
41
42                                         closii = true;
43                                         int error = 0;
44                                         Socket.Blocking_internal (handle, false, out error);
45                                         //AbortRegisteredThreads ();
46                                         Socket.Close_internal (handle, out error);
47                                         //Console.Error.WriteLine ("Closed "+ handle);
48                                 }
49                         }
50                         base.Dispose (disposing);
51                 }*/
52
53                 protected override bool ReleaseHandle ()
54                 {
55                         int error = 0;
56
57                         Socket.Blocking_internal (handle, false, out error);
58
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.");
65
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.
69                                                 break;
70                                         }
71
72                                         /*
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.
77                                          */
78                                         lock (blocking_threads) {
79                                                 if (blocking_threads.Count == 1 && blocking_threads[0] == Thread.CurrentThread)
80                                                         break;
81                                         }
82
83                                         AbortRegisteredThreads ();
84                                         // Sleep so other threads can resume
85                                         Thread.Sleep (1);
86                                 }
87                         }
88
89                         Socket.Close_internal (handle, out error);
90
91                         return error == 0;
92                 }
93
94                 public void RegisterForBlockingSyscall ()
95                 {
96                         if (blocking_threads == null)
97                                 Interlocked.CompareExchange (ref blocking_threads, new List<Thread> (), null);
98                         
99                         bool release = false;
100                         try {
101                                 DangerousAddRef (ref release);
102                         } finally {
103                                 /* We must use a finally block here to make this atomic. */
104                                 lock (blocking_threads) {
105                                         blocking_threads.Add (Thread.CurrentThread);
106                                 }
107                                 if (release)
108                                         DangerousRelease ();
109
110                                 // Handle can be closed by DangerousRelease
111                                 if (IsClosed)
112                                         throw new SocketException (SOCKET_CLOSED);
113                         }
114                 }
115
116                 /* This must be called from a finally block! */
117                 public void UnRegisterForBlockingSyscall ()
118                 {
119                         //If this NRE, we're in deep problems because Register Must have
120                         lock (blocking_threads) {
121                                 blocking_threads.Remove (Thread.CurrentThread);
122                         }
123                 }
124
125                 void AbortRegisteredThreads () {
126                         if (blocking_threads == null)
127                                 return;
128
129                         lock (blocking_threads) {
130                                 foreach (var t in blocking_threads)
131                                         Socket.cancel_blocking_socket_operation (t);
132                         }
133                 }
134         }
135 }
136