Bug 20974 - HttpClient stops working after cancellation
Summary: HttpClient stops working after cancellation
Status: RESOLVED FIXED
Alias: None
Product: Android
Classification: Xamarin
Component: Mono runtime / AOT Compiler ()
Version: 4.12.4
Hardware: PC Windows
: Normal normal
Target Milestone: ---
Assignee: Marek Habersack
URL:
Depends on:
Blocks:
 
Reported: 2014-06-30 03:45 UTC by Miha Markic
Modified: 2015-12-14 10:02 UTC (History)
4 users (show)

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


Attachments
Sample project (3.08 MB, application/zip)
2014-06-30 03:46 UTC, Miha Markic
Details


Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.


Please create a new report on Developer Community or GitHub with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:
Status:
RESOLVED FIXED

Description Miha Markic 2014-06-30 03:45:07 UTC
When creating tasks for like 1000 concurrent downloads, then immediately cancelling them and create another 1000 tasks, the new HttpClient GetAsync stops and throws internal exception after a TimeOut (while inital batch gets cancelled correctly). It doesn't happen 100% of the time but with high batch count and slow downloads the chances increase.


Steps to reproduce:
1. Open attached solution
2. Go to ClassLibary1.Class1 and use the following URL to download from: https://download.mozilla.org/?product=firefox-30.0-SSL&os=linux&lang=ach
(it should be one that takes time to finish, in this case Firefox download works perfectly. Try others if sample doesn't throw)
3. Run the app (emulator or device)
4. Hit button and wait

It starts 1000 downloads, then cancells them and starts another round of 1000. If error manifests than it will stop in the catch section of Download method:

if (round == 3)
    Debugger.Break();

Note that this TaskCanceledException is thrown internally, not requested for second batch. After that it seems that HttpClient won't recover anymore.


Below is code.
 
public Task MassiveDownload(CancellationToken ct, int round)
        {
            List<Task> tasks = new List<Task>();
            for (int i = 0; i < 1000; i++)
            {
                tasks.Add(Download(ct, i, round));
            }
            return Task.WhenAll(tasks);
        }

        public async Task Download(CancellationToken ct, int i, int round)
        {
            Debug.WriteLine("Starting " + round + ":" + i);
            HttpClient client = new HttpClient();
            try
            {
                // PLACE YOUR URL BELOW
                var msg = await client.GetAsync("https://download.mozilla.org/?product=firefox-30.0-SSL&os=linux&lang=ach", ct);
                Debug.WriteLine("Response for " + round + ":" + i + " = " + msg.StatusCode);
                if (msg.IsSuccessStatusCode)
                {
                    if (msg.IsSuccessStatusCode)
                    {
                        using (StreamContent sc = (StreamContent)msg.Content)
                        using (Stream stream = await sc.ReadAsStreamAsync())
                        using (MemoryStream target = new MemoryStream())
                        {

                            await stream.CopyToAsync(target, 4096);
                        }
                    }
                }
            }
            catch (TaskCanceledException ex)
            {
                Debug.WriteLine("Cancelled " + round + ":" + i);
                if (round == 3)
                    Debugger.Break();
            }
        }

and here is the calling code
                Class1 c1 = new Class1();
                cts = new CancellationTokenSource();
                var ignore = c1.MassiveDownload(cts.Token, count++);

                cts.Cancel();
                c1 = new Class1();
                cts = new CancellationTokenSource();
                ignore = c1.MassiveDownload(cts.Token, count++);

and here is the exception which in unexpected:
System.Threading.Tasks.TaskCanceledException: The Task was canceled
  at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x00000] in <filename unknown>:0 
  at System.Runtime.CompilerServices.TaskAwaiter`1[System.Net.Http.HttpResponseMessage].GetResult () [0x00000] in <filename unknown>:0 
  at System.Net.Http.HttpClientHandler+<SendAsync>c__async0.MoveNext () [0x00000] in <filename unknown>:0 
--- End of stack trace from previous location where exception was thrown ---
  at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x00000] in <filename unknown>:0 
  at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1+ConfiguredTaskAwaiter[System.Net.Http.HttpResponseMessage].GetResult () [0x00000] in <filename unknown>:0 
  at System.Net.Http.HttpClient+<SendAsyncWorker>c__async0.MoveNext () [0x00000] in <filename unknown>:0 
--- End of stack trace from previous location where exception was thrown ---
  at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x00000] in <filename unknown>:0 
  at System.Runtime.CompilerServices.TaskAwaiter`1[System.Net.Http.HttpResponseMessage].GetResult () [0x00000] in <filename unknown>:0 
  at ClassLibrary1.Class1+<Download>d__0.MoveNext () [0x000b4] in f:\Temp\TempProjects\App9\ClassLibrary1\Class1.cs:31
