Bug 909 - Threadpool deadlock on 2.10.5
Summary: Threadpool deadlock on 2.10.5
Status: RESOLVED INVALID
Alias: None
Product: Runtime
Classification: Mono
Component: General ()
Version: unspecified
Hardware: PC Mac OS
: --- blocker
Target Milestone: ---
Assignee: Gonzalo Paniagua Javier
URL:
Depends on:
Blocks:
 
Reported: 2011-09-19 13:56 UTC by Miguel de Icaza [MSFT]
Modified: 2011-09-19 15:47 UTC (History)
1 user (show)

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


Attachments
Compilable test case (9.94 KB, application/octet-stream)
2011-09-19 14:05 UTC, Miguel de Icaza [MSFT]
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 GitHub or Developer Community 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 INVALID

Description Miguel de Icaza [MSFT] 2011-09-19 13:56:53 UTC
The following sample hangs on the second or third call.

using System;
using System.IO;
using System.Net;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Collections;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO.Compression;
using System.Threading;

namespace GoogleMusic
{
	public class Api
	{
		string cookiesFilePath = "";
		BinaryFormatter bf = new BinaryFormatter ();
		string gpcaz = null;
		string galx = null;
		
		public bool IsSignedIn{ get; private set; }
		
		public Api (string cookiesFilePath = "")
		{
			
			this.cookiesFilePath = cookiesFilePath ?? "";
			//LoadCookies ();
		}

		string serviceRoot = "http://music.google.com/music/services/";

		public void SignIn (string email, string password, Action<bool> signedIn)
		{
			var success = signIn (email, password);
			if (signedIn != null)
				signedIn (success);
		}

		string sCookie = "";
		static CookieContainer cookieContainer = new CookieContainer ();

		private bool signIn (string email, string password)
		{			
			
			Console.WriteLine(email + " : " + password);
			
	
			try {	
				
				
				HttpWebRequest PreRequest = (HttpWebRequest)WebRequest.Create ("https://accounts.google.com/ServiceLogin?service=sj&passive=1209600&continue=http://music.google.com/music/listen&followup=http://music.google.com/music/listen");
				PreRequest.CookieContainer = cookieContainer;
				PreRequest.AllowAutoRedirect = true;
				
				PreRequest.KeepAlive = true;
				HttpWebResponse PreResponse = (HttpWebResponse)PreRequest.GetResponse ();
				Stream PreResponseStream = PreResponse.GetResponseStream ();
				PreResponse.Cookies = PreRequest.CookieContainer.GetCookies (PreRequest.RequestUri);
				Encoding PreEnc = System.Text.Encoding.UTF8;
				StreamReader PreResponseStreamReader = new StreamReader (PreResponse.GetResponseStream (), PreEnc, true);
				String PreMyHTML = PreResponseStreamReader.ReadToEnd ();
				
			//	var html3 = HttpsGet ("https://accounts.google.com/ServiceLogin?service=sj&passive=1209600&continue=http://music.google.com/music/listen&followup=http://music.google.com/music/listen", "");
				//Console.WriteLine(PreMyHTML);
				String Pattern = "name=\"GALX\"[\\s]*value=\"([a-zA-Z0-9-_\\.]*)\"";
				Match MyMatch = Regex.Match (PreMyHTML, Pattern);
				String GALX = MyMatch.Groups [1].ToString ();

				var html2 = PostForm ("https://accounts.google.com/ServiceLogin", new Dictionary<string,string> () {				
				{"ltmpl", "es2st"},
			    {"pstMsg", "1"},
			    {"dnConn", ""},
			    {"service", "sj"},
				{"continue","http://music.google.com/music/listen"},
				{"followup","http://music.google.com/music/listen"},
				//{"dsh", "-2527351217389807091"},
				{"passive", "1209600"},
			    {"hl", "en-US"},
				//{"ltmpl", "es2st"},
			    {"timeStmp", ""},
			    {"secTok", ""},
			    {"GALX", GALX},
			    {"Email", email},
			    {"Passwd", password},
			    {"PersistentCookie", "yes"},
			    {"rmShown", "1"},
			    {"signIn", "Sign in"},
				{"asts", ""},
			});
				
		
				if (html2.Contains ("cookie functionality is turned off. Please turn it on."))
					throw new Exception ("Error logging in");
			
				if (string.IsNullOrEmpty (sCookie))
					sCookie = GetSessionCookie ("http://music.google.com/music/listen");
				var status1 = PostForm (serviceRoot + "getstatus?u=0&" + sCookie,new Dictionary<string,string>());
				try {
					//Console.WriteLine("status: " + jo);
					//progress.Hide (true);
					IsSignedIn = true;
					//if (Settings.LastUpdateCompleted.AddMinutes (60) < DateTime.UtcNow)
					//	GetSongs ();
				} catch (Exception ex) {
					IsSignedIn = false;
					throw new Exception ("Invalid Username/Password");
				}
				///Console.WriteLine(html2);
				//SaveCookies ();	
				//progress.Hide (true);
				//Util.PopNetworkActive ();
				return IsSignedIn;
			} catch (Exception ex) {
				
				
				if (ex.Message.Contains ("NameResolutionFailure")) {
					Console.WriteLine ("Failed: NRF");
				}
				//Console.WriteLine("Reachability status" + Reachability.InternetConnectionStatus());
				//Util.PopNetworkActive ();
				throw ex;
				return false;	
			}
		}
		
