Merge pull request #1912 from ludovic-henry/threadpool-managed-asyncresult
[mono.git] / mcs / class / System / System.Net.Sockets / SocketAsyncResult.cs
1 // System.Net.Sockets.SocketAsyncResult.cs
2 //
3 // Authors:
4 //      Ludovic Henry <ludovic@xamarin.com>
5 //
6 // Copyright (C) 2015 Xamarin, Inc. (https://www.xamarin.com)
7 //
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28
29 using System.Collections;
30 using System.Collections.Generic;
31 using System.Runtime.InteropServices;
32 using System.Runtime.Remoting.Messaging;
33 using System.Threading;
34
35 namespace System.Net.Sockets
36 {
37         [StructLayout (LayoutKind.Sequential)]
38         internal sealed class SocketAsyncResult: IAsyncResult, IThreadPoolWorkItem
39         {
40                 /* Same structure in the runtime. Keep this in sync with
41                  * MonoSocketAsyncResult in metadata/socket-io.h and
42                  * ProcessAsyncReader in System.Diagnostics/Process.cs. */
43
44                 public Socket socket;
45                 IntPtr handle;
46                 object state;
47                 AsyncCallback callback; // used from the runtime
48                 WaitHandle wait_handle;
49
50                 Exception delayed_exception;
51
52                 public EndPoint EndPoint;                 // Connect,ReceiveFrom,SendTo
53                 public byte [] Buffer;                    // Receive,ReceiveFrom,Send,SendTo
54                 public int Offset;                        // Receive,ReceiveFrom,Send,SendTo
55                 public int Size;                          // Receive,ReceiveFrom,Send,SendTo
56                 public SocketFlags SockFlags;             // Receive,ReceiveFrom,Send,SendTo
57                 public Socket AcceptSocket;               // AcceptReceive
58                 public IPAddress[] Addresses;             // Connect
59                 public int Port;                          // Connect
60                 public IList<ArraySegment<byte>> Buffers; // Receive, Send
61                 public bool ReuseSocket;                  // Disconnect
62
63                 // Return values
64                 Socket accept_socket;
65                 int total;
66
67                 bool completed_synchronously;
68                 bool completed;
69                 bool is_blocking;
70                 internal int error;
71                 public SocketOperation operation;
72                 AsyncResult async_result;
73                 public int EndCalled;
74
75                 /* These fields are not in MonoSocketAsyncResult */
76                 public SocketAsyncWorker Worker;
77                 public int CurrentAddress;                // Connect
78
79                 public SocketAsyncResult ()
80                 {
81                 }
82
83                 public SocketAsyncResult (Socket socket, object state, AsyncCallback callback, SocketOperation operation)
84                 {
85                         Init (socket, state, callback, operation, new SocketAsyncWorker (this));
86                 }
87
88                 public object AsyncState {
89                         get {
90                                 return state;
91                         }
92                 }
93
94                 public WaitHandle AsyncWaitHandle {
95                         get {
96                                 lock (this) {
97                                         if (wait_handle == null)
98                                                 wait_handle = new ManualResetEvent (completed);
99                                 }
100
101                                 return wait_handle;
102                         }
103                         set {
104                                 wait_handle = value;
105                         }
106                 }
107
108                 public bool CompletedSynchronously {
109                         get {
110                                 return completed_synchronously;
111                         }
112                 }
113
114                 public bool IsCompleted {
115                         get {
116                                 return completed;
117                         }
118                         set {
119                                 completed = value;
120                                 lock (this) {
121                                         if (wait_handle != null && value)
122                                                 ((ManualResetEvent) wait_handle).Set ();
123                                 }
124                         }
125                 }
126
127                 public Socket Socket {
128                         get {
129                                 return accept_socket;
130                         }
131                 }
132
133                 public int Total {
134                         get { return total; }
135                         set { total = value; }
136                 }
137
138                 public SocketError ErrorCode {
139                         get {
140                                 SocketException ex = delayed_exception as SocketException;
141                                 if (ex != null)
142                                         return ex.SocketErrorCode;
143
144                                 if (error != 0)
145                                         return (SocketError) error;
146
147                                 return SocketError.Success;
148                         }
149                 }
150
151                 public void Init (Socket socket, object state, AsyncCallback callback, SocketOperation operation, SocketAsyncWorker worker)
152                 {
153                         this.socket = socket;
154                         this.is_blocking = socket != null ? socket.is_blocking : true;
155                         this.handle = socket != null ? socket.Handle : IntPtr.Zero;
156                         this.state = state;
157                         this.callback = callback;
158                         this.operation = operation;
159
160                         if (wait_handle != null)
161                                 ((ManualResetEvent) wait_handle).Reset ();
162
163                         delayed_exception = null;
164
165                         EndPoint = null;
166                         Buffer = null;
167                         Offset = 0;
168                         Size = 0;
169                         SockFlags = SocketFlags.None;
170                         AcceptSocket = null;
171                         Addresses = null;
172                         Port = 0;
173                         Buffers = null;
174                         ReuseSocket = false;
175                         accept_socket = null;
176                         total = 0;
177
178                         completed_synchronously = false;
179                         completed = false;
180                         is_blocking = false;
181                         error = 0;
182                         async_result = null;
183                         EndCalled = 0;
184                         Worker = worker;
185                 }
186
187                 public void DoMConnectCallback ()
188                 {
189                         if (callback == null)
190                                 return;
191                         ThreadPool.UnsafeQueueUserWorkItem (_ => callback (this), null);
192                 }
193
194                 public void Dispose ()
195                 {
196                         Init (null, null, null, 0, Worker);
197                         if (wait_handle != null) {
198                                 wait_handle.Close ();
199                                 wait_handle = null;
200                         }
201                 }
202
203                 public void CheckIfThrowDelayedException ()
204                 {
205                         if (delayed_exception != null) {
206                                 socket.is_connected = false;
207                                 throw delayed_exception;
208                         }
209
210                         if (error != 0) {
211                                 socket.is_connected = false;
212                                 throw new SocketException (error);
213                         }
214                 }
215
216                 void CompleteDisposed (object unused)
217                 {
218                         Complete ();
219                 }
220
221                 public void Complete ()
222                 {
223                         if (operation != SocketOperation.Receive && socket.is_disposed)
224                                 delayed_exception = new ObjectDisposedException (socket.GetType ().ToString ());
225
226                         IsCompleted = true;
227
228                         Queue<SocketAsyncWorker> queue = null;
229                         switch (operation) {
230                         case SocketOperation.Receive:
231                         case SocketOperation.ReceiveFrom:
232                         case SocketOperation.ReceiveGeneric:
233                         case SocketOperation.Accept:
234                                 queue = socket.readQ;
235                                 break;
236                         case SocketOperation.Send:
237                         case SocketOperation.SendTo:
238                         case SocketOperation.SendGeneric:
239                                 queue = socket.writeQ;
240                                 break;
241                         }
242
243                         if (queue != null) {
244                                 lock (queue) {
245                                         /* queue.Count will only be 0 if the socket is closed while receive/send/accept
246                                          * operation(s) are pending and at least one call to this method is waiting
247                                          * on the lock while another one calls CompleteAllOnDispose() */
248                                         if (queue.Count > 0)
249                                                 queue.Dequeue (); /* remove ourselves */
250                                         if (queue.Count > 0) {
251                                                 if (!socket.is_disposed) {
252                                                         Socket.socket_pool_queue (SocketAsyncWorker.Dispatcher, (queue.Peek ()).result);
253                                                 } else {
254                                                         /* CompleteAllOnDispose */
255                                                         SocketAsyncWorker [] workers = queue.ToArray ();
256                                                         for (int i = 0; i < workers.Length; i++)
257                                                                 ThreadPool.UnsafeQueueUserWorkItem (workers [i].result.CompleteDisposed, null);
258                                                         queue.Clear ();
259                                                 }
260                                         }
261                                 }
262                         }
263
264                         // IMPORTANT: 'callback', if any is scheduled from unmanaged code
265                 }
266
267                 public void Complete (bool synch)
268                 {
269                         this.completed_synchronously = synch;
270                         Complete ();
271                 }
272
273                 public void Complete (int total)
274                 {
275                         this.total = total;
276                         Complete ();
277                 }
278
279                 public void Complete (Exception e, bool synch)
280                 {
281                         this.completed_synchronously = synch;
282                         this.delayed_exception = e;
283                         Complete ();
284                 }
285
286                 public void Complete (Exception e)
287                 {
288                         this.delayed_exception = e;
289                         Complete ();
290                 }
291
292                 public void Complete (Socket s)
293                 {
294                         this.accept_socket = s;
295                         Complete ();
296                 }
297
298                 public void Complete (Socket s, int total)
299                 {
300                         this.accept_socket = s;
301                         this.total = total;
302                         Complete ();
303                 }
304
305                 void IThreadPoolWorkItem.ExecuteWorkItem()
306                 {
307                         ((IThreadPoolWorkItem) async_result).ExecuteWorkItem ();
308
309                         if (completed && callback != null) {
310                                 ThreadPool.UnsafeQueueCustomWorkItem (new AsyncResult (state => callback ((IAsyncResult) state), this, false), false);
311                         }
312                 }
313
314                 void IThreadPoolWorkItem.MarkAborted(ThreadAbortException tae)
315                 {
316                 }
317         }
318 }