[socket] Factor SocketAsyncResult and ProcessAsyncResult common parts into IOAsyncRes...
[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: IOAsyncResult
39         {
40                 public Socket socket;
41                 public IntPtr handle;
42                 public SocketOperation operation;
43
44                 Exception DelayedException;
45
46                 public EndPoint EndPoint;                 // Connect,ReceiveFrom,SendTo
47                 public byte [] Buffer;                    // Receive,ReceiveFrom,Send,SendTo
48                 public int Offset;                        // Receive,ReceiveFrom,Send,SendTo
49                 public int Size;                          // Receive,ReceiveFrom,Send,SendTo
50                 public SocketFlags SockFlags;             // Receive,ReceiveFrom,Send,SendTo
51                 public Socket AcceptSocket;               // AcceptReceive
52                 public IPAddress[] Addresses;             // Connect
53                 public int Port;                          // Connect
54                 public IList<ArraySegment<byte>> Buffers; // Receive, Send
55                 public bool ReuseSocket;                  // Disconnect
56                 public int CurrentAddress;                // Connect
57
58                 // Return values
59                 Socket accept_socket;
60                 int total;
61
62                 internal int error;
63
64                 public int EndCalled;
65
66                 public SocketAsyncWorker Worker;
67
68                 public SocketAsyncResult ()
69                         : base (null, null)
70                 {
71                 }
72
73                 public SocketAsyncResult (Socket socket, AsyncCallback callback, object state, SocketOperation operation, SocketAsyncWorker worker = null)
74                         : base (callback, state)
75                 {
76                         this.socket = socket;
77                         this.handle = socket != null ? socket.Handle : IntPtr.Zero;
78                         this.operation = operation;
79
80                         DelayedException = null;
81
82                         EndPoint = null;
83                         Buffer = null;
84                         Offset = 0;
85                         Size = 0;
86                         SockFlags = SocketFlags.None;
87                         AcceptSocket = null;
88                         Addresses = null;
89                         Port = 0;
90                         Buffers = null;
91                         ReuseSocket = false;
92                         accept_socket = null;
93                         total = 0;
94
95                         error = 0;
96                         EndCalled = 0;
97                         Worker = worker ?? new SocketAsyncWorker (this);
98                 }
99
100                 public Socket Socket {
101                         get {
102                                 return accept_socket;
103                         }
104                 }
105
106                 public int Total {
107                         get { return total; }
108                         set { total = value; }
109                 }
110
111                 public SocketError ErrorCode {
112                         get {
113                                 SocketException ex = DelayedException as SocketException;
114                                 if (ex != null)
115                                         return ex.SocketErrorCode;
116
117                                 if (error != 0)
118                                         return (SocketError) error;
119
120                                 return SocketError.Success;
121                         }
122                 }
123
124                 public void Dispose ()
125                 {
126                 }
127
128                 public void CheckIfThrowDelayedException ()
129                 {
130                         if (DelayedException != null) {
131                                 socket.is_connected = false;
132                                 throw DelayedException;
133                         }
134
135                         if (error != 0) {
136                                 socket.is_connected = false;
137                                 throw new SocketException (error);
138                         }
139                 }
140
141                 internal override void CompleteDisposed ()
142                 {
143                         Complete ();
144                 }
145
146                 public void Complete ()
147                 {
148                         if (operation != SocketOperation.Receive && socket.is_disposed)
149                                 DelayedException = new ObjectDisposedException (socket.GetType ().ToString ());
150
151                         IsCompleted = true;
152
153                         AsyncCallback callback = AsyncCallback;
154                         if (callback != null) {
155                                 ThreadPool.UnsafeQueueUserWorkItem (_ => callback (this), null);
156                         }
157
158                         Queue<KeyValuePair<IntPtr, IOSelectorJob>> queue = null;
159                         switch (operation) {
160                         case SocketOperation.Receive:
161                         case SocketOperation.ReceiveFrom:
162                         case SocketOperation.ReceiveGeneric:
163                         case SocketOperation.Accept:
164                                 queue = socket.readQ;
165                                 break;
166                         case SocketOperation.Send:
167                         case SocketOperation.SendTo:
168                         case SocketOperation.SendGeneric:
169                                 queue = socket.writeQ;
170                                 break;
171                         }
172
173                         if (queue != null) {
174                                 lock (queue) {
175                                         /* queue.Count will only be 0 if the socket is closed while receive/send/accept
176                                          * operation(s) are pending and at least one call to this method is waiting
177                                          * on the lock while another one calls CompleteAllOnDispose() */
178                                         if (queue.Count > 0)
179                                                 queue.Dequeue (); /* remove ourselves */
180                                         if (queue.Count > 0) {
181                                                 if (!socket.is_disposed) {
182                                                         IOSelector.Add (queue.Peek ().Key, queue.Peek ().Value);
183                                                 } else {
184                                                         /* CompleteAllOnDispose */
185                                                         KeyValuePair<IntPtr, IOSelectorJob> [] jobs = queue.ToArray ();
186                                                         for (int i = 0; i < jobs.Length; i++)
187                                                                 ThreadPool.QueueUserWorkItem (j => ((IOSelectorJob) j).MarkDisposed (), jobs [i].Value);
188                                                         queue.Clear ();
189                                                 }
190                                         }
191                                 }
192                         }
193
194                         // IMPORTANT: 'callback', if any is scheduled from unmanaged code
195                 }
196
197                 public void Complete (bool synch)
198                 {
199                         CompletedSynchronously = synch;
200                         Complete ();
201                 }
202
203                 public void Complete (int total)
204                 {
205                         this.total = total;
206                         Complete ();
207                 }
208
209                 public void Complete (Exception e, bool synch)
210                 {
211                         DelayedException = e;
212                         CompletedSynchronously = synch;
213                         Complete ();
214                 }
215
216                 public void Complete (Exception e)
217                 {
218                         DelayedException = e;
219                         Complete ();
220                 }
221
222                 public void Complete (Socket s)
223                 {
224                         this.accept_socket = s;
225                         Complete ();
226                 }
227
228                 public void Complete (Socket s, int total)
229                 {
230                         this.accept_socket = s;
231                         this.total = total;
232                         Complete ();
233                 }
234         }
235 }