How does this not cause a stack overflow?

I am looking at the source code for a server using SocketAsyncEventArgs, and I'm trying to figure out how this will not cause a stack overflow:

So, this code is called to allow the socket to accept an incoming connection (scroll down to see what I mean):

/// <summary> /// Begins an operation to accept a connection request from the client. /// </summary> /// <param name="acceptEventArg">The context object to use when issuing /// the accept operation on the server listening socket.</param> private void StartAccept(SocketAsyncEventArgs acceptEventArg) { if (acceptEventArg == null) { acceptEventArg = new SocketAsyncEventArgs(); acceptEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(OnAcceptCompleted); } else { // Socket must be cleared since the context object is being reused. acceptEventArg.AcceptSocket = null; } this.semaphoreAcceptedClients.WaitOne(); Boolean willRaiseEvent = this.listenSocket.AcceptAsync(acceptEventArg); if (!willRaiseEvent) { this.ProcessAccept(acceptEventArg); } } 

Then this code is called after the connection is actually accepted (see last line):

  /// <summary> /// Process the accept for the socket listener. /// </summary> /// <param name="e">SocketAsyncEventArg associated with the completed accept operation.</param> private void ProcessAccept(SocketAsyncEventArgs e) { if (e.BytesTransferred > 0) { Interlocked.Increment(ref this.numConnectedSockets); Console.WriteLine("Client connection accepted. There are {0} clients connected to the server", this.numConnectedSockets); } // Get the socket for the accepted client connection and put it into the // ReadEventArg object user token. SocketAsyncEventArgs readEventArgs = this.readWritePool.Pop(); readEventArgs.UserToken = e.AcceptSocket; // As soon as the client is connected, post a receive to the connection. Boolean willRaiseEvent = e.AcceptSocket.ReceiveAsync(readEventArgs); if (!willRaiseEvent) { this.ProcessReceive(readEventArgs); } // Accept the next connection request. this.StartAccept(e); // <==== tail end recursive? } 

Look at the last line. It calls the top function again. How does this not overflow the stack by ping-ponging back and forth between the two functions? This seems to be tail end recursion, but it is not Haskell, so I don’t see how this will work.

It was my understanding that they did not start in threads, but where they just ran on one processor.

+4
source share
5 answers

If AsyncAccept (or any AsyncXXX operation, for that matter) cannot be satisfied immediately, then it will return true, indicating that the operation will be performed asynchronously. When this happens, the callback event will ultimately be fired on the thread stream. Even if it will return to the user interface thread (because it was initiated there), it will do so through a message.

AsyncAccept will most likely return true, because if there are any really non-waiting socket connections (see backlog in Listen ), you are waiting for a client connection.

Therefore, StartAccept () will simply exit without calling ProcessAccept, and ProcessAccept, when (and if) it fires, is likely to be in a different thread.

+1
source

Take a look at the code:

 if (!willRaiseEvent) { this.ProcessAccept(acceptEventArg); } 

Although I have not figured out the whole mechanism yet, willRaiseEvent == true will explicitly end the recursion, so I assume this happens, so this is not an infinite recursion.

+1
source

It all depends on the willCauseEvent flag, which when set to true breaks the recursion. This flag is probably set to true if there is no pending connection.

0
source

It is difficult to determine from the context, but it seems that the first only calls the second call when

 Boolean willRaiseEvent = this.listenSocket.AcceptAsync(acceptEventArg); if (!willRaiseEvent) { 

and so I assume that most of the time it raises an event on completion (from another callback thread)?

(See also section "Stacking"

http://blogs.msdn.com/mjm/archive/2005/05/04/414793.aspx

which looks like something similar.)

0
source

ProcessAccept() always calls StartAccept() , but the converse is not true.

In StartAccept() , ProcessAccept() is called only if the willRaiseEvent parameter willRaiseEvent set to true. This is your way out of infinite recursion right there.

You are always looking for possible exit points if you suspect infinite recursion (either in one recursive function, or in one of your ping-pongs, as you eloquently expressed it :-).

0
source

All Articles