views:

2089

answers:

5

I'm working on Linux system (Ubuntu 7.04 server with a 2.6.20 kernel).

I've got a program that has a thread (thread1) waiting on a select for a UDP socket to become readable. I'm using the select (with my socket as the single readfd and the single exceptfd) instead of just calling recvfrom because I want a timeout.

From another thread, I shutdown and close the socket. If I do this while thread1 is blocked in a recvfrom, then the recvfrom will terminate immediately. If I do this while thread1 is blocked in a select with a timeout, then the select will NOT terminate immediately, but will eventually timeout properly.

Can anyone tell me why it is that the select doesn't exit as soon as the socket is closed? Isn't that an exception? I can see where it isn't readable (obviously), but it's closed, which seems to be to be exeptional.

Here's the opening of the socket (all error handling removed to keep things simple):

m_sockfd = socket(PF_INET, SOCK_DGRAM, 0);
struct sockaddr_in si_me;
memset((char *) &si_me, 0, sizeof(si_me));
si_me.sin_family = AF_INET;
si_me.sin_port = htons(port);
si_me.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(m_sockfd, (struct sockaddr *)(&si_me), sizeof(si_me)) < 0)
{
// deal with error
}

Here's the select statement that thread1 executes:

struct timeval to;
to.tv_sec = timeout_ms/1000;// just the seconds portion
to.tv_usec = (timeout_ms%1000)*1000;// just the milliseconds 
                                    // converted to microseconds

// watch our one fd for readability or
// exceptions.
fd_set  readfds, exceptfds;
FD_ZERO(&readfds);
FD_SET(m_sockfd, &readfds);
FD_ZERO(&exceptfds);
FD_SET(m_sockfd, &exceptfds);

int nsel = select(m_sockfd+1, &readfds, NULL, &exceptfds, &to);

UPDATE: Obviously (as stated below), closing the socket isn't an exceptional condition (from select's point of view). I think what I need to know is: Why? And, Is that intentional?.

I REALLY want to understand the thinking behind this select behavior because it seems counter to my expectations. Thus, I obviously need to adjust my thinking on how the TCP stack works. Please explain it to me.

+3  A: 

UDP is a connectionless protocol. Since there is no connection, none can be broken, so the consumer doesn't know that the producer will never send again.

You could make the producer send an "end of stream" message, and have the consumer terminate upon receiving it.

John Zwinck
All correct, and true and a viable workaround, but: The recvfrom DOES terminate when I close the socket. Why doesn't the select? I'm really confused as to why closing the socket doesn't produce an exception that terminates the select.
Michael Kohne
A: 

I think the most obvious solution is that being closed isn't considered an exceptional condition. I think the root of the problem is, that you're not really embracing the philosophy of select. Why on earth are you fiddling around with the socket in another thread, that sounds like a recipe for disaster.

The only thing I do with the socket from another thread is close it, for purposes of stopping a wait in progress. This way I can get my program to exit BEFORE the select timeout expires.
Michael Kohne
A: 

I would say the difference is that recvfrom is actively trying to read a message from a single socket, where select is waiting for a message to arrive, possibly on multiple handles, and not necessarily socket handles.

Rob K
+2  A: 

Maybe you should use something else to wake up the select. Maybe a pipe or something like that.

iny
That would be a good solution. Have the select wait on both the socket and pipe, and the other thread would write to the pipe to cause the select to return.
Rob K
That would work and probably will be what I end up with. I had just wanted to avoid any extra moving parts. Thanks!
Michael Kohne
+1  A: 

Could you not send a signal (e.g. USR2) to the thread which would cause select() to return with EINTR? Then in the signal handler set a flag telling it not to restart the select()?

That would remove the need for waiting on multiple file descriptors, and seems a lot cleaner than using a pipe to kill it.

Richard Whitty
Yes, that would work, though in this particular case I don't think I can use signals internally (some of the badly written base classes in our library use and/or block signals in weird ways). If I can clean the underlying libraries, I might try this at some point.
Michael Kohne