In my application on linux, I send and receive data using an UDP socket. Sending and receiving is done in two separate threads. Sending works fine.
In my receive function I get continously EAGAIN Resource temporarily unavailable. I do understand, that recvmsg may return EAGAIN, when I call it with the flag MSG_DONTWAIT. But as I first call select to check if anything is available for reading, I do not expect this result.
The function is called in a while-loop and select returns continously faster than the timeout, so the message recvmsg failed (11). Resource temporarily unavailable is spamed to the console at a much higher rate than the timeout or any packages are received.
// Passed values as comments
MySocket::ReceiveRet MySocket::Receive(
void *data /* Pointer to std::array on stack*/,
size_t max_len /* = 170 */,
suseconds_t usTimeout /* = 1000*/
)
{
static uint64_t rcvId = 0;
MySocket::ReceiveRet ret;
if (m_socket == -1)
{
ret.errorCode = -1001;
return ret;
}
struct timeval delta;
memset(&delta, 0, sizeof(delta));
delta.tv_usec = usTimeout;
fd_set readfs, errorfs;
FD_ZERO(&readfs);
FD_ZERO(&errorfs);
FD_SET(m_socket, &readfs);
//FD_SET(m_socket, &errorfs);
// Check for new packages
// See for '+1' reason https://stackoverflow.com/questions/24539031/what-exactly-is-the-first-argument-of-select-function
int retSelct = select(m_socket + 1, &readfs, 0, &errorfs, &delta);
if (retSelct <= 0)
{
// Nothing received
ret.errorCode = retSelct;
return ret;
}
if(!FD_ISSET(m_socket, &readfs))
{
std::cerr << rcvId << " Not ready for read" << std::endl;
ret.errorCode = -1002;
return ret;
}
struct msghdr msg;
struct iovec entry;
struct sockaddr_in from_addr;
union
{
char control[512];
struct cmsghdr cm;
} control;
memset(&msg, 0, sizeof(msg));
msg.msg_iov = &entry;
msg.msg_iovlen = 1;
entry.iov_base = data;
entry.iov_len = max_len;
msg.msg_name = (caddr_t)&from_addr;
msg.msg_namelen = sizeof(from_addr);
msg.msg_control = &control;
msg.msg_controllen = sizeof(control);
// Read the message and metadata
int res = recvmsg(m_socket, &msg, MSG_DONTWAIT);
if (res < 0)
{
std::cerr << rcvId << "recvmsg failed (" << errno << "). " << strerror(errno) << " FD_ISSET(m_socket, &readfs): " << FD_ISSET(m_socket, &readfs) << std::endl;
ret.errorCode = res;
return ret;
}
[...]
I do suspect, that select detects data on another queue (e.g. write, error), but I thought I've not requested this.
I've found Can I guarantee that recv() will not block after select() reports that the socket is ready to read?, which points out, that select may sometimes return false values, but in this case it looks like it is always wrong.
PS: I've tagged this as C, as the socker API is a C API and the C++ Part of the code should not be related to the problem.
Edit: There are multiple queues in a socket (data, error, more??). Select will return, when any of these queues contains data for reading. So all queues needs to be checked. In my case I used this code to receive data and if this failes I try to receive an queued error.
int res = recvmsg(m_socket, &msg, MSG_DONTWAIT);
if(res < 0 && errno == EAGAIN)
{
res = recvmsg(m_socket, &msg, MSG_ERRQUEUE| MSG_DONTWAIT);
}
if (res < 0)
{
std::cerr << "ProcessReceive recvmsg failed (" << errno << "). " << strerror(errno)
<< std::endl;
return;
}
if((msg.msg_flags & MSG_ERRQUEUE) == MSG_ERRQUEUE)
{
// Error was received
[...]
} else {
// Normal data was received
[...]
}
Thanks to pmacfarlane, who asked me in the comments to check the returned fdset of select, which made me realize that select does not distinguish between queues.