		private bool GetSongsIfNeeded ()
		{
			string status = PostToGoogle (serviceRoot + "getstatus?u=0&" + sCookie,new Dictionary<string,string>());

			try {
				//if (jo.ContainsKey ("availableTracks"))
				//	Settings.AvailableSongs = jo ["availableTracks"];
				//Console.WriteLine("status: " + jo);
				IsSignedIn = true;
				//if (Settings.AvailableSongs != Settings.SongsCount)
				//	GetSongs ();
				//else					
				//	GetPlaylists ();
				return true;
			} catch (Exception ex) {
				Console.WriteLine (ex);
			}
			return false;
			
		}

		string PostForm (string url, string referer, IDictionary<string, string> form)
		{
			var formEncoded = string.Join ("&", (from i in form 
				select Uri.EscapeDataString (i.Key) + "=" + Uri.EscapeDataString (i.Value)).ToArray ());
			var content = Encoding.UTF8.GetBytes (formEncoded);
			var req = CreateRequest (url);
			req.Method = "POST";
			req.ContentType = "application/x-www-form-urlencoded";
			req.ContentLength = content.Length;			
			if (!string.IsNullOrEmpty (referer)) {
				req.Referer = referer;
			}
			using (var s = req.GetRequestStream ()) {
				s.Write (content, 0, content.Length);
			}
			return ReadResponseText (req);
		}
		
		string ReadResponseText (HttpWebRequest req)
		{
			using (var resp = (HttpWebResponse)req.GetResponse ()) {
				
				using (var stream = resp.GetResponseStream()) {
					StringBuilder sb = new StringBuilder ();
					Byte[] buf = new byte[8192];
					string tmpString = null;
					int count = 0;
					do {
						count = stream.Read (buf, 0, buf.Length);
						if (count != 0) {
							tmpString = Encoding.ASCII.GetString (buf, 0, count);
							sb.Append (tmpString);
						}
					} while (count > 0);
					return sb.ToString ();
				}
			}
		}
		
			
		string PostForm (string url, IDictionary<string, string> form)
		{ 
			
			return PostForm (url, "", form);
		}
		
		string PostToGoogle (string url, IDictionary<string, string> form)
		{
			var formEncoded = string.Join ("&", (from i in form 
				select Uri.EscapeDataString (i.Key) + "=" + Uri.EscapeDataString (i.Value)).ToArray ());
			
			
			var content = Encoding.UTF8.GetBytes (url);
			var req = CreateGoogleRequest (url);
			req.Method = "POST";
			req.ContentType = "application/x-www-form-urlencoded; charset=UTF-8";
			req.ContentLength = content.Length;
			//using (var s = req.GetRequestStream ()) {
			//	s.Write (content, 0, content.Length);
			//}
			var theReturn = ReadResponseText (req);
			return theReturn;
		}

