How to Wake Select() on a Socket Close

How to wakeup Select call without timeout period from another thread

Thanks all for valuable suggestion, I am able to resolve the issue with shutdown() call on socket FD using reference answer present on this link, it will pass wakeup signal to select, which is waiting for action. We should close socket only after select call otherwise select will not able to get wake up signal.

select() on half-open socket does not return on close()?

Yes you misuse the select. The man select states:

If a file descriptor being monitored by select() is closed in another thread, the result is unspecified. On some UNIX systems, select() unblocks and returns, with an indication that the file descriptor is ready (a subsequent I/O operation will likely fail with an error, unless another the file descriptor reopened between the time select() returned and the I/O operations was performed). On Linux (and some other systems), closing the file descriptor in another thread has no effect on select(). In summary, any application that relies on a particular behavior in this scenario must be considered buggy.

So you cannot close connection from other thread. Unfortunately the poll has the same issue.


EDIT

There are several possible solution and I have not sufficient information about your application. Following changed can be considered:

  • Use epoll instead of select if you are on linux or other modern polling mechanism if you on another OS. select is quite old function and it was designed in time when threading was not considered seriously.
  • Establish a communication channel between the select thread and the keep-alive thread. When keep alive thread detects a dead peer then don't close the socket itself but instructs the select thread to do that. Typically it can be done through a local socket. The local socket is added to select descriptor set and when the keep-alive thread writes something to it the select thread wakes up and can take an action.

how to wakeup select() within timeout from another thread

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.

Cannot close listening socket to abort accept()/select() from a separate thread

close() won't do what you want in the multithreaded case. Use one of the other mechanisms you describe instead.

In the single-threaded case, control returns to the select(), which is restarted and notices EBADF on the now-dismissed file descriptor. (This is highly dangerous, of course, because fd #3 might be recycled by any other thread, or even a complex signal handler, at any time, though your toy program appears safe.) In the multi-threaded case, close() just doesn't wake up your select()ing thread.

The Python docs warn:

Note: close() releases the resource associated with a connection but does not necessarily close the connection immediately. If you want to close the connection in a timely fashion, call shutdown() before close().

In fact, this is a relatively thorny and platform-dependent problem. Excerpting a 2008 article in the venerable Dr Dobb's:

On some operating systems, [ shutdown() instead of close() ] is also the only working solution: On FreeBSD, a close() without shutdown() does not wake up the processes waiting in a read() or select()....

Another issue to consider is that closing by shutdown() or by close() may not be considered a read event in the OS....

However, shutdown() works only on the sockets with established connections, not on the ones listening for new connections nor on the other kinds of file descriptors....

(For what it's worth, on my system a shutdown() does awaken the select()ing thread.)

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.



Related Topics



Leave a reply



Submit