[Mono-bugs] [Bug 71217][Wis] New - Socket.BeginSend & Other Async Methods Block for Many Simultaneous calls

bugzilla-daemon@bugzilla.ximian.com bugzilla-daemon@bugzilla.ximian.com
Wed, 12 Jan 2005 18:11:21 -0500 (EST)


Please do not reply to this email- if you want to comment on the bug, go to the
URL shown below and enter your comments there.

Changed by scott@imeem.com.

http://bugzilla.ximian.com/show_bug.cgi?id=71217

--- shadow/71217	2005-01-12 18:11:21.000000000 -0500
+++ shadow/71217.tmp.21543	2005-01-12 18:11:21.000000000 -0500
@@ -0,0 +1,256 @@
+Bug#: 71217
+Product: Mono: Class Libraries
+Version: 1.1
+OS: Red Hat 9.0
+OS Details: 
+Status: NEW   
+Resolution: 
+Severity: 
+Priority: Wishlist
+Component: System
+AssignedTo: mono-bugs@ximian.com                            
+ReportedBy: scott@imeem.com               
+QAContact: mono-bugs@ximian.com
+TargetMilestone: ---
+URL: 
+Cc: 
+Summary: Socket.BeginSend & Other Async Methods Block for Many Simultaneous calls
+
+Please fill in this template when reporting a bug, unless you know what you
+are doing.
+Description of Problem:
+When using BeginSend/BeingReceive/BeginConnect on sockets the Mono
+implementation essentialy grabs a thread from a thread pool and tasks it
+with performing the equivalent blocking version of the same call. When we
+try to do this for many sockets at once the thread pool runs out of threads
+and the calls aren't serviced. This makes it hard to write servers which
+handle many sockets using the Async methods (and if you ever get into a
+discussion with .Net programmers on how to write large scale server
+applications they'll insist that the Begin/End async methods are the only
+mechanism worth using)
+
+
+Steps to reproduce the problem:
+1. This code will demo the problem, essentially it opens about 1000 sockets
+and starts a BeginReceive() call on all of them in sequence, then *in
+reverse order* we write data to all the sockets. 
+ 
+using System;
+using System.Collections;
+using System.Net;
+using System.Net.Sockets;
+using System.Threading;
+
+namespace TestMono
+{
+	/// <summary>
+	/// Summary description for Class1.
+	/// </summary>
+	class Class1
+	{
+
+		static int portNo = 9999;
+		static bool keepWorking = false;
+		static ArrayList sockList = new ArrayList();
+		static AsyncCallback callback = new AsyncCallback(AsyncLoop);
+
+		/// <summary>
+		/// The main entry point for the application.
+		/// </summary>
+		[STAThread]
+		static void Main(string[] args)
+		{
+			//
+			// TODO: Add code to start application here
+			//
+
+			Thread serverThread = new Thread(new ThreadStart(ServerThread));
+			serverThread.Start();
+			Thread connectThread = new Thread(new ThreadStart(ConnectThread));
+			connectThread.Start();
+
+			SelectThread();
+
+		}
+
+
+		static void ServerThread() 
+		{
+
+			Socket sock = new
+Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
+			IPEndPoint iep = new IPEndPoint(IPAddress.Any,portNo);
+			sock.Bind(iep);
+			sock.Listen(500);
+			// workphase1 = other thread connects 
+			keepWorking = true;
+			while (keepWorking) 
+			{
+				Socket newSock = sock.Accept();
+				sockList.Add(newSock);
+				//Console.WriteLine("Server accepting Socket");
+			}
+			Console.WriteLine("closing server Socket");
+			// done test - close them all
+			sock.Close();
+		}
+
+
+		static void ConnectThread() 
+		{
+			for(int i = 0;i<500;i++)
+			{
+				try 
+				{
+					Socket sock = new
+Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
+					IPEndPoint iep = new IPEndPoint(IPAddress.Parse("127.0.0.1"),portNo);
+					sock.Connect(iep);
+					if(sock.Connected) 
+					{
+						//Console.WriteLine("Added Socket " + i);
+						sockList.Add(sock);
+					} 
+					else 
+					{
+						Console.WriteLine("Socket " + i + " did not get connected");
+					}
+					Thread.Sleep(10);
+				} 
+				catch (Exception e) 
+				{
+					Console.WriteLine("Socket " + i + " threw exception" +e.ToString());
+				}
+
+			}			
+			keepWorking = false;
+		}
+
+		struct sockBuf 
+		{
+			public Socket sock;
+			public Byte[] buf;
+			public int id;
+		}
+
+		static void DoAsyncTest() 
+		{
+			// create a lot of Async readers
+			// then write in descending order
+			for( int i = 0 ; i < sockList.Count ; i++) 
+			{
+				Socket sock = (Socket) sockList[i];
+				sockBuf sb = new sockBuf();
+				sb.sock = sock;
+				sb.buf = new byte[16];
+				sb.id = i;
+				sock.BeginReceive(sb.buf,0,16,SocketFlags.None,callback,sb);
+			}
+			byte[] buf = new byte[16];
+			for(int i = sockList.Count -1 ; i >= 0 ; i--) 
+			{
+				Socket sock = (Socket) sockList[i];
+				Console.WriteLine("Writing 16 bytes to id " + i);
+				sock.Send(buf);
+				Thread.Sleep(1000);
+			}
+
+		}
+
+		static void AsyncLoop(IAsyncResult iar) 
+		{
+			sockBuf sb = (sockBuf) iar.AsyncState;
+			Socket sock = sb.sock;
+			try 
+			{
+				int recv = sock.EndReceive(iar);
+				Console.WriteLine("AsyncLoop got " + recv + " bytes on socket " + sb.id);
+			} 
+			catch 
+			{
+				Console.WriteLine("Got exception from EndReceive");
+				return;
+			}
+			if(keepWorking) 
+			{
+				sock.BeginReceive(sb.buf,0,16,SocketFlags.None,callback,sb);
+			}
+		}
+
+		// see if we have a problem when calling select on large numbers of sockets
+		static void SelectThread() 
+		{
+			Thread.Sleep(1000);
+			while (keepWorking) 
+			{
+				Console.WriteLine(sockList.Count + " sockets connected");
+				Thread.Sleep(1000);
+			}
+			Console.WriteLine(sockList.Count + " sockets connected");
+			Thread.Sleep(1000);
+			keepWorking = false;
+	
+			// now do something with this mass of sockets
+			DoAsyncTest();
+
+			Console.WriteLine("Closing Socket List");
+			foreach(Socket sock in sockList) 
+			{
+				try 
+				{
+					sock.Close();
+				} 
+				catch (Exception e) 
+				{
+					Console.WriteLine("Exception while closing socket " + e.ToString());
+				}
+			}
+		}
+
+	}
+}
+
+Actual Results: 
+On Mono none of the BeginReceive calls return any data until we reach
+somewhere around Socket 50
+i.e. 
+Writing 16 bytes to id 999
+Writing 16 bytes to id 998
+Writing 16 bytes to id 997
+Writing 16 bytes to id 996
+Writing 16 bytes to id 995
+.
+.
+.
+Writing 16 bytes to id 50
+syncLoop got 16 bytes on socket 50
+syncLoop got 16 bytes on socket 51
+syncLoop got 16 bytes on socket 52
+syncLoop got 16 bytes on socket 53
+syncLoop got 16 bytes on socket 54
+....
+
+
+
+
+Expected Results:
+The .Net implementation of the Async functions get the data as soon as it's
+sent
+Writing 16 bytes to id 999
+syncLoop got 16 bytes on socket 998
+Writing 16 bytes to id 998
+syncLoop got 16 bytes on socket 999
+Writing 16 bytes to id 997
+syncLoop got 16 bytes on socket 996
+Writing 16 bytes to id 996
+syncLoop got 16 bytes on socket 997
+Writing 16 bytes to id 995
+syncLoop got 16 bytes on socket 994
+...
+
+etc etc
+
+How often does this happen? 
+
+
+Additional Information: