Переглянути джерело

Async-ify the remaining WebConnectionStream pieces.

Martin Baulig 12 роки тому
батько
коміт
faaa8a7c94

+ 71 - 23
mcs/class/System/System.Net/HttpWebRequest.cs

@@ -862,19 +862,19 @@ namespace System.Net
 			return EndGetRequestStream (asyncResult);
 		}
 
-		void CheckIfForceWrite ()
+		WebAsyncResult CheckIfForceWrite (AsyncCallback callback, object state)
 		{
 			if (writeStream == null || writeStream.RequestWritten || !InternalAllowBuffering)
-				return;
+				return null;
 #if NET_4_0
 			if (contentLength < 0 && writeStream.CanWrite == true && writeStream.WriteBufferLength < 0)
-				return;
+				return null;
 
 			if (contentLength < 0 && writeStream.WriteBufferLength >= 0)
 				InternalContentLength = writeStream.WriteBufferLength;
 #else
 			if (contentLength < 0 && writeStream.CanWrite == true)
-				return;
+				return null;
 #endif
 
 			// This will write the POST/PUT if the write stream already has the expected
@@ -882,7 +882,9 @@ namespace System.Net
 			// contains data and it has been closed already (xamarin bug #1512).
 
 			if (writeStream.WriteBufferLength == contentLength || (contentLength == -1 && writeStream.CanWrite == false))
-				writeStream.WriteRequest ();
+				return writeStream.WriteRequestAsync (callback, state);
+
+			return null;
 		}
 
 		public override IAsyncResult BeginGetResponse (AsyncCallback callback, object state)
@@ -905,10 +907,36 @@ namespace System.Net
 							"method while a previous call is still in progress.");
 			}
 
-			CheckIfForceWrite ();
 			asyncRead = new WebAsyncResult (this, callback, state);
 			WebAsyncResult aread = asyncRead;
 			initialMethod = method;
+
+			aread.InnerAsyncResult = CheckIfForceWrite (GetResponseAsyncCB, aread);
+			if (aread.InnerAsyncResult == null)
+				GetResponseAsyncCB2 (aread);
+			else
+				Monitor.Exit (locker);
+			return aread;
+		}
+
+		void GetResponseAsyncCB (IAsyncResult ar)
+		{
+			var result = (WebAsyncResult)ar;
+			var innerResult = (WebAsyncResult)result.InnerAsyncResult;
+			result.InnerAsyncResult = null;
+
+			if (innerResult != null && innerResult.GotException) {
+				asyncRead.SetCompleted (true, innerResult.Exception);
+				asyncRead.DoCallback ();
+				return;
+			}
+
+			Monitor.Enter (locker);
+			GetResponseAsyncCB2 ((WebAsyncResult)ar);
+		}
+
+		void GetResponseAsyncCB2 (WebAsyncResult aread)
+		{
 			if (haveResponse) {
 				Exception saved = saved_exc;
 				if (webResponse != null) {
@@ -919,15 +947,15 @@ namespace System.Net
 						aread.SetCompleted (true, saved);
 					}
 					aread.DoCallback ();
-					return aread;
+					return;
 				} else if (saved != null) {
 					Monitor.Exit (locker);
 					aread.SetCompleted (true, saved);
 					aread.DoCallback ();
-					return aread;
+					return;
 				}
 			}
-			
+
 			if (!requestSent) {
 				requestSent = true;
 				redirects = 0;
@@ -936,9 +964,8 @@ namespace System.Net
 			}
 
 			Monitor.Exit (locker);
-			return aread;
 		}
-		
+
 		public override WebResponse EndGetResponse (IAsyncResult asyncResult)
 		{
 			if (asyncResult == null)
@@ -1281,27 +1308,37 @@ namespace System.Net
 				writeStream.SendChunked = false;
 			}
 
