3 // AsyncProtocolRequest.cs
6 // Martin Baulig <martin.baulig@xamarin.com>
8 // Copyright (c) 2015 Xamarin, Inc.
13 using System.Net.Security;
14 using SD = System.Diagnostics;
15 using System.Threading;
16 using System.Threading.Tasks;
18 namespace Mono.Net.Security
20 delegate AsyncOperationStatus AsyncOperation (AsyncProtocolRequest asyncRequest, AsyncOperationStatus status);
22 class BufferOffsetSize
27 public int TotalBytes;
30 public int EndOffset {
31 get { return Offset + Size; }
34 public int Remaining {
35 get { return Buffer.Length - Offset - Size; }
38 public BufferOffsetSize (byte[] buffer, int offset, int size)
46 public override string ToString ()
48 return string.Format ("[BufferOffsetSize: {0} {1}]", Offset, Size);
52 class BufferOffsetSize2 : BufferOffsetSize
54 public readonly int InitialSize;
56 public BufferOffsetSize2 (int size)
57 : base (new byte [size], 0, 0)
66 Buffer = new byte [InitialSize];
70 public void MakeRoom (int size)
72 if (Remaining >= size)
75 int missing = size - Remaining;
76 if (Offset == 0 && Size == 0) {
77 Buffer = new byte [size];
81 var buffer = new byte [Buffer.Length + missing];
82 Buffer.CopyTo (buffer, 0);
86 public void AppendData (byte[] buffer, int offset, int size)
89 System.Buffer.BlockCopy (buffer, offset, Buffer, EndOffset, size);
94 enum AsyncOperationStatus {
106 class AsyncProtocolRequest
108 public readonly MobileAuthenticatedStream Parent;
109 public readonly BufferOffsetSize UserBuffer;
112 public int CurrentSize;
113 public int UserResult;
115 AsyncOperation Operation;
118 public readonly int ID = ++next_id;
121 public readonly LazyAsyncResult UserAsyncResult;
123 public AsyncProtocolRequest (MobileAuthenticatedStream parent, LazyAsyncResult lazyResult, BufferOffsetSize userBuffer = null)
126 UserAsyncResult = lazyResult;
127 UserBuffer = userBuffer;
130 public bool CompleteWithError (Exception ex)
132 Status = (int)AsyncOperationStatus.Complete;
133 if (UserAsyncResult == null)
135 if (!UserAsyncResult.InternalPeekCompleted)
136 UserAsyncResult.InvokeCallback (ex);
140 [SD.Conditional ("MARTIN_DEBUG")]
141 protected void Debug (string message, params object[] args)
143 Parent.Debug ("AsyncProtocolRequest({0}:{1}): {2}", Parent.ID, ID, string.Format (message, args));
146 internal void RequestRead (int size)
148 var oldStatus = (AsyncOperationStatus)Interlocked.CompareExchange (ref Status, (int)AsyncOperationStatus.WantRead, (int)AsyncOperationStatus.Running);
149 Debug ("RequestRead: {0} {1}", oldStatus, size);
150 if (oldStatus == AsyncOperationStatus.Running)
151 RequestedSize = size;
152 else if (oldStatus == AsyncOperationStatus.WantRead)
153 RequestedSize += size;
154 else if (oldStatus != AsyncOperationStatus.WantWrite)
155 throw new InvalidOperationException ();
158 internal void ResetRead ()
160 var oldStatus = (AsyncOperationStatus)Interlocked.CompareExchange (ref Status, (int)AsyncOperationStatus.Complete, (int)AsyncOperationStatus.WantRead);
161 Debug ("ResetRead: {0} {1}", oldStatus, Status);
164 internal void ResetWrite ()
166 var oldStatus = (AsyncOperationStatus)Interlocked.CompareExchange (ref Status, (int)AsyncOperationStatus.Complete, (int)AsyncOperationStatus.WantWrite);
167 Debug ("ResetWrite: {0} {1}", oldStatus, Status);
170 internal void RequestWrite ()
172 var oldStatus = (AsyncOperationStatus)Interlocked.CompareExchange (ref Status, (int)AsyncOperationStatus.WantWrite, (int)AsyncOperationStatus.Running);
173 Debug ("RequestWrite: {0} {1}", oldStatus, Status);
174 if (oldStatus == AsyncOperationStatus.Running)
176 else if (oldStatus != AsyncOperationStatus.WantRead && oldStatus != AsyncOperationStatus.WantWrite)
177 throw new InvalidOperationException ();
180 internal void StartOperation (AsyncOperation operation)
182 Debug ("Start Operation: {0} {1}", Status, operation);
183 if (Interlocked.CompareExchange (ref Status, (int)AsyncOperationStatus.Initialize, (int)AsyncOperationStatus.NotStarted) != (int)AsyncOperationStatus.NotStarted)
184 throw new InvalidOperationException ();
186 Operation = operation;
188 if (UserAsyncResult == null) {
193 ThreadPool.QueueUserWorkItem (_ => StartOperation ());
196 void StartOperation ()
200 if (UserAsyncResult != null && !UserAsyncResult.InternalPeekCompleted)
201 UserAsyncResult.InvokeCallback (UserResult);
202 } catch (Exception ex) {
203 if (UserAsyncResult == null)
205 if (!UserAsyncResult.InternalPeekCompleted)
206 UserAsyncResult.InvokeCallback (ex);
210 void ProcessOperation ()
212 AsyncOperationStatus status;
214 status = (AsyncOperationStatus)Interlocked.Exchange (ref Status, (int)AsyncOperationStatus.Running);
216 Debug ("ProcessOperation: {0}", status);
218 status = ProcessOperation (status);
220 Debug ("ProcessOperation done: {0}", status);
222 AsyncOperationStatus oldStatus;
223 if (status == AsyncOperationStatus.Complete) {
224 oldStatus = (AsyncOperationStatus)Interlocked.CompareExchange (ref Status, (int)AsyncOperationStatus.FinishWrite, (int)AsyncOperationStatus.WantWrite);
225 if (oldStatus == AsyncOperationStatus.WantWrite) {
226 // We are done, but still need to flush the write queue.
227 status = AsyncOperationStatus.FinishWrite;
232 oldStatus = (AsyncOperationStatus)Interlocked.CompareExchange (ref Status, (int)status, (int)AsyncOperationStatus.Running);
233 Debug ("ProcessOperation done: {0} -> {1}", oldStatus, status);
235 if (oldStatus != AsyncOperationStatus.Running) {
236 if (status == oldStatus || status == AsyncOperationStatus.Continue || status == AsyncOperationStatus.Complete)
239 throw new InvalidOperationException ();
241 } while (status != AsyncOperationStatus.Complete);
244 AsyncOperationStatus ProcessOperation (AsyncOperationStatus status)
246 if (status == AsyncOperationStatus.WantRead) {
247 if (RequestedSize < 0)
248 throw new InvalidOperationException ();
249 else if (RequestedSize == 0)
250 return AsyncOperationStatus.Continue;
252 Debug ("ProcessOperation - read inner: {0}", RequestedSize);
253 var ret = Parent.InnerRead (RequestedSize);
254 Debug ("ProcessOperation - read inner done: {0} - {1}", RequestedSize, ret);
257 return AsyncOperationStatus.ReadDone;
259 RequestedSize -= ret;
261 if (ret == 0 || RequestedSize == 0)
262 return AsyncOperationStatus.Continue;
264 return AsyncOperationStatus.WantRead;
265 } else if (status == AsyncOperationStatus.WantWrite) {
266 Debug ("ProcessOperation - want write");
267 Parent.InnerWrite ();
268 Debug ("ProcessOperation - want write done");
269 return AsyncOperationStatus.Continue;
270 } else if (status == AsyncOperationStatus.Initialize || status == AsyncOperationStatus.Continue) {
271 Debug ("ProcessOperation - continue");
272 status = Operation (this, status);
273 Debug ("ProcessOperation - continue done: {0}", status);
275 } else if (status == AsyncOperationStatus.ReadDone) {
276 Debug ("ProcessOperation - read done");
277 status = Operation (this, status);
278 Debug ("ProcessOperation - read done: {0}", status);
280 } else if (status == AsyncOperationStatus.FinishWrite) {
281 Debug ("ProcessOperation - finish write");
282 Parent.InnerWrite ();
283 Debug ("ProcessOperation - finish write done");
284 return AsyncOperationStatus.Complete;
287 throw new InvalidOperationException ();