Comment 1 Miha Markic 2014-06-30 03:46:06 UTC
Created attachment 7231 [details]
Sample project

Replace URL with https://download.mozilla.org/?product=firefox-30.0-SSL&os=linux&lang=ach or other "slow" downloads
Comment 2 Miha Markic 2014-06-30 03:47:40 UTC
(both attachments are same, sorry for double post)
Comment 3 Shruti 2014-07-14 06:38:50 UTC
I have tried this issue with following same steps as mentioned in description section.
During reproducing this issue, I have observed little different behavior. 
1. It starts 1000 downloads.
2. Then again it starts with another round of 1000 downloads.
3. Then It Cancelled downloads.

After 2-3 minutes, it stop in the catch section of Download method:
if (round == 3)
    Debugger.Break();

Screencast Info :http://www.screencast.com/t/KhGAZZ59mgp4

Supplement Info ->
Exception Log : https://gist.github.com/shrutis360/d8a70cd6009fe72f290a
Stack Log: https://gist.github.com/shrutis360/d16c9a18a45b8be7e4c0
Output Log: https://gist.github.com/shrutis360/9405be9204ff974d9fe7
Android Log : https://gist.github.com/shrutis360/1acd5fcf98fae5e34936
adb Log : https://gist.github.com/shrutis360/3d6394a006c65306a1d8

Environment Info : 
=== Xamarin Studio ===

Version 5.1.2 (build 0)
Installation UUID: a360e1d3-8a88-44f7-9f4a-0e675f61de0c
Runtime:
	Microsoft .NET 4.0.30319.18408
	GTK+ 2.24.22 (MS-Windows theme)
	GTK# 2.12.25

=== Xamarin.Android ===

Version: 4.12.6 (Trial Edition)
Android SDK: D:\oldSDK\AndroidSDK
	Supported Android versions:
		1.6   (API level 4)
		2.1   (API level 7)
		2.2   (API level 8)
		2.3   (API level 10)
		3.1   (API level 12)
		3.2   (API level 13)
		4.0   (API level 14)
		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)
Java SDK: C:\Program Files (x86)\Java\jdk1.6.0_31
java version "1.6.0_31"
Java(TM) SE Runtime Environment (build 1.6.0_31-b05)
Java HotSpot(TM) Client VM (build 20.6-b01, mixed mode)

=== Build Information ===

Release ID: 501020000
Git revision: 4574b1e5cf09f2e95e486c259bd9267a68c77cb3
Build date: 2014-07-09 11:47:18-04
Xamarin addins: fdea6fe3e36cda39291b39c81133e409c6d6f092

=== Operating System ===

Windows 6.2.9200.0 (64-bit)
Comment 5 Rupert Rawnsley 2015-11-03 10:38:51 UTC
There are definitely problems with the Mono HttpClient implementation, but in this example it appears that some of the 1000 HTTP requests made during round 3 are taking longer than 100 seconds, which is the default timeout for HttpClient.GetAsync. This timeout is the legitimate cause of the TaskCanceledException. Try setting client.Timeout = Timeout.InfiniteTimeSpan and repeat the test.

Therefore I would say that this is "as expected" under slow network conditions. That said, I am seeing some very odd behaviour in the example code so I may post a follow up if I can add anything useful.
Comment 6 Rupert Rawnsley 2015-11-04 07:06:26 UTC
I've posted a modified version of the sample project to the Xamarin Forums to solicit opinion: https://forums.xamarin.com/discussion/55223/shared-httpclient-instance-fails-after-cancellation
Comment 7 Rupert Rawnsley 2015-11-11 06:04:22 UTC
Raised a new bug report in case it's a different problem: https://bugzilla.xamarin.com/show_bug.cgi?id=35779
Comment 8 Marek Habersack 2015-12-14 10:02:39 UTC
This bug is fixed in the current stable release of Xamarin.Android (6.0.0-34)

Note that the test application will *still* receive the ThreadCanceledExcetption for the 2nd run of the downloader. This is expected as the task is cancelled after the configured HttpClient.Timeout - if you set the timeout to infinity, no exception is thrown. This is expected and correct behavior.