[sgen] Write barrier nursery checks might be needed
[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 ABORT_RETRIES = 10;
21                 static bool THROW_ON_ABORT_RETRIES = Environment.GetEnvironmentVariable("MONO_TESTS_IN_PROGRESS") == "yes";
22
23                 public SafeSocketHandle (IntPtr preexistingHandle, bool ownsHandle) : base (ownsHandle)
24                 {
25                         SetHandle (preexistingHandle);
26                 }
27
28                 // This is just for marshalling
29                 internal SafeSocketHandle () : base (true)
30                 {
31                 }
32
33                 bool closii;
34
35                 /*protected override void Dispose (bool disposing)
36                 {
37                         lock (this) {
38                                 if (!closii) {
39
40                                         closii = true;
41                                         int error = 0;
42                                         Socket.Blocking_internal (handle, false, out error);
43                                         //AbortRegisteredThreads ();
44                                         Socket.Close_internal (handle, out error);
45                                         //Console.Error.WriteLine ("Closed "+ handle);
46                                 }
47                         }
48                         base.Dispose (disposing);
49                 }*/
50
51                 protected override bool ReleaseHandle ()
52                 {
53                         int error = 0;
54
55                         Socket.Blocking_internal (handle, false, out error);
56
57                         if (blocking_threads != null) {
58                                 int abort_attempts = 0;
59                                 while (blocking_threads.Count > 0) {
60                                         if (abort_attempts++ >= ABORT_RETRIES) {
61                                                 if (THROW_ON_ABORT_RETRIES)
62                                                         throw new Exception ("Could not abort registered blocking threads before closing socket.");
63
64                                                 // Attempts to close the socket safely failed.
65                                                 // We give up, and close the socket with pending blocking system calls.
66                                                 // This should not occur, nonetheless if it does this avoids an endless loop.
67                                                 break;
68                                         }
69
70                                         AbortRegisteredThreads ();
71                                         // Sleep so other threads can resume
72                                         Thread.Sleep (1);
73                                 }
74                         }
75
76                         Socket.Close_internal (handle, out error);
77
78                         return error == 0;
79                 }
80
81                 public void RegisterForBlockingSyscall ()
82                 {
83                         if (blocking_threads == null)
84                                 Interlocked.CompareExchange (ref blocking_threads, new List<Thread> (), null);
85                         
86                         bool release = false;
87                         try {
88                                 DangerousAddRef (ref release);
89                         } finally {
90                                 /* We must use a finally block here to make this atomic. */
91                                 lock (blocking_threads) {
92                                         blocking_threads.Add (Thread.CurrentThread);
93                                 }
94                                 if (release)
95                                         DangerousRelease ();
96                         }
97                 }
98
99                 /* This must be called from a finally block! */
100                 public void UnRegisterForBlockingSyscall ()
101                 {
102                         //If this NRE, we're in deep problems because Register Must have
103                         lock (blocking_threads) {
104                                 blocking_threads.Remove (Thread.CurrentThread);
105                         }
106                 }
107
108                 void AbortRegisteredThreads () {
109                         if (blocking_threads == null)
110                                 return;
111
112                         lock (blocking_threads) {
113                                 foreach (var t in blocking_threads)
114                                         Socket.cancel_blocking_socket_operation (t);
115                         }
116                 }
117         }
118 }
119