I have a question regarding adding new socket file descriptors to an FDSET. Lets say we've already connected to a socket s1:
fd_set readfds;
//s1 = socket(...);
//connect(s1, ...)...
FD_ZERO(&readfds);
FD_SET(s1, &readfds);
and we are waiting for data to come down the socket, by calling select in a thread:
socket_reader_thread() {
for (;;)
{
int rv = select(n, &readfds, NULL, NULL, &tv);
if (rv == -1) {
perror("select"); // error occurred in select()
}
else if (rv == 0) {
printf("Timeout occurred! No data after 10.5 seconds.\n");
}
else {
// one the descriptors have data
.....
}
}
}
If I now wanted to add another socket (or may be two more socket etc) to the readfds set, given that select is blocking, how should I proceed? how can I interrupt select
Is the trick to add a zero timeout and use select like poll?
You need to use the "pipe-trick".
This is where an additional socket or pipe is created add it to the fd_set.
Then to interrupt a running or pending select, send a 1 byte message to it via another thread.
The select will then return and if the special pipe FD is one of the ones that are ready in the set, that means you need to say look at a list or something "do work" - like add any new FDs to the fd_set before returning to the select call.
You can interrupt select by sending (and catching) a signal to your process, for example using raise. select will return in this case with -1 and errno set to EINTR. You can then change the events you want to wait for and call select again.
Is the trick to add a zero timeout and use select like poll?
One can simply use a timeout of 0 in which case it will just do a non-blocking check if any of the events got triggered, i.e. polling. But this should only be done in a few cases since busy polling instead of a blocking wait uses lots of resources of machine. And I would even consider the interrupting of a blocking select a questionable design, although probably not as bad as busy polling.
Related
I'm working on an embedded processor running Yocto. I have a modified uio_pdrv_genirq.c UIO driver.
I am writing a library to control the DMA. There is one function which writes to the device file and initiates the DMA. A second function is intended to wait for the DMA to complete by calling select(). Whilst DMA is in progress the device file blocks. On completion the DMA controller issues an interrupt which releases the block on the device file.
I have the system working as expected using read() but I want to switch to select() so that I can include a time out. However, when I use select(), it doesn't seem to be recognising the block and always returns immediately (before the DMA has completed). I have included a simple version of the code:
int gannet_dma_interrupt_wait(dma_device_t *dma_device,
dma_direction dma_transfer_direction) {
fd_set rfds;
struct timeval timeout;
int select_res;
/* Initialize the file descriptor set and add the device file */
FD_ZERO(&rfds);
FD_SET(dma_device->fd, &rfds);
/* Set the timeout period. */
timeout.tv_sec = 5;
timeout.tv_usec = 0;
/* The device file will block until the DMA transfer has completed. */
select_res = select(FD_SETSIZE, &rfds, NULL, NULL, &timeout);
/* Reset the channel */
gannet_dma_reset(dma_device, dma_transfer_direction);
if (select_res == -1) {
/* Select has encountered an error */
perror("ERROR <Interrupt Select Failed>\n");
exit(0);
}
else if (select_res == 1) {
/* The device file descriptor block released */
return 0;
}
else {
/* The device file descriptor block exceeded timeout */
return EINTR;
}
}
Is there anything obviously wrong with my code? Or can anyone suggest an alternative to select?
It turns out that the UIO driver contains two counters. One records the
number of events (event_count), the other records how many events the
calling function is aware of (listener->event_count).
When you do a read() on a UIO driver it returns the number of events and
makes listener->event_count equal to event_count. ie. the listener is
now up to date with all the events that have occurred.
When you use poll() or select() on a UIO driver, it checks if these two
numbers are different and returns if they are (if they are the same it
waits until they differ and then returns). It does NOT update the
listener->event_count.
Clearly if you do not do a read() between calls to select() then
the listener->event_count will not match the event_count and the second
select() will return immediately. Therefore it is necessary to call
read() in between calls to select().
With hindsight it seems clear that select() should work in this way but it wasn't obvious to me at the time.
This answer assumes that it is possible to use select() as intented for the specified device file (I use select() for socket descriptors only). As an alternative function to select(), you may want to check out the poll() family of functions. What follows will hopefully at least offer hints as to what can be done to resolve your problem with calling select().
The first parameter to the select() function has to be the maximum despriptor number plus 1. Since you have only one descriptor, you can pass it directly to select() as its first parameter and add 1. Also consider that the file descriptor in dma_device could be invalid. Returning EINTR on a timeout may actually be what you intend to do but should that not be the case and to test for an invalid descriptor, here is a different version for you to consider. The select() call could be interrupted by a signal, in which case, the return value is -1 and errno will be set to EINTR. This could be handled internally by your function as in:
FD_ZERO(&rfds);
FD_SET(dma_device->fd, &rfds);
timeout.tv_sec = 5;
timeout.tv_usec = 0;
// restart select() if it's interrupted by a signal;
do {
select_res = select(dma_device->fd + 1, &rfds, NULL, NULL, &timeout);
}
while( select_res < 0 && errno == EINTR);
if (select_res > 0) {
// a file descriptor is legible
}
else {
if (select_res == 0) {
// select() timed-out
}
else {
// an error other than a signal occurred
if (errno == EBADF) {
// your file descriptor is invalid
}
}
}
I am aware that select() will be triggered whenever there is a data in the registered socket buffer.
what will happen if there is a delay between these two statements.
FD_SET(listener, &read_fds); // &
(select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1)
what will happen if packet arrives between these two statements?
//create socket and listen for packets &
FD_SET(listener, &read_fds);
Assume that recv() is done once select is triggered.
What will happen if a packet arrives before the select() call is
made.?
does FD_ISSET still detects the packet which is already in
socket buffer or it will be detected only if new packet arrives and
select gets triggered?
Sample code:
// add the listener to the master set
FD_SET(listener, &master);
// keep track of the biggest file descriptor
fdmax = listener; // so far, it's this one
// main loop
for(;;) {
read_fds = master; // copy it
if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1) {
perror("select");
exit(4);
}
// run through the existing connections looking for data to read
for(i = 0; i <= fdmax; i++) {
if (FD_ISSET(i, &read_fds)) { // we got one!!
Understanding gap between socket creation and select() system call
There is no gap between socket creation and select() in your question.
I am aware that select() will be triggered whenever there is a data in the registered socket buffer.
That's true for read events and it applies to the socket receive buffer of connected sockets. It also triggers when there is an inbound connection on a listening socket, or room in the socket send buffer for send events.
what will happen if there is a delay between these two statements.
FD_SET(listener, &read_fds); // &
(select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1)
Nothing bad. Any event that occurs between them will still be signalled. But the first statement isn't a socket creation, contrary to your title.
what will happen if packet arrives between these two statements?
//create socket and listen for packets &
FD_SET(listener, &read_fds);
The socket send buffer exists from the moment the socket is created, so the data will go into the buffer, so when select() runs it will see that and trigger a read event.
Assume that recv() is done once select is triggered.
What will happen if a packet arrives before the select() call is made.?
The socket send buffer exists from the moment the socket is created, so the data will go into the buffer, so when select() runs it will see that and trigger a read event.
does FD_ISSET still detects the packet which is already in socket buffer
Yes.
or it will be detected only if new packet arrives and select gets triggered?
It will always be detected.
If data is waiting to be read, select will return immediately, and FD_ISSET will return true for the file descriptor that the data arrived on. It doesn't matter if data arrived before or after select was called.
select() completes immediately if one or more of the watched conditions is already active; otherwise it blocks until one or more of the watched conditions becomes active (or the timeout, if specified, expires).
I have a single threaded program. It sends message to four destinations every five seconds. I don't want connect() to be blocked. So I am writing my program like this:
int j, rc, non_blocking=1, sockets[4], max_fd=0;
struct sockaddr server=get_server_addr();
fd_set fdset;
const struct timeval conn_timeout = { 2, 0 }; /* 2 seconds */
for (j=0; j<4; ++j)
{
sockets[j]=socket( AF_INET, SOCK_STREAM, 0 );
ioctl(sockets[j], FIONBIO, (char *)&non_blocking);
connect(sockets[j], &server, sizeof (server));
}
/* prepare fd_set */
FD_ZERO ( &fdset );
for (j=0;j<4;++j)
{
if (sockets[j] != -1 )
{
FD_SET ( sockets[j], &fdset );
if ( sockets[j] > max_fd )
{
max_fd = sockets[j];
}
}
}
rc=select(max_fd + 1, NULL, &fdset, NULL, &conn_timeout );
if(rc > 0)
{
for (j=0;j<4;++j)
{
if(sockets[j]!=-1 && FD_ISSET(sockets[j],&fdset))
{
/* send() */
}
}
}
/* close all valid sockets */
However, it seems select() returns immediately after ONE file descriptor is ready instead of blocking for conn_timeout (2 seconds). So in this case how can I achieve my targets?
The program continues if all sockets are ready.
The program can block there for 2 seconds if any one of sockets are not ready.
Yeah, select was designed on the assumption that you would want to service each socket as soon as it became ready.
If I understand what you're trying to do, then the simplest way to accomplish it will be to remove each socket from the fdset as it becomes ready. If there are any sockets left in the set, use gettimeofday to adjust the timeout downward, and call select again. When the set is empty, all four sockets are usable and you can proceed.
There are three basic approaches:
If you want to stay strictly portable you need to iterate:
calculate end time from current time and timeout of your choice
Cycle:
-- Create fdset with those fds not yet ready
-- calculate max time to wait
-- select()
-- remeber those fds that are now ready
-- break if end time reached or all fds ready
End cycle
Now you have knowledge of the ready fds and the elapsed time
If you want to stay portable, but can use threads:
start n threads
select on one fd per thread
join all threads
If you do not need to be portable: Most OSes have a facility for such a situation, e.g. Windows/.NET has WaitAll (together with async send and an event)
I don't see the connection between your stated targets and your stated problem. You are correct in saying that select() blocks until at least one socket is ready, but according to target #2 above that is exactly what you want. There's nothing in your stated targets about blocking until all four sockets are ready at the same time.
You should also note that sockets are almost always ready for writing, unless the send buffer is full, which means the receiver's receive buffer is full, which means the receiver is slower than the sender. So using select() alone as the underlying write timer isn't a good idea.
i came across this problem when writing my first async server with single select call:
if( (retv = select((hsock<highestsocket?highestsocket:hsock)+1, &rFdx, &wFdx, &eFdx, 0) ) > 0)
{
printf("select() ended...\n");
if(FD_ISSET(hsock, &rFdx))
{
// .... handle new connection
}
for(unsigned int i=0; i < ClientList.size(); i++)
{
ServerClient* client = ClientList[i];
if(FD_ISSET(client->socket, &rFdx))
{
// handle client read
}
if(FD_ISSET(client->socket, &wFdx))
{
// handle client write
}
}
}
I expect to select to stop waiting and then handle client write when calling FD_SET from other thread.
If i call FD_SET on client socket from same thread, everything works as expected.
However calling it from other thread does nothing, select keeps waiting until any data is received from client.
Of course you can't modify data in one thread and expect that modification to be visible in other threads without the proper synchronization. It's likely that select reads the fd_set immediately when it's called and then never looks at it again; but regardless, there's no way of obtaining the necessary synchronization if select has already been called. You really need to rethink your design. One solution would be the "self-pipe" trick: having a pipe open that select is always looking for input on, and having your other threads send a message over the pipe to cancel any pending select and get the select thread to rescan the table of file descriptors it should be looking at (with the proper synchronization!) and update its own fd_sets.
I am trying to make a simple client-server chat program. On the client side I spin off another thread to read any incomming data from the server. The problem is, I want to gracefully terminate that second thread when a person logs out from the main thread. I was trying to use a shared variable 'running' to terminate, problem is, the socket read() command is a blocking command, so if I do while(running == 1), the server has to send something before the read returns and the while condition can be checked again. I am looking for a method (with common unix sockets only) to do a non-blocking read, basically some form of peek() would work, for I can continually check the loop to see if I'm done.
The reading thread loop is below, right now it does not have any mutex's for the shared variables, but I plan to add that later don't worry! ;)
void *serverlisten(void *vargp)
{
while(running == 1)
{
read(socket, readbuffer, sizeof(readbuffer));
printf("CLIENT RECIEVED: %s\n", readbuffer);
}
pthread_exit(NULL);
}
You can make socket not blockable, as suggested in another post plus use select to wait input with timeout, like this:
fd_set input;
FD_ZERO(&input);
FD_SET(sd, &input);
struct timeval timeout;
timeout.tv_sec = sec;
timeout.tv_usec = msec * 1000;
int n = select(sd + 1, &input, NULL, NULL, &timeout);
if (n == -1) {
//something wrong
} else if (n == 0)
continue;//timeout
if (!FD_ISSET(sd, &input))
;//again something wrong
//here we can call not blockable read
fcntl(socket, F_SETFL, O_NONBLOCK);
or, if you have other flags:
int x;
x=fcntl(socket ,F_GETFL, 0);
fcntl(socket, F_SETFL, x | O_NONBLOCK);
then check the return value of read to see whether there was data available.
note: a bit of googling will yield you lots of full examples.
You can also use blocking sockets, and "peek" with select with a timeout. It seems more appropriate here so you don't do busy wait.
The best thing is likely to get rid of the extra thread and use select() or poll() to handle everything in one thread.
If you want to keep the thread, one thing you can do is call shutdown() on the socket with SHUT_RDWR, which will shut down the connection, wake up all threads blocked on it but keep the file descriptor valid. After you have joined the reader thread, you can then close the socket. Note that this only works on sockets, not on other types of file descriptor.
Look for function setsockopt with option SO_RCVTIMEO.