Breaking Out from Socket Select

breaking out from socket select

The easiest way is probably to use pipe(2) to create a pipe and add the read end to readfds. When the other thread wants to interrupt the select() just write a byte to it, then consume it afterward.

C++ select function breaks prematurely

Instead of using a socket to signal an event you could use a pipe (man 2 pipe).

I'd rather revise your architecture if possible.

PS: I swear I haven't looked on the question proposed by Michael Burr in a comment.

How do you gracefully select() on sockets that could be closed on another thread?

The short answer: Don't close sockets from another thread, that you're reading from in this one!

The FD could, potentially, be reassigned. But you will have problems if you're reading from one FD in multiple threads without some kind of scheme to communicate between them. Now, if you have a "Socket description" structure in shared memory that has a control semaphore and some indications of the FD, and other status information, maybe that could be manageable, but I think you'll discover that the simplest solution is almost always to make FD's specific to a single thread…

linux C++ socket select loop

The select() function uses the specified file descriptor mask to determine which file descriptors to monitor for an event (read, write, etc.). When a file descriptor is available for an I/O activity (read, write) the select() function modifies the descriptors to indicate which of the files are ready for the given I/O action.

See this article on the select function and the macros/functions used with the file descriptors.

Old style Unix type programs often treated the file descriptor as a bit mask and just checked the bits. However the actual implementation of the file descriptor can vary by compiler so it is best to use the standard file descriptor macros/functions to set, clear, and test the various file descriptors.

So when using the select() function you need to use FD_ZERO() and FD_SET() so that you will set the specific file descriptors that you want for this particular call to the select() function. When select() returns, it will indicate which of the file descriptors designated are actually ready to be used for the I/O action (read, write, etc.).

So your code will actually be something like:

while(true)
{
fd_set rfds;

FD_ZERO(&rfds);
FD_SET(socketHandle, &rfds);
recVal = select(socketHandle + 1, &rfds, NULL, NULL, &tv);
switch(recVal)
{
case(0):
{
//Timeout
break;
}
case(-1):
{
//Error
break;
}
default:
{
/*Packet Data Type*/ pkt;
if(recvfrom(socketHandle, &pkt, sizeof(/*Packet Data Type*/), 0, (sockaddr*)&serverAddr, (socklen_t*)&sockLen) < 0)
{
//Failed to Recieve Data
break;
}
else
{
//Recieved Data!!
}
break;
}
}

However what you really should do is to use the FD_ISSET() function to check which particular file descriptors are ready for use. In your case you have only the one but in a situation where there were multiple descriptors you would want to use the FD_ISSET() function as well.

How to cancel waiting in select() on Windows

You need to use something similar to safe-pipe trick, but in your case you need to use a pair of connected TCP sockets.

  1. Create a pair of sockets.
  2. Add one to the select and wait on it as well
  3. Notify by writing to other socket from other threads.
  4. Select is immediately waken-up as one of the sockets is readable, reads all the
    data in this special socket and check all data in queues to send/recv

How to create pair of sockets under Windows?

inline void pair(SOCKET fds[2])
{
struct sockaddr_in inaddr;
struct sockaddr addr;
SOCKET lst=::socket(AF_INET, SOCK_STREAM,IPPROTO_TCP);
memset(&inaddr, 0, sizeof(inaddr));
memset(&addr, 0, sizeof(addr));
inaddr.sin_family = AF_INET;
inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
inaddr.sin_port = 0;
int yes=1;
setsockopt(lst,SOL_SOCKET,SO_REUSEADDR,(char*)&yes,sizeof(yes));
bind(lst,(struct sockaddr *)&inaddr,sizeof(inaddr));
listen(lst,1);
int len=sizeof(inaddr);
getsockname(lst, &addr,&len);
fds[0]=::socket(AF_INET, SOCK_STREAM,0);
connect(fds[0],&addr,len);
fds[1]=accept(lst,0,0);
closesocket(lst);
}

Of course some checks should be added for return values.



Related Topics



Leave a reply



Submit