-			byte[] requestHeaders = GetRequestHeaders ();
-			WebAsyncResult result = new WebAsyncResult (new AsyncCallback (SetWriteStreamCB), null);
-			writeStream.SetHeadersAsync (requestHeaders, result);
+			try {
+				var result = writeStream.SetHeadersAsync (false, SetWriteStreamCB, null);
+				if (result == null)
+					SetWriteStreamCB (null);
+			} catch (Exception exc) {
+				SetWriteStreamErrorCB (exc);
+			}
+		}
+
+		void SetWriteStreamErrorCB (Exception exc)
+		{
+			WebException wexc = exc as WebException;
+			if (wexc != null)
+				SetWriteStreamError (wexc.Status, wexc);
+			else
+				SetWriteStreamError (WebExceptionStatus.SendFailure, exc);
 		}
 
 		void SetWriteStreamCB (IAsyncResult ar)
 		{
 			WebAsyncResult result = ar as WebAsyncResult;
 
-			if (result.Exception != null) {
-				WebException wexc = result.Exception as WebException;
-				if (wexc != null) {
-					SetWriteStreamError (wexc.Status, wexc);
-					return;
-				}
-				SetWriteStreamError (WebExceptionStatus.SendFailure, result.Exception);
+			if (result != null && result.Exception != null) {
+				SetWriteStreamErrorCB (result.Exception);
 				return;
 			}
 		
 			haveRequest = true;
 
+			WebAsyncResult writeRequestResult = null;
+
 			if (bodyBuffer != null) {
 				// The body has been written and buffered. The request "user"
 				// won't write it again, so we must do it.
@@ -1314,8 +1351,19 @@ namespace System.Net
 			} else if (method != "HEAD" && method != "GET" && method != "MKCOL" && method != "CONNECT" &&
 					method != "TRACE") {
 				if (getResponseCalled && !writeStream.RequestWritten)
-					// FIXME: this is a blocking call on the thread pool that could lead to thread pool exhaustion
-					writeStream.WriteRequest ();
+					writeRequestResult = writeStream.WriteRequestAsync (SetWriteStreamCB2, null);
+			}
+
+			if (writeRequestResult == null)
+				SetWriteStreamCB2 (null);
+		}
+
+		void SetWriteStreamCB2 (IAsyncResult ar)
+		{
+			var result = (WebAsyncResult)ar;
+			if (result != null && result.GotException) {
+				SetWriteStreamErrorCB (result.Exception);
+				return;
 			}
 
 			if (asyncWrite != null) {

+ 72 - 52
mcs/class/System/System.Net/WebConnectionStream.cs

@@ -448,7 +448,7 @@ namespace System.Net
 			return (nb >= 0) ? nb : 0;
 		}
 
-	   	void WriteRequestAsyncCB (IAsyncResult r)
+	   	void WriteAsyncCB (IAsyncResult r)
 		{
 			WebAsyncResult result = (WebAsyncResult) r.AsyncState;
 			result.InnerAsyncResult = null;
@@ -501,7 +501,7 @@ namespace System.Net
 			}
 
 			WebAsyncResult result = new WebAsyncResult (cb, state);
-			AsyncCallback callback = new AsyncCallback (WriteRequestAsyncCB);
+			AsyncCallback callback = new AsyncCallback (WriteAsyncCB);
 
 			if (sendChunked) {
 				requestWritten = true;
@@ -626,38 +626,32 @@ namespace System.Net
 		{
 		}
 
-		internal void SetHeadersAsync (byte[] buffer, WebAsyncResult result)
+		internal WebAsyncResult SetHeadersAsync (bool setInternalLength, AsyncCallback callback, object state)
 		{
 			if (headersSent)
-				return;
+				return null;
 
-			headers = buffer;
-			long cl = request.ContentLength;
 			string method = request.Method;
 			bool no_writestream = (method == "GET" || method == "CONNECT" || method == "HEAD" ||
-						method == "TRACE");
+			                      method == "TRACE");
 			bool webdav = (method == "PROPFIND" || method == "PROPPATCH" || method == "MKCOL" ||
-			               method == "COPY" || method == "MOVE" || method == "LOCK" ||
-			               method == "UNLOCK");
-			if (sendChunked || cl > -1 || no_writestream || webdav) {
+			              method == "COPY" || method == "MOVE" || method == "LOCK" ||
+			              method == "UNLOCK");
+
+			if (setInternalLength && !no_writestream && writeBuffer != null)
+				request.InternalContentLength = writeBuffer.Length;
+
+			if (sendChunked || request.ContentLength > -1 || no_writestream || webdav) {
 				headersSent = true;
+				headers = request.GetRequestHeaders ();
 
-				try {
-					result.InnerAsyncResult = cnc.BeginWrite (request, headers, 0, headers.Length, new AsyncCallback (SetHeadersCB), result);
-					if (result.InnerAsyncResult == null) {
-						// when does BeginWrite return null? Is the case when the request is aborted?
-						if (!result.IsCompleted)
-							result.SetCompleted (true, 0);
-						result.DoCallback ();
-					}
-				} catch (Exception exc) {
-					result.SetCompleted (true, exc);
-					result.DoCallback ();
-				}
-			} else {
-				result.SetCompleted (true, 0);
-				result.DoCallback ();
+				var result = new WebAsyncResult (callback, state);
+				result.InnerAsyncResult = cnc.BeginWrite (request, headers, 0, headers.Length, new AsyncCallback (SetHeadersCB), result);
+				if (result.InnerAsyncResult != null)
+					return result;
 			}
+
+			return null;
 		}
 
 		void SetHeadersCB (IAsyncResult r)
@@ -686,58 +680,84 @@ namespace System.Net
 			get { return requestWritten; }
 		}
 
-		internal void WriteRequest ()
+		internal WebAsyncResult WriteRequestAsync (AsyncCallback callback, object state)
 		{
 			if (requestWritten)
-				return;
+				return null;
 
 			requestWritten = true;
 			if (sendChunked)
-				return;
+				return null;
 
 			if (!allowBuffering || writeBuffer == null)
-				return;
+				return null;
 
-			byte [] bytes = writeBuffer.GetBuffer ();
-			int length = (int) writeBuffer.Length;
+			byte[] bytes = writeBuffer.GetBuffer ();
+			int length = (int)writeBuffer.Length;
 			if (request.ContentLength != -1 && request.ContentLength < length) {
 				nextReadCalled = true;
 				cnc.Close (true);
 				throw new WebException ("Specified Content-Length is less than the number of bytes to write", null,
-							WebExceptionStatus.ServerProtocolViolation, null);
+					WebExceptionStatus.ServerProtocolViolation, null);
 			}
 
-			if (!headersSent) {
-				string method = request.Method;
-				bool no_writestream = (method == "GET" || method == "CONNECT" || method == "HEAD" ||
-							method == "TRACE");
-				if (!no_writestream)
-					request.InternalContentLength = length;
+			var result = new WebAsyncResult (callback, state);
+			result.InnerAsyncResult = SetHeadersAsync (true, WriteRequestAsyncCB, result);
+			if (result.InnerAsyncResult == null)
+				WriteRequestAsyncCB (result);
+			return result;
+		}
+
+		void WriteRequestAsyncCB (IAsyncResult ar)
+		{
+			var result = (WebAsyncResult)ar;
+			var innerResult = (WebAsyncResult)result.InnerAsyncResult;
+			result.InnerAsyncResult = null;
 
-				byte[] requestHeaders = request.GetRequestHeaders ();
-				WebAsyncResult ar = new WebAsyncResult (null, null);
-				SetHeadersAsync (requestHeaders, ar);
-				ar.AsyncWaitHandle.WaitOne ();
-				if (ar.Exception != null)
-					throw ar.Exception;
+			if (innerResult != null && innerResult.GotException) {
+				result.SetCompleted (false, innerResult.Exception);
+				result.DoCallback ();
+				return;
 			}
 
-			if (cnc.Data.StatusCode != 0 && cnc.Data.StatusCode != 100)
+			if (cnc.Data.StatusCode != 0 && cnc.Data.StatusCode != 100) {
+				result.SetCompleted (false, 0);
+				result.DoCallback ();
 				return;
-				
-			IAsyncResult result = null;
+			}
+
+			byte[] bytes = writeBuffer.GetBuffer ();
+			int length = (int)writeBuffer.Length;
+
 			if (length > 0)
-				result = cnc.BeginWrite (request, bytes, 0, length, null, null);
-			
+				result.InnerAsyncResult = cnc.BeginWrite (request, bytes, 0, length, WriteRequestAsyncCB2, result);
+
 			if (!initRead) {
 				initRead = true;
 				WebConnection.InitRead (cnc);
 			}
 
-			if (length > 0) 
-				complete_request_written = cnc.EndWrite (request, false, result);
-			else
+			if (length == 0) {
+				result.SetCompleted (false, 0);
+				result.DoCallback ();
 				complete_request_written = true;
+			}
+		}
+
+		void WriteRequestAsyncCB2 (IAsyncResult ar)
+		{
+			var result = (WebAsyncResult)ar.AsyncState;
+			var innerResult = result.InnerAsyncResult;
+			result.InnerAsyncResult = null;
+
+			try {
+				complete_request_written = cnc.EndWrite (request, false, innerResult);
+				result.SetCompleted (false, 0);
+			} catch (Exception exc) {
+				result.SetCompleted (false, exc);
+			} finally {
+				result.DoCallback ();
+			}
 		}
 
 		internal void InternalClose ()