Blocking Select() does not work

M

Michi Henning

Hi,

I'm using a blocking Select() call on a socket with
a timeout value of -1. I'd expect the call to block
indefinitely, but it doesn't. When I use Poll() instead,
a timeout of -1 works fine and blocks indefinitely.

The net effect is that I cannot write a select on more
than one file descripter if I want to block. (With
timeout values >= 0, both Select() and Poll() work fine.)

To reproduce, run the code below and telnet to 127.0.0.1
at port 12345 from another window. The Select() version
returns immediately even though it shouldn't, whereas
the Poll() version correctly blocks and returns once
you hit any key in the telnet window.

Cheers,

Michi.



#define SHOW_BUG

using System;
using System.Collections;
using System.Net;
using System.Net.Sockets;

class Server
{
static void Main(string[] args)
{
Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
s.Bind(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 12345));
s.Listen(1);
Socket c = s.Accept();
#if SHOW_BUG
ArrayList readList = new ArrayList();
readList.Add(c);
Socket.Select(readList, null, null, -1);
Console.WriteLine("Select returned, number of readable descriptors = " + readList.Count);
#else
bool rc = c.Poll(-1, SelectMode.SelectRead);
Console.WriteLine("Poll returned " + rc);
#endif
}
}
 
J

Jon Skeet [C# MVP]

Michi Henning said:
I'm using a blocking Select() call on a socket with
a timeout value of -1. I'd expect the call to block
indefinitely, but it doesn't. When I use Poll() instead,
a timeout of -1 works fine and blocks indefinitely.

Why would you expect it to block indefinitely? I can't see anything in
the documentation to suggest that it should - whereas the Poll
documentation *does* say that a negative value will cause a potentially
indefinite wait.
 
M

Michi Henning

Jon Skeet said:
Why would you expect it to block indefinitely? I can't see anything in
the documentation to suggest that it should - whereas the Poll
documentation *does* say that a negative value will cause a potentially
indefinite wait.

Right, that's exactly what the doc says. But native select() (in the SDK)
blocks indefinitely when called with a negative timeout. And, if what
the documentation says is really as intended, there is simply no way
to do a blocking select on more than a single socket at a time. That's
clearly a bug -- if I have multiple sources of data that I want to monitor
for activity, I can't do that unless I do a busy wait.

If Poll() blocks indefinitely with a negative timeout, so should Select()
(which is exactly what happens when I'm using the SDK instead of
the .NET frameowork).

Cheers,

Michi.
 
C

Chad Z. Hower aka Kudzu

Michi Henning said:
Right, that's exactly what the doc says. But native select() (in the SDK)
blocks indefinitely when called with a negative timeout. And, if what
the documentation says is really as intended, there is simply no way
to do a blocking select on more than a single socket at a time. That's

You can bypass and interface to Winsock2 yourself.


--
Chad Z. Hower (a.k.a. Kudzu) - http://www.hower.org/Kudzu/
"Programming is an art form that fights back"


ELKNews - Get your free copy at http://www.atozedsoftware.com
 
J

Jon Skeet [C# MVP]

Michi Henning said:
Right, that's exactly what the doc says. But native select() (in the SDK)
blocks indefinitely when called with a negative timeout. And, if what
the documentation says is really as intended, there is simply no way
to do a blocking select on more than a single socket at a time.

Well, you can block for a fairly long time, and do so repeatedly, of
course... it's not exactly a tricky problem to code round, and you
could very easily have a utility method which does exactly what you
want it to. I don't expect the performance penalty for calling the
method once every half hour (which according to some quick calculations
is roughly what the maximum parameter gives) would be significant.

Select() *does* block, just not indefinitely, so saying "there is
simply no way to do a blocking select on more than a single socket at a
time" is incorrect, unless I've missed something.
That's clearly a bug -- if I have multiple sources of data that I want
to monitor for activity, I can't do that unless I do a busy wait.

To me, a bug is when something does not behave as documented, which
isn't the case here.
If Poll() blocks indefinitely with a negative timeout, so should
Select()
(which is exactly what happens when I'm using the SDK instead of
the .NET frameowork).

I presume by "SDK" you mean "Win32 API". I'm not saying it wouldn't be
a good idea for Select to be designed to work the same way as Poll, and
I'm interested as to why it isn't, but saying it "does not work" is
wrong IMO, as it's working exactly as documented.
 
M

Michi Henning

Well, you can block for a fairly long time, and do so repeatedly, of
course... it's not exactly a tricky problem to code round, and you
could very easily have a utility method which does exactly what you
want it to. I don't expect the performance penalty for calling the
method once every half hour (which according to some quick calculations
is roughly what the maximum parameter gives) would be significant.

Right. The performance penalty is clearly unnoticeable. But it means
that I have to write special code, just in case I need to block for more
than roughly 35 minutes. Why should I have to write that special code?
And the discrepancy between the .NET versions and the SDK versions
of Poll() and Select() is jarring.
Select() *does* block, just not indefinitely, so saying "there is
simply no way to do a blocking select on more than a single socket at a
time" is incorrect, unless I've missed something.

Right. I can block for 35 minutes, but no longer. (I seem to remember
a Windows Update patch for Win98 that fixed a crash if a machine
was up for more than 28 days or so.) With the current behavior, sooner or
later, we'll have patch for programs that wait for socket input for more
than 35 minutes.
To me, a bug is when something does not behave as documented, which
isn't the case here.

Correct. It's not documented so, according to the documentation, I'm
complaining about nothing. Of course, we can discuss fundamentals here.
On the other hand, the problem is clearly caused by a trivial oversight
in the implementation of Socket.Select(), and fixing it would be equally
trivial. So, why not fix it?
I presume by "SDK" you mean "Win32 API".

Sorry, yes. I'm not a Windows expert.
I'm not saying it wouldn't be
a good idea for Select to be designed to work the same way as Poll, and
I'm interested as to why it isn't, but saying it "does not work" is
wrong IMO, as it's working exactly as documented.

Yes, no argument there. But to say that it works exactly as documented
is stretching a truth a little. In fact, the documentation for Select() that
ships with the C# compiler doesn't mention the fourth parameter to
Select() at all...

Cheers,

Michi.
 
M

Michi Henning

Chad Z. Hower aka Kudzu said:
You can bypass and interface to Winsock2 yourself.

Yes, that's in fact precisely what I did. But the effort of using platform
invoke is not exactly on par with calling Socket.Select() :)

If platform invoke were as convenient as using the .NET framework,
we wouldn't need the framework now, would we? ;-)

Cheers,

Michi.
 
C

Chad Z. Hower aka Kudzu

Michi Henning said:
Yes, that's in fact precisely what I did. But the effort of using
platform invoke is not exactly on par with calling Socket.Select() :)

