Search code examples
cmultithreadingclientposix-select

Select() doesn't recognise changes through FD_SET while blocking


I'm calling FD_SET() to set the write_fd of a non-blocking socket while select() is blocking within another thread – the problem is that select() keeps blocking even if the fd is ready to write.

What I actually want do is: Preparing data to write for this socket within another thread, afterwards I add the socket to the write_fd. The select() thread should recognise that and handle the prepared data.

Doesn't select() recognise changes within the fd while blocking? If yes – is there something like the epoll() EPOLL_CTL_MOD instead of FD_SET() to update the set; or is the only way to recognise changes to set the timeout of the select()-function?

In my opinion that wouldn't be a solution because that would be "slow" and would produce CPU overhead ...

EDIT:

// This thread is running all day long ...
static void * workman() {
    FD_ZERO(&fd_read);
    FD_ZERO(&fd_write);
    FD_SET(socketId , &fd_read);

    while(1) {
        // PROBLEM Keeps blocking when box() is called
        select(socketId+1, &fd_read, &fd_write, NULL, NULL);

        if(FD_ISSET(socketId, &fd_read)) {
            // RECIVE DATA
        }
        else if(FD_ISSET(socketId, &fd_write)) {
            FD_CLR(socketId, &fd_write);
            pthread_mutex_lock(&interface.mutex);
                strncpy(conn.outBuffer, interface.buffer, strlen(interface.buffer));
                interface.buffer[0] = '\0';
            pthread_mutex_unlock(&interface.mutex);
            // SEND DATA
        }
    }

    return 0;
}

// This function is called within another thread on user input
int box(char *content) {
    pthread_mutex_lock(&interface.mutex);
        // preparing the data and write it into interface.buffer if available
    pthread_mutex_unlock(&interface.mutex);

    FD_SET(socketId, &fd_write);

    return 0;
}

Solution

  • Yes, as you suspect, select() will not detect changes done to the file descriptor sets from another thread. After all, it cannot do that efficiently, without some magic mechanism to asynchronously detect writes to specific memory locations.

    And, yes, you should use the epoll interface. The man page of epoll_wait specifically notes that changes from another thread are handled.

    While one thread is blocked in a call to epoll_pwait(), it is possible for another thread to add a file descriptor to the waited-upon epoll instance. If the new file descriptor becomes ready, it will cause the epoll_wait() call to unblock.

    But, if you are unable to use epoll or another file notification interface which supports such changes, there is still a solution. You can use an internal pipe (or similar mechanisms such as eventfd), to cause select to return and restart after you have updated the file descriptor sets. Of course, you should be careful to do proper locking to avoid race conditions. Likewise, be sure to put the pipe into non-blocking mode, or a write to the write end of the pipe could block under heavy load and possibly cause your program to deadlock.