4

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)

Community
  • 1
  • 1
Wallace
  • 561
  • 2
  • 21
  • 54

1 Answers1

8

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.

trojanfoe
  • 120,358
  • 21
  • 212
  • 242
  • You might want to make it more explicit that the reason this works is because you have the select() call in the other thread watching m_pipe[0] so that it wakes up when it is ready-for-read... – Jeremy Friesner Jul 02 '13 at 15:09
  • @JeremyFriesner Agreed; I was waiting for some feedback from the OP, but he seems disinterested. – trojanfoe Jul 02 '13 at 15:11
  • @trojanfoe I was reading your code thinking how it works, only to find that I don't understand why this can wakeup select until I saw comments from Jeremy Friesner :_) So your method will write socket data to wake up select, am I right? – Wallace Jul 03 '13 at 00:07
  • Yes. It's a cheap imitation of a Windows Event object when used with `WaitForMultipleObjects()`. I typically use it for a quit event so if I'm in a `poll()` I also listen to this *quit event* and if I want the loop to quit I "set" the event. – trojanfoe Jul 03 '13 at 05:28