Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Non-blocking way to check if a StreamReader has data available

Tags:

c#

mono

I have a StreamReader and I want to know if there is data available without blocking the thread.

I tried the Peek method but it blocks when there is no data available.

using (StreamReader reader = new StreamReader(stream))
{
    if (reader.Peek() == -1) // Blocks here while there is no data and unblocks as soon as there is data.
    {

    }
}

If I check the mono code of the Peek() method, it says in comment

    //
    // Peek can block:
    // http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=96484
    //

Unfortunately, the link doesn't work anymore.

I found here, here, here and here that Microsoft seems to have a bug that cause Peek to block. But all these posts are pretty old. I think mono deliberately made Peek() blocking because of this bug.

So I have two questions

  1. Is it still true that Microsoft has a bug causing Peek() to block? If no, mono should change its implementation of Peek() to be non-blocking.
  2. Is there any other way to check if a StreamReader has data available without blocking the thread?
like image 892
Alexandre Pepin Avatar asked Oct 21 '14 19:10

Alexandre Pepin


1 Answers

OK, let me just say that I don't really know what you are trying to accomplish here. However, from what I can see, the Peek method has to block the current thread in order to work. This is what the documentation says:

The Peek method returns an integer value in order to determine whether the end of the file, or another error has occurred. This allows a user to first check if the returned value is -1 before casting it to a Char type.

So, Peek should only return -1 if it encounters an error or end of the file. This is a little confusing, because there may be no file involved. The stream might be a response from a WebRequest, in which case, the part of the stream that you're trying to read might not be downloaded yet. So, Peek has to wait until it's done because, and this is not clear from the documentation, it returns the first byte read from the stream.

The problems that are mentioned in the links you posted are concerned with multiple threads using the same StreamReader, which is not your case. I also believe that there used to be a bug that would cause one StreamReader waiting for input to block another, but I believe it has since been fixed. I'm not sure what the Mono implementation does.

To answer your question, to do this without blocking the thread, I would try the following:

  1. Just put the whole thing into a separate thread. Then you won't care if it's blocked or not.
  2. Use ReadAsync and then await or ContinueWith on the task to make it non-blocking.

However, as was correctly remarked in the comments, if you put the whole thing into another thread, do you really need Peek? Why not just put it into your typical while (Read(...)) { ... } block and process the data as it's coming?

like image 192
vesan Avatar answered Oct 25 '22 22:10

vesan