If platform invoke were as convenient as using the .NET framework,
we wouldn't need the framework now, would we? ;-)

It depends. I havent done PInokes in VB or C# as all my code there has been
pure managed, but at least in Delphi its just a matter of declaring the
interface to the DLL and Delphi manages the rest.


--
Chad Z. Hower (a.k.a. Kudzu) - http://www.hower.org/Kudzu/
"Programming is an art form that fights back"


ELKNews - Get your free copy at http://www.atozedsoftware.com
 
M

Michi Henning

Chad Z. Hower aka Kudzu said:
It depends. I havent done PInokes in VB or C# as all my code there has been
pure managed, but at least in Delphi its just a matter of declaring the
interface to the DLL and Delphi manages the rest.

I have no experience with Delphi. From C#, platform invoke is
simple as long as the parameters are simple. Once things are passed
by pointer, or you are getting a pointer as an out parameter from
a C call that you are supposed to later deallocated, things are not
as simple and take quite a while to figure out. The same is true
for passing structures or unions that play tricks with arrays as
the last field to pad the structure to a desired size. Not trivial
to call something like that with platform invoke (at least not
until you've finally figured out how to do it or located the
obscure bit of the relevant documentation -- the platform
invoke doc is -- how shall I put it -- obtuse?)

Cheers,

Michi.
 
J

