Internal worker is kept while there is an ongoing operation
[mono.git] / mcs / class / System / System.Net.Sockets / SocketAsyncEventArgs.cs
1 // System.Net.Sockets.SocketAsyncEventArgs.cs
2 //
3 // Authors:
4 //      Marek Habersack (mhabersack@novell.com)
5 //      Gonzalo Paniagua Javier (gonzalo@xamarin.com)
6 //
7 // Copyright (c) 2008,2010 Novell, Inc. (http://www.novell.com)
8 // Copyright (c) 2011 Xamarin, Inc. (http://xamarin.com)
9 //
10
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31 using System;
32 using System.Collections.Generic;
33 using System.Reflection;
34 using System.Security;
35 using System.Threading;
36
37 namespace System.Net.Sockets
38 {
39         public class SocketAsyncEventArgs : EventArgs, IDisposable
40         {
41                 bool disposed;
42                 int in_progress;
43                 internal Socket.Worker Worker;
44                 EndPoint remote_ep;
45 #if MOONLIGHT || NET_4_0
46                 public Exception ConnectByNameError { get; internal set; }
47 #endif
48
49                 public event EventHandler<SocketAsyncEventArgs> Completed;
50
51                 IList <ArraySegment <byte>> _bufferList;
52                 
53                 public Socket AcceptSocket { get; set; }
54                 public byte[] Buffer { get; private set; }
55
56                 public IList<ArraySegment<byte>> BufferList {
57                         get { return _bufferList; }
58                         set {
59                                 if (Buffer != null && value != null)
60                                         throw new ArgumentException ("Buffer and BufferList properties cannot both be non-null.");
61                                 _bufferList = value;
62                         }
63                 }
64
65                 public int BytesTransferred { get; internal set; }
66                 public int Count { get; internal set; }
67                 public bool DisconnectReuseSocket { get; set; }
68                 public SocketAsyncOperation LastOperation { get; private set; }
69                 public int Offset { get; private set; }
70                 public EndPoint RemoteEndPoint {
71                         get { return remote_ep; }
72                         set { remote_ep = value; }
73                 }
74 #if !NET_2_1
75                 public IPPacketInformation ReceiveMessageFromPacketInfo { get; private set; }
76                 public SendPacketsElement[] SendPacketsElements { get; set; }
77                 public TransmitFileOptions SendPacketsFlags { get; set; }
78 #endif
79                 [MonoTODO ("unused property")]
80                 public int SendPacketsSendSize { get; set; }
81                 public SocketError SocketError { get; set; }
82                 public SocketFlags SocketFlags { get; set; }
83                 public object UserToken { get; set; }
84
85 #if MOONLIGHT && !INSIDE_SYSTEM
86                 private SocketClientAccessPolicyProtocol policy_protocol;
87
88                 public SocketClientAccessPolicyProtocol SocketClientAccessPolicyProtocol {
89                         get { return policy_protocol; }
90                         set {
91                                 if ((value != SocketClientAccessPolicyProtocol.Tcp) && (value != SocketClientAccessPolicyProtocol.Http))
92                                         throw new ArgumentException ("Invalid value");
93                                 policy_protocol = value;
94                         }
95                 }
96 #endif
97
98                 internal Socket curSocket;
99 #if NET_2_1
100                 public Socket ConnectSocket {
101                         get {
102                                 switch (SocketError) {
103                                 case SocketError.AccessDenied:
104                                         return null;
105                                 default:
106                                         return curSocket;
107                                 }
108                         }
109                 }
110
111                 internal bool PolicyRestricted { get; private set; }
112
113                 internal SocketAsyncEventArgs (bool policy) : 
114                         this ()
115                 {
116                         PolicyRestricted = policy;
117                 }
118 #endif
119                 
120                 public SocketAsyncEventArgs ()
121                 {
122                         Worker = new Socket.Worker (this);
123                         AcceptSocket = null;
124                         Buffer = null;
125                         BufferList = null;
126                         BytesTransferred = 0;
127                         Count = 0;
128                         DisconnectReuseSocket = false;
129                         LastOperation = SocketAsyncOperation.None;
130                         Offset = 0;
131                         RemoteEndPoint = null;
132 #if !NET_2_1
133                         SendPacketsElements = null;
134                         SendPacketsFlags = TransmitFileOptions.UseDefaultWorkerThread;
135 #endif
136                         SendPacketsSendSize = -1;
137                         SocketError = SocketError.Success;
138                         SocketFlags = SocketFlags.None;
139                         UserToken = null;
140
141 #if MOONLIGHT && !INSIDE_SYSTEM
142                         policy_protocol = SocketClientAccessPolicyProtocol.Tcp;
143 #endif
144                 }
145
146                 ~SocketAsyncEventArgs ()
147                 {
148                         Dispose (false);
149                 }
150
151                 void Dispose (bool disposing)
152                 {
153                         disposed = true;
154
155                         if (disposing) {
156                                 if (disposed || Interlocked.CompareExchange (ref in_progress, 0, 0) != 0)
157                                         return;
158                                 if (Worker != null) {
159                                         Worker.Dispose ();
160                                         Worker = null;
161                                 }
162                         }
163                         AcceptSocket = null;
164                         Buffer = null;
165                         BufferList = null;
166                         RemoteEndPoint = null;
167                         UserToken = null;
168 #if !NET_2_1
169                         SendPacketsElements = null;
170 #endif
171                 }               
172
173                 public void Dispose ()
174                 {
175                         Dispose (true);
176                         GC.SuppressFinalize (this);
177                 }
178
179                 internal void SetLastOperation (SocketAsyncOperation op)
180                 {
181                         if (disposed)
182                                 throw new ObjectDisposedException ("System.Net.Sockets.SocketAsyncEventArgs");
183                         if (Interlocked.Exchange (ref in_progress, 1) != 0)
184                                 throw new InvalidOperationException ("Operation already in progress");
185                         LastOperation = op;
186                 }
187
188                 protected virtual void OnCompleted (SocketAsyncEventArgs e)
189                 {
190                         if (e == null)
191                                 return;
192                         
193                         EventHandler<SocketAsyncEventArgs> handler = e.Completed;
194                         if (handler != null)
195                                 handler (e.curSocket, e);
196                 }
197
198                 public void SetBuffer (int offset, int count)
199                 {
200                         SetBufferInternal (Buffer, offset, count);
201                 }
202
203                 public void SetBuffer (byte[] buffer, int offset, int count)
204                 {
205                         SetBufferInternal (buffer, offset, count);
206                 }
207
208                 void SetBufferInternal (byte[] buffer, int offset, int count)
209                 {
210                         if (buffer != null) {
211                                 if (BufferList != null)
212                                         throw new ArgumentException ("Buffer and BufferList properties cannot both be non-null.");
213                                 
214                                 int buflen = buffer.Length;
215                                 if (offset < 0 || (offset != 0 && offset >= buflen))
216                                         throw new ArgumentOutOfRangeException ("offset");
217
218                                 if (count < 0 || count > buflen - offset)
219                                         throw new ArgumentOutOfRangeException ("count");
220
221                                 Count = count;
222                                 Offset = offset;
223                         }
224                         Buffer = buffer;
225                 }
226
227 #region Internals
228                 internal static AsyncCallback Dispatcher = new AsyncCallback (DispatcherCB);
229
230                 static void DispatcherCB (IAsyncResult ares)
231                 {
232                         SocketAsyncEventArgs args = (SocketAsyncEventArgs) ares.AsyncState;
233                         if (Interlocked.Exchange (ref args.in_progress, 0) != 1)
234                                 throw new InvalidOperationException ("No operation in progress");
235                         SocketAsyncOperation op = args.LastOperation;
236                         // Notes;
237                         //      -SocketOperation.AcceptReceive not used in SocketAsyncEventArgs
238                         //      -SendPackets and ReceiveMessageFrom are not implemented yet
239                         if (op == SocketAsyncOperation.Receive)
240                                 args.ReceiveCallback (ares);
241                         else if (op == SocketAsyncOperation.Send)
242                                 args.SendCallback (ares);
243 #if !MOONLIGHT
244                         else if (op == SocketAsyncOperation.ReceiveFrom)
245                                 args.ReceiveFromCallback (ares);
246                         else if (op == SocketAsyncOperation.SendTo)
247                                 args.SendToCallback (ares);
248                         else if (op == SocketAsyncOperation.Accept)
249                                 args.AcceptCallback (ares);
250                         else if (op == SocketAsyncOperation.Disconnect)
251                                 args.DisconnectCallback (ares);
252 #endif
253                         else if (op == SocketAsyncOperation.Connect)
254                                 args.ConnectCallback ();
255                         /*
256                         else if (op == Socket.SocketOperation.ReceiveMessageFrom)
257                         else if (op == Socket.SocketOperation.SendPackets)
258                         */
259                         else
260                                 throw new NotImplementedException (String.Format ("Operation {0} is not implemented", op));
261
262                 }
263
264                 internal void ReceiveCallback (IAsyncResult ares)
265                 {
266                         try {
267                                 BytesTransferred = curSocket.EndReceive (ares);
268                         } catch (SocketException se){
269                                 SocketError = se.SocketErrorCode;
270                         } catch (ObjectDisposedException) {
271                                 SocketError = SocketError.OperationAborted;
272                         } finally {
273                                 OnCompleted (this);
274                         }
275                 }
276
277                 void ConnectCallback ()
278                 {
279                         try {
280                                 SocketError = (SocketError) Worker.result.error;
281                         } finally {
282                                 OnCompleted (this);
283                         }
284                 }
285
286                 internal void SendCallback (IAsyncResult ares)
287                 {
288                         try {
289                                 BytesTransferred = curSocket.EndSend (ares);
290                         } catch (SocketException se){
291                                 SocketError = se.SocketErrorCode;
292                         } catch (ObjectDisposedException) {
293                                 SocketError = SocketError.OperationAborted;
294                         } finally {
295                                 OnCompleted (this);
296                         }
297                 }
298
299 #if !MOONLIGHT
300                 internal void AcceptCallback (IAsyncResult ares)
301                 {
302                         try {
303                                 AcceptSocket = curSocket.EndAccept (ares);
304                         } catch (SocketException ex) {
305                                 SocketError = ex.SocketErrorCode;
306                         } catch (ObjectDisposedException) {
307                                 SocketError = SocketError.OperationAborted;
308                         } finally {
309                                 if (AcceptSocket == null)
310                                         AcceptSocket = new Socket (curSocket.AddressFamily, curSocket.SocketType, curSocket.ProtocolType, (IntPtr)(-1));
311                                 OnCompleted (this);
312                         }
313                 }
314
315                 internal void DisconnectCallback (IAsyncResult ares)
316                 {
317                         try {
318                                 curSocket.EndDisconnect (ares);
319                         } catch (SocketException ex) {
320                                 SocketError = ex.SocketErrorCode;
321                         } catch (ObjectDisposedException) {
322                                 SocketError = SocketError.OperationAborted;
323                         } finally {
324                                 OnCompleted (this);
325                         }
326                 }
327
328                 internal void ReceiveFromCallback (IAsyncResult ares)
329                 {
330                         try {
331                                 BytesTransferred = curSocket.EndReceiveFrom (ares, ref remote_ep);
332                         } catch (SocketException ex) {
333                                 SocketError = ex.SocketErrorCode;
334                         } catch (ObjectDisposedException) {
335                                 SocketError = SocketError.OperationAborted;
336                         } finally {
337                                 OnCompleted (this);
338                         }
339                 }
340
341                 internal void SendToCallback (IAsyncResult ares)
342                 {
343                         try {
344                                 BytesTransferred = curSocket.EndSendTo (ares);
345                         } catch (SocketException ex) {
346                                 SocketError = ex.SocketErrorCode;
347                         } catch (ObjectDisposedException) {
348                                 SocketError = SocketError.OperationAborted;
349                         } finally {
350                                 OnCompleted (this);
351                         }
352                 }
353
354 #endif
355 #endregion
356         }
357 }