[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: