Reputation: 33435
I am in the same situation as this guy, but I don't quite understand the answer.
The problem:
accept
on a socket, which is blocking.close
on this socket.The solution:
what you should do is send a signal to the thread which is blocked in accept. This will give it EINTR and it can cleanly disengage - and then close the socket. Don't close it from a thread other than the one using it.
I don't get what to do here -- when the signal is received in Thread 1, accept
is already blocking, and will continue to block after the signal handler has finished.
accept
to return immediately, why can't Thread 2 do the same without signals?Upvotes: 6
Views: 1881
Reputation: 7832
I believe signals can be used without increasing "the caveats on the library". Consider the following:
#include <pthread.h>
#include <signal.h>
#include <stddef.h>
static pthread_t thread;
static volatile sig_atomic_t sigCount;
/**
* Executes a concurrent task. Called by `pthread_create()`..
*/
static void* startTask(void* arg)
{
for (;;) {
// calls to `select()`, `accept()`, `read()`, etc.
}
return NULL;
}
/**
* Starts concurrent task. Doesn't return until the task completes.
*/
void start()
{
(void)pthread_create(&thread, NULL, startTask, NULL);
(void)pthread_join(thread);
}
static void noop(const int sig)
{
sigCount++;
}
/**
* Stops concurrent task. Causes `start()` to return.
*/
void stop()
{
struct sigaction oldAction;
struct sigaction newAction;
(void)sigemptyset(&newAction.sa_mask);
newAction.sa_flags = 0;
newAction.sa_handler = noop;
(void)sigaction(SIGTERM, &newAction, &oldAction);
(void)pthread_kill(thread, SIGTERM); // system calls return with EINTR
(void)sigaction(SIGTERM, &oldAction, NULL); // restores previous handling
if (sigCount > 1) // externally-generated SIGTERM was received
oldAction.sa_handler(SIGTERM); // call previous handler
sigCount = 0;
}
This has the following advantages:
pthread_cancel()
, pthread_cleanup_push()
, pthread_cleanup_pop()
, and pthread_setcancelstate()
.Upvotes: 1
Reputation: 33435
Call shutdown() from Thread 2. accept
will return with "invalid argument".
This seems to work but the documentation doesn't really explain its operation across threads -- it just seems to work -- so if someone can clarify this, I'll accept that as an answer.
Upvotes: 1
Reputation: 311028
Just close the listening socket, and handle the resulting error or exception from accept().
Upvotes: 0
Reputation: 60933
Instead of blocking in accept()
, block in select()
, poll()
, or one of the similar calls that allows you to wait for activity on multiple file descriptors and use the "self-pipe trick". All of the file descriptors passed to select()
should be in non-blocking mode. One of the file descriptors should be the server socket that you use with accept()
; if that one becomes readable then you should go ahead and call accept()
and it will not block. In addition to that one, create a pipe()
, set it to non-blocking, and check for the read side becoming readable. Instead of calling close()
on the server socket in the other thread, send a byte of data to the first thread on the write end of the pipe. The actual byte value doesn't matter; the purpose is simply to wake up the first thread. When select()
indicates that the pipe is readable, read()
and ignore the data from the pipe, close()
the server socket, and stop waiting for new connections.
Upvotes: 5
Reputation: 19064
The accept() call will return with error code EINTR if a signal is caught before a connection is accepted. So check the return value and error code then close the socket accordingly.
If you wish to avoid the signal mechanism altogether, use select() to determine if there are any incoming connections ready to be accepted before calling accept(). The select() call can be made with a timeout so that you can recover and respond to abort conditions.
I usually call select() with a timeout of 1000 to 3000 milliseconds from a while loop that checks for an exit/abort condition. If select() returns with a ready descriptor I call accept() otherwise I either loop around and block again on select() or exit if requested.
Upvotes: 2