		public static IDictionary<string, string> DefaultHeaders (string url)
		{
			
			
			//var cookies = cookieContainer.GetCookieHeader (new Uri (url));
			return new Dictionary<string,string> () {
					{"Content-Type","application/x-www-form-urlencoded"},
					{"Accept","*/*"},
					{"Accept-Language","en-us"},
					{"Referer","http://music.google.com/music/listen#start_pl"},
					{"Accept-Encoding","gzip, deflate"},
					{"User-Agent","Mozilla/5.0 (iPhone Simulator; U; CPU iPhone OS 4_3 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8F192 Safari/6533.18.5"},
					{"Host","music.google.com"},
					{"Pragma","no-cache"},
					{"Connection","Keep-Alive"},
			//{"Cookie",cookies},
				};
			
		}

		HttpWebRequest CreateRequest (string url, string referer = "")
		{
			var req = (HttpWebRequest)WebRequest.Create (url);
			//req.UserAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6) Mono/2.10 (HTML5, like Gecko) GooglePlus/1.0";
			// G+ is picky about User-Agents. So anti-web.
			req.UserAgent = "Mozilla/5.0 (iPhone Simulator; U; CPU iPhone OS 4_3 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8F192 Safari/6533.18.5";
			req.Accept = "application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5;application/json;";
			req.Headers.Add ("Accept-Charset", "utf-8");
			req.Headers.Add (HttpRequestHeader.AcceptEncoding, "gzip,deflate");
			req.AllowAutoRedirect = true;
			req.ProtocolVersion = HttpVersion.Version11;
			req.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
			req.CookieContainer = cookieContainer;
			if (!string.IsNullOrEmpty (referer)) {
				req.Referer = referer;
			}
			return req;
		}
		
		HttpWebRequest CreateGoogleRequest (string url)
		{
			var req = (HttpWebRequest)WebRequest.Create (url);
			//req.UserAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6) Mono/2.10 (HTML5, like Gecko) GooglePlus/1.0";
			// G+ is picky about User-Agents. So anti-web.
			req.UserAgent = "Mozilla/5.0 (iPhone Simulator; U; CPU iPhone OS 4_3 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8F192 Safari/6533.18.5";
			req.Accept = "application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5;application/json;";
			req.Headers.Add ("Accept-Charset", "utf-8");
			req.Headers.Add (HttpRequestHeader.AcceptEncoding, "gzip,deflate");
			req.AllowAutoRedirect = true;
			req.ProtocolVersion = HttpVersion.Version11;
			req.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
			req.CookieContainer = cookieContainer;
			req.Referer = "http://music.google.com/music/listen";
			req.KeepAlive = true;
			return req;
		}
		
		string GetSessionCookie (string url)
		{
			foreach (Cookie cookie in cookieContainer.GetCookies(new Uri("http://music.google.com"))) {
				if (cookie.Name == "xt")
					return cookie.ToString ();
			}
			var req = CreateRequest (url, "");
			var resp = (HttpWebResponse)req.GetResponse ();
			foreach (var cookie in resp.Cookies) {
				var cookieString = cookie.ToString ();
				if (cookieString.Contains ("xt="))
					return cookieString;
			}
			return "";
		}

	
	}

	class X {
	static void Main ()
	{
		var api = new Api ("");
		api.SignIn ("foo", "bar", (x) => {
			});
		api.SignIn ("foo", "bar", (x) => {
			});
		api.SignIn ("foo", "bar", (x) => {
			});
		api.SignIn ("foo", "bar", (x) => {
			});
		api.SignIn ("foo", "bar", (x) => {
			});
	}
	}
}
Comment 1 Miguel de Icaza [MSFT] 2011-09-19 14:05:25 UTC
Created attachment 431 [details]
Compilable test case
Comment 2 Gonzalo Paniagua Javier 2011-09-19 14:17:50 UTC
Output running the test case on MS:

C:\Users\gonzalo\Downloads>foo
foo : bar
foo : bar

Unhandled Exception: System.Net.WebException: The operation has timed out
   at GoogleMusic.Api.signIn(String email, String password)
   at GoogleMusic.X.Main()
Comment 3 Gonzalo Paniagua Javier 2011-09-19 15:47:25 UTC
For a sign in requests there are at least 7 requests generated. The problem is that there are 2(3?) connection groups created based in the host/port of each request and the requests from one connection group are waiting on the ones in the other to finish and the other way around.

The workaround until a new release with higher default connection limit comes out is:

  System.Net.ServicePointManager.DefaultConnectionLimit = XX;

where XX has to be >= 3