views:

164

answers:

2

I'm writing a server in C# which creates a (long, possibly even infinite) IEnumerable<Result> in response to a client request, and then streams those results back to the client.

Can I set it up so that, if the client is reading slowly (or possibly not at all for a couple of seconds at a time), the server won't need a thread stalled waiting for buffer space to clear up so that it can pull the next couple of Results, serialize them, and stuff them onto the network?

Is this how NetworkStream.BeginWrite works? The documentation is unclear (to me) about when the callback method will be called. Does it happen basically immediately, just on another thread which then blocks on EndWrite waiting for the actual writing to happen? Does it happen when some sort of lower-level buffer in the sockets API underflows? Does it happen when the data has been actually written to the network? Does it happen when it has been acknowledged?

I'm confused, so there's a possibility that this whole question is off-base. If so, could you turn me around and point me in the right direction to solve what I'd expect is a fairly common problem?

A: 

First, the only way your main thread can keep executing while other work is being done is through the use of another thread. A thread can't do two things at once.

However, I think what you're trying to avoid is mess with the Thread object and yes that is possible through the use of BeginWrite. As per your questions

The documentation is unclear (to me) about when the callback method will be called.

The call is made after the network driver reads the data into it's buffers.

Does it happen basically immediately, just on another thread which then blocks on EndWrite waiting for the actual writing to happen?

Nope, just until it's in the buffers handled by the network driver.

Does it happen when some sort of lower-level buffer in the sockets API underflows?

If by underflow your mean it has room for it then yes.

Does it happen when the data has been actually written to the network?

No.

Does it happen when it has been acknowledged?

No.

EDIT

Personally I would try using a Thread. There's a lot of stuff that BeginWrite is doing behind the scenes that you should probably recognize... plus I'm weird and I like controlling my threads.

Spencer Ruport
There's some information in this question that isn't quite accurate. For example, as I explained in my post, the callback is certainly not always called as soon as data arrives in the send buffer. It's up to the implementation, but even on Windows, it depends whether IO completion ports are available. Sometimes it may be called immediately and block on EndWrite. I would also add that it's unwise to create a thread for every single send. At the very least, use a thread pool. But then you're just complicating things,a nd most likely doing it more inefficiently than BeginWrite would.
IRBMe
+2  A: 

I'll answer the third part of your question in a bit more detail.

The MSDN documentation states that:

When your application calls BeginWrite, the system uses a separate thread to execute the specified callback method, and blocks on EndWrite until the NetworkStream sends the number of bytes requested or throws an exception.

As far as my understanding goes, whether or not the callback method is called immediately after calling BeginSend depends upon the underlying implementation and platform. For example, if IO completion ports are available on Windows, it won't be. A thread from the thread pool will block before calling it.

In fact, the NetworkStream's BeginWrite method simply calls the underlying socket's BeginSend method on my .Net implementation. Mine uses the underlying WSASend Winsock function with completion ports, where available. This makes it far more efficient than simply creating your own thread for each send/write operation, even if you were to use a thread pool.

The Socket.BeginSend method then calls the OverlappedAsyncResult.CheckAsyncCallOverlappedResult method if the result of WSASend was IOPending, which in turn calls the native RegisterWaitForSingleObject Win32 function. This will cause one of the threads in the thread pool to block until the WSASend method signals that it has completed, after which the callback method is called.

The Socket.EndSend method, called by NetworkStream.EndSend, will wait for the send operation to complete. The reason it has to do this is because if IO completion ports are not available then the callback method will be called straight away.

I must stress again that these details are specific to my implementation of .Net and my platform, but that should hopefully give you some insight.

IRBMe