select
is a demultiplexing mechanism. While you are using it to determine when data is ready on a single socket or timeout, it was actually designed to return data ready status on many sockets (hence the fd_set
). Conceptually, it is the same with poll
, epoll
and kqueue
. Combined with non-blocking I/O, these mechanisms provide an application writer with the tools to implement a single threaded concurrent server.
In my opinion, your application does not need that kind of power. Your application will only be handling two connections, and you are already using one thread per connection. I believe leaving the socket in blocking I/O mode is more appropriate.
If you insist on non-blocking mode, my suggestion is to replace the select
call with something else. Since what you want from select
is an indication of read readiness or timeout for a single socket, you can achieve a similar effect with recv
passed with appropriate parameters and with the appropriate timeout set on the socket.
tm.tv_sec = time;
tm.tv_usec = 0;
setsockopt(connectedSocket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tm, sizeof(tm));
char c;
swtich (recv(connectedSocket, &c, 1, MSG_PEEK|MSG_WAITALL)) {
case -1:
if (errno == EAGAIN) {
// handle timeout ...
} else {
// handle other error ...
}
break;
case 0: // FALLTHROUGH
default:
// handle read ready ...
break;
}
From man recv
:
MSG_PEEK -- This flag causes the receive operation to return data from the beginning of the receive queue without removing that data from the queue. Thus, a subsequent receive call will return the same data.
MSG_WAITALL (since Linux 2.2) -- This flag requests that the operation block until the full request is satisfied. However, the call may still return less data than requested if a signal is caught, an error or disconnect occurs, or the next data to be received is of a different type than that returned.
As to why select
is behaving in the way you observed. While the select
call is thread-safe, it is likely fully guarded against reentrancy. So, one thread's call to select
will only come in after another thread's call completes (the calls to select
are serialized). This is inline with its function as a demultiplexer. It's purpose is to serve as a single arbiter for which connections are ready. As such, it wants to be controlled by a single thread.