Jon Skeet [C# MVP]

Michi Henning said:
Right. The performance penalty is clearly unnoticeable. But it means
that I have to write special code, just in case I need to block for more
than roughly 35 minutes. Why should I have to write that special code?
And the discrepancy between the .NET versions and the SDK versions
of Poll() and Select() is jarring.

I agree that it would be desirable for you not to have to write that
special code. On the other hand, unless I'm mistaken it's almost
trivial to write.
Right. I can block for 35 minutes, but no longer. (I seem to remember
a Windows Update patch for Win98 that fixed a crash if a machine
was up for more than 28 days or so.) With the current behavior, sooner or
later, we'll have patch for programs that wait for socket input for more
than 35 minutes.

I don't think so: any program which wants to wait for socket input for
more than 35 minutes but not indefinitely will have found that it can't
due to the range of int. Any program which wants to wait for socket
input indefinitely will have run into the problem already, and
presumably written the extra code to just loop. I can't see any
patching being required unless coders have been *really* dumb about it.
Correct. It's not documented so, according to the documentation, I'm
complaining about nothing. Of course, we can discuss fundamentals here.
On the other hand, the problem is clearly caused by a trivial oversight
in the implementation of Socket.Select(), and fixing it would be equally
trivial. So, why not fix it?

Indeed - and I'm not saying that it shouldn't be changed appropriately.
I'm just saying it's important to get the terminology here - I don't
believe it's a trivial oversight in the *implementation* of
Socket.Select, for instance - it's a trivial oversight in the *design*
which would also be trivial to fix in the implementation.
Sorry, yes. I'm not a Windows expert.

Me either - but as there's a .NET SDK, I thought it would be best to
check we're talking about the same thing.
Yes, no argument there. But to say that it works exactly as documented
is stretching a truth a little. In fact, the documentation for Select() that
ships with the C# compiler doesn't mention the fourth parameter to
Select() at all...

Really? The version I've got (in the SDK) specifies:

microSeconds
The time to wait for a response, in microseconds.

It doesn't mention it after that, but it goes that far. Certainly the
documentation isn't as full as it might be, but there's nothing in the
implementation which actually goes against what the documentation
states.
 
M

Michi Henning

I agree that it would be desirable for you not to have to write that
special code. On the other hand, unless I'm mistaken it's almost
trivial to write.

No, you are not mistaken :)
I don't think so: any program which wants to wait for socket input for
more than 35 minutes but not indefinitely will have found that it can't
due to the range of int. Any program which wants to wait for socket
input indefinitely will have run into the problem already, and
presumably written the extra code to just loop. I can't see any
patching being required unless coders have been *really* dumb about it.

Yes, I agree with you there -- I didn't think thius through enough, so I'll
withdraw the comment.
Indeed - and I'm not saying that it shouldn't be changed appropriately.
I'm just saying it's important to get the terminology here - I don't
believe it's a trivial oversight in the *implementation* of
Socket.Select, for instance - it's a trivial oversight in the *design*
which would also be trivial to fix in the implementation.

Ah, OK, I guess you can look at it from that angle equally well.
Me either - but as there's a .NET SDK, I thought it would be best to
check we're talking about the same thing.

The help doc that comes with the C# compiler uses the term
"Platform SDK", at least when I'm using the index and want to
restrict the hits to the Win32 API. I don't know myself which term
is more appropriate. Seems that "Win32 API" is more commonly
used?
Really? The version I've got (in the SDK) specifies:

microSeconds
The time to wait for a response, in microseconds.

Ah, yes, that's what I get when I click on the parameter. But the
parameter isn't mentioned in the body of the text.
It doesn't mention it after that, but it goes that far. Certainly the
documentation isn't as full as it might be, but there's nothing in the
implementation which actually goes against what the documentation
states.

I have to agree there. On the other hand, if a negative argument for the
timeout is non-sensical (in the sense that it does the same thing as a zero
timeout), shouldn't a negative value result in a ArgumentException?

Anyway, this is getting into the finer details of API design and different
people have different opinions in that space.

Anyway, I think it would be really nice if Select() would treat a negative
timeout the same way as Poll() does, both because that would be
convenient and for consistency with the Win32 API behavior.

Cheers,

Michi.
 
J

Jon Skeet [C# MVP]

<snipping lots as I go>

Michi Henning said:
The help doc that comes with the C# compiler uses the term
"Platform SDK", at least when I'm using the index and want to
restrict the hits to the Win32 API. I don't know myself which term
is more appropriate. Seems that "Win32 API" is more commonly
used?

Well, "Platform SDK" is unfortunately a really vague term - it could be
the API for *any* platform. :(
Anyway, I think it would be really nice if Select() would treat a negative
timeout the same way as Poll() does, both because that would be
convenient and for consistency with the Win32 API behavior.

Agreed. That seems a pretty good point at which to draw this thread to
a close :)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top