This is Xamarin's bug tracking system. For product support, please use the support links listed in your Xamarin Account.
Bug 32374 - WebRequest::BeginGetRequestStream incorrectly sets IAsyncResult::CompletedSynchronously to always true
Summary: WebRequest::BeginGetRequestStream incorrectly sets IAsyncResult::CompletedSyn...
Status: VERIFIED FIXED
Alias: None
Product: Class Libraries
Classification: Mono
Component: System (show other bugs)
Version: Trunk
Hardware: PC Mac OS
: --- normal
Target Milestone: 4.6.0 (C8)
Assignee: Marek Safar
URL:
Depends on:
Blocks:
 
Reported: 2015-07-23 16:38 UTC by Jon Goldberger
Modified: 2016-08-24 15:15 UTC (History)
7 users (show)

See Also:
Tags:
Is this bug a regression?: ---
Last known good build:


Attachments

Description Jon Goldberger 2015-07-23 16:38:35 UTC
Created attachment 12199 [details]
Test project

## Description

When HttpWebRequest.BeginGetRequestStream method is used to post http request, then inside AsyncCallback, the IAsyncResult.CompletedSynchronously returned as true (Ideally it should not be). And this bug leads to problem in odata library, as they are using a pattern to handle situation in case call is completed synchronously with BeginGetRequestStream method. (Prescribed by network stack team. Refer these links, MSDN<https://social.msdn.microsoft.com/Forums/vstudio/en-US/bc9d38c1-7ebc-42b2-a519-31728819b108/networkstreambeginread-sometimes-calls-the-async-callback-synchronously?forum=netfxbcl>, StackOverflow<http://stackoverflow.com/questions/1372053/asynccallback-completedsynchronously>).

Behavior is correct on Windows/Windows phone


## Steps to reproduce

1. Open the attached test project.

2. Open the AppDelegate.cs file.

3. Place breakpoints lines 69 (`if (result.CompletedSynchronously)`) and 81 (`if (result.CompletedSynchronously)`).

4. Launch the app to a simulator/device.

5. Note the value of result.CompletedSynchronously when debugger breaks on line 69. Should be false.

6. Press Continue.

Expected result: result.CompletedSynchronously should still be false.

Actual Result: result.CompletedSynchronously is true. 

## Notes

There is an in depth description of this issue by the developer in the comments in the AppDelegate file. This behavior appears to differ from pure .NET behavior.

I tested on an iPhone 5s simulator running iOS 8.4

## My environment:

=== Xamarin Studio ===

Version 5.9.4 (build 5)
Installation UUID: 2dc9022f-f9a8-424f-8284-bf224cbbfde0
Runtime:
	Mono 4.0.2 ((detached/c99aa0c)
	GTK+ 2.24.23 (Raleigh theme)

	Package version: 400020005

=== Xamarin.Android ===

Version: 5.1.4.16 (Business Edition)
Android SDK: /Users/apple/Library/Developer/Xamarin/android-sdk-mac_x86
	Supported Android versions:
		2.3    (API level 10)
		4.0.3  (API level 15)
		4.1    (API level 16)
		4.2    (API level 17)
		4.3    (API level 18)
		4.4    (API level 19)
		4.4.87 (API level 20)
		5.0    (API level 21)
Java SDK: /usr
java version "1.7.0_79"
Java(TM) SE Runtime Environment (build 1.7.0_79-b15)
Java HotSpot(TM) 64-Bit Server VM (build 24.79-b02, mixed mode)

=== Xamarin Android Player ===

Version: Unknown version
Location: /Applications/Xamarin Android Player.app

=== Apple Developer Tools ===

Xcode 6.4 (7720)
Build 6E35b

=== Xamarin.iOS ===

Version: 8.10.3.2 (Business Edition)
Hash: 8b265d6
Branch: master
Build date: 2015-06-30 15:19:13-0400

=== Xamarin.Mac ===

Version: 2.0.2.35 (Business Edition)

=== Build Information ===

Release ID: 509040005
Git revision: 8010a90f6e246b32364e3fb46ef2c9d1be9c9a2b
Build date: 2015-06-08 16:52:06-04
Xamarin addins: 7e93e9c3503f28770f23ce1b7eafd829919f18e8

=== Operating System ===

Mac OS X 10.10.4
Darwin Jons-iMac.local 14.4.0 Darwin Kernel Version 14.4.0
    Thu May 28 11:35:04 PDT 2015
    root:xnu-2782.30.5~1/RELEASE_X86_64 x86_64
Comment 2 Marek Safar 2015-08-17 11:55:40 UTC
I cannot reproduce the issue using master XI.

result.CompletedSynchronously is false at lines 69 and 74. As there is no code executed perhaps something in background should finish first but that's not clear from the sample.
Comment 3 Jon Goldberger 2015-09-04 15:51:54 UTC
You said that it was still false at line 74, but the check was to see if it was still false on line 81. Can you verify this?
Comment 6 Marek Safar 2016-02-10 14:16:08 UTC
Console app which reproduces the issue

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;

namespace CompletedTest
{
	class MainClass
	{
		public static void Main (string [] args)
		{
			int i = 0;
			var x = new MainClass ();
			x.Foo ();

			Console.ReadKey ();
		}

		// class-level declarations

		//
		// This method is invoked when the application has loaded and is ready to run. In this 
		// method you should instantiate the window, load the UI into it and then make the window
		// visible.
		//
		// You have 17 seconds to return from this method, or iOS will terminate your application.
		//
		public void Foo ()
		{
			var request = (HttpWebRequest)WebRequest.Create ("http://www.microsoft.com/"); // Any working endpoint with POST VERB
			request.Method = "POST";

			IAsyncResult result = request.BeginGetRequestStream (ResponseReady, request);

			// CompletedSynchronously (for System.Net networking stack) means "was the operation completed before the first time
			// that somebody asked if it was completed synchronously"? They do this because some of their asynchronous operations
			// (particularly those in the Socket class) will avoid the cost of capturing and transferring the ExecutionContext
			// to the callback thread by checking CompletedSynchronously, and calling the callback from within BeginXxx instead of
			// on the completion port thread if the native winsock call completes quickly.

			// For other operations however (notably those in HttpWebRequest), they use the same underlying IAsyncResult implementation,
			// but do NOT check CompletedSynchronously before returning from BeginXxx.  That means that CompletedSynchronously will
			// be false if and only if you checked it from the thread which called BeginXxx BEFORE the operation completed.  It will
			// then continue to be false even after IsCompleted becomes true.

			// Note that CompletedSynchronously == true does not guarantee anything about how much of your callback has executed.

			// The usual pattern for handling synchronous completion is that both the caller and callback should check CompletedSynchronously.
			// If its true, the callback should do nothing and the caller should call EndRead and process the result.
			// This guarantees that the caller and callback are not accessing the stream or buffer concurrently without the need
			// for explicit synchronization between the two.

			// This pattern prescribed by networking team, due to the unexpected behaviors of IAsyncResult.CompletedSynchronously in the 
			// System.Net networking stack, we have to make async calls to their APIs using the specific pattern they've . 
			// This method runs in the caller thread and invokes the BeginXXX methods.  It then checks IAsyncResult.CompletedSynchronously 
			// and if it is true, we invoke the callback in the caller thread.
			// This is the action that invokes the BeginXXX method. Note we MUST use our special callback from GetDataServiceAsyncCallback()
			// when invoking the async call.
			// More info: http://stackoverflow.com/questions/1372053/asynccallback-completedsynchronously
			if (result.CompletedSynchronously) {
				this.SendData (result, request);
			}

			return;
		}

		private void ResponseReady (IAsyncResult result)
		{
			// Here is the bug; After 'request.BeginGetRequestStream' call above CompletedSynchronously was false and here it becomes true under Xamarin iOS and Android.
			// This works fine in Windows/Windows phone platform.
			if (result.CompletedSynchronously) {
				// If true, It doesn't close the stream and connection times out.
				return;
			} else {
				this.SendData (result, ((HttpWebRequest)result.AsyncState));
			}
		}

		private void SendData (IAsyncResult result, HttpWebRequest request)
		{
			using (Stream postStream = request.EndGetRequestStream (result)) {
				var data = Encoding.UTF8.GetBytes ("Test");

				postStream.Write (data, 0, data.Length);
			}
		}
	}
}


The problem is in SimpleAsyncResult::Run which always sets CompletedSynchronously. Simply changing that won't work as RunWithLock does not work properly without this assumption.

Reassigning to Martin who wrote this code (it does not make much sense to me).
Comment 8 Shalabh 2016-08-05 22:54:06 UTC
Hello Colleagues,

My name is Shalabh Jain and I work for the Microsoft AX Retail team. My colleague created this bug some time back and we are now blocked because of it. Can you please prioritize this bug and provide us with an ETA?

Regards,
Shalabh
Comment 9 Rodrigo Kumpera 2016-08-08 15:24:38 UTC
Hey Marek,

Can your team take a look at this one?
Comment 10 Marek Safar 2016-08-17 11:57:12 UTC
Fixed in master and Mono 4.6
Comment 11 Shalabh 2016-08-18 00:07:53 UTC
Hi Marek,

Thanks for the fix, can you please let me know how and when can we get this fix?

Regards,
Shalabh
Comment 12 Marek Safar 2016-08-19 12:26:13 UTC
As I wrote in comment above it'll be included in Mono 4.6 based releases. The release process is not in my hands but monitor Xamarin alpha channels it should appear there soon
Comment 13 Sunil Kumar 2016-08-24 15:15:15 UTC
************************************************************
Reproduce Status:
************************************************************

I have tried to reproduce this issue with latest stable XS 6.0.2.73 + Mono 4.4.2.11 and able to reproduce this issue using console App given in comment 6. The value of result.CompletedSynchronously when debugger breaks on line 75 is   'true' as shown in screencast: http://www.screencast.com/t/Hc6KAc6wJg9O

************************************************************
Verify Status
************************************************************

I have checked this issue with latest C8 XS 6.1.0.5365 + Mono 4.6.0.167 and Master XS 6.2.0.370 + Mono 4.7.0.279 using console App given in comment 6 and observed that now this issue is working fine. The value of result.CompletedSynchronously when debugger breaks on line 75 is 'false'.

Screencast(with C8 builds): http://www.screencast.com/t/Mj8f2rnUvTIM
Screencast(with Master builds): http://www.screencast.com/t/r63uGHKmXGp1

Environment info:
Cycle8: https://gist.github.com/sunil360/2203c2db60b0cb5c04155e4b99c81cf4
Master: https://gist.github.com/sunil360/6631f46447f4608cfbcb1822a7073394

Hence closing this issue marking its status as VERIFIED.

Note You need to log in before you can comment on or make changes to this bug.