Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to wakeup select() within timeout from another thread

According to the "man select" information:

     "On  success,  select() and pselect() return the number of file descrip‐
   tors contained in the three returned  descriptor  sets which may be zero 
   if the timeout expires  before  anything  interesting happens.  On error, 
   -1 is returned, and errno is set appropriately; the sets and timeout become
   undefined, so do not  rely  on  their  contents after an error."

Select will wakup because of:

       1)read/write availability       
       2)select error                 
       3)descriptoris closed.  

However, how can we wake up the select() from another thread if there is no data available and the select is still within timeout?

[update]
Pseudo Code

          // Thread blocks on Select
          void *SocketReadThread(void *param){
               ...
               while(!(ReadThread*)param->ExitThread()) {
                   struct timeval timeout;
                   timeout.tv_sec = 60; //one minute
                   timeout.tv_usec = 0;

                   fd_set rds;
                   FD_ZERO(&rds);
                   FD_SET(sockfd, &rds)'

                   //actually, the first parameter of select() is 
                    //ignored on windows, though on linux this parameter
                   //should be (maximum socket value + 1)
                   int ret = select(sockfd + 1, &rds, NULL, NULL, &timeout );
                   //handle the result
                   //might break from here

               }
               return NULL;
          }

          //main Thread
          int main(){
                //create the SocketReadThread
                ReaderThread* rthread = new ReaderThread;
                pthread_create(&pthreadid, NULL, SocketReaderThread, 
                          NULL, (void*)rthread);

                 // do lots of things here
                 ............................

                //now main thread wants to exit SocketReaderThread
                //it sets the internal state of ReadThread as true
                rthread->SetExitFlag(true);
                //but how to wake up select ??????????????????
                //if SocketReaderThread currently blocks on select
          }

[UPDATE]
1) @trojanfoe provides a method to achieve this, his method writes socket data (maybe dirty data or exit message data) to wakeup select. I am going to have a test and update the result there.
2) Another thing to mention, closing a socket doesn't guarantee to wake up select function call, please see this post.

[UPDATE2]
After doing many tests, here are some facts about waking up select:
1) If the socket watched by select is closed by another application, then select() calling will wakeup immediately. Hereafter, reading from or writing to the socket will get return value of 0 with an errno = 0
2) If the socket watched by select is closed by another thread of the same application, then select() won't wake up until timeout if there is no data to read or write. After select timeouts, making read/write operation results in an error with errno = EBADF (because the socket has been closed by another thread during timeout period)

like image 939
Wallace Avatar asked Mar 24 '23 08:03

Wallace


1 Answers

I use an event object based on pipe():

IoEvent.h:

#pragma once

class IoEvent {
protected:
    int m_pipe[2];
    bool m_ownsFDs;

public:
    IoEvent();              // Creates a user event
    IoEvent(int fd);        // Create a file event

    IoEvent(const IoEvent &other);

    virtual ~IoEvent();

    /**
     * Set the event to signalled state.
     */
    void set();

    /**
     * Reset the event from signalled state.
     */
    void reset();

    inline int fd() const {
        return m_pipe[0];
    }
};

IoEvent.cpp:

#include "IoEvent.h"
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <poll.h>

using namespace std;

IoEvent::IoEvent() : 
    m_ownsFDs(true) {
    if (pipe(m_pipe) < 0)
        throw MyException("Failed to create pipe: %s (%d)", strerror(errno), errno);

    if (fcntl(m_pipe[0], F_SETFL, O_NONBLOCK) < 0)
        throw MyException("Failed to set pipe non-blocking mode: %s (%d)", strerror(errno), errno);
}

IoEvent::IoEvent(int fd) : 
    m_ownsFDs(false) {
    m_pipe[0] = fd;
    m_pipe[1] = -1;
}

IoEvent::IoEvent(const IoEvent &other) {
    m_pipe[0] = other.m_pipe[0];
    m_pipe[1] = other.m_pipe[1];
    m_ownsFDs = false;
}

IoEvent::~IoEvent() {
    if (m_pipe[0] >= 0) {
        if (m_ownsFDs)
            close(m_pipe[0]);

        m_pipe[0] = -1;
    }

    if (m_pipe[1] >= 0) {
        if (m_ownsFDs)
            close(m_pipe[1]);

        m_pipe[1] = -1;
    }
}

void IoEvent::set() {
    if (m_ownsFDs)
        write(m_pipe[1], "x", 1);
}

void IoEvent::reset() {
    if (m_ownsFDs) {
        uint8_t buf;

        while (read(m_pipe[0], &buf, 1) == 1)
            ;
    }
}

You could ditch the m_ownsFDs member; I'm not even sure I use that any more.

like image 153
trojanfoe Avatar answered Mar 31 '23 21:03

trojanfoe