Safely close an indefinitely running thread

66 views Asked by At

So first off, I realize that if my code was in a loop I could use a do while loop to check a variable set when I want the thread to close, but in this case that is not possible (so it seems):

DWORD WINAPI recv thread (LPVOID random) {
    recv(ClientSocket, recvbuffer, recvbuflen, 0);
    return 1;
}

In the above, recv() is a blocking function. (Please pardon me if the formatting isn't correct. It's the best I can do on my phone.)

How would I go about terminating this thread since it never closes but never loops?

Thanks, ~P

3

There are 3 answers

2
Jeremy Friesner On

The way I handle this problem is to never block inside recv() -- preferably by setting the socket to non-blocking mode, but you may also be able to get away with simply only calling recv() when you know the socket currently has some bytes available to read.

That leads to the next question: if you don't block inside recv(), how do you prevent CPU-spinning? The answer to that question is to call select() (or poll()) with the correct arguments so that you'll block there until the socket has bytes ready to recv().

Then comes the third question: if your thread is now blocked (possibly forever) inside select(), aren't we back to the original problem again? Not quite, because now we can implement a variation of the self-pipe trick. In particular, because select() (or poll()) can 'watch' multiple sockets at the same time, we can tell the call to block until either of two sockets has data ready-to-read. Then, when we want to shut down the thread, all the main thread has to do is send a single byte of data to the second socket, and that will cause select() to return immediately. When the thread sees that it is this second socket that is ready-for-read, it should respond by exiting, so that the main thread's blocking call to WaitForSingleObject(theThreadHandle) will return, and then the main thread can clean up without any risk of race conditions.

The final question is: how to set up a socket-pair so that your main thread can call send() on one of the pair's sockets, and your recv-thread will see the sent data appear on the other socket? Under POSIX it's easy, there is a socketpair() function that does exactly that. Under Windows, socketpair() does not exist, but you can roll your own implementation of it as shown here.

0
Arno On

Amongst other solutions you can

a) set a timeout for the socket and handle timeouts correctly by checking the return values and/or errors in an appropriate loop:

setsockopt(ClientSocket,SOL_SOCKET,SO_RCVTIMEO,(char *)&timeout,sizeof(timeout))

b) close the socket with recv(..) returning from blocked state with error.

1
Sridhar DD On

You can use poll before recv() to check if some thing there to receive.

struct pollfd poll;
int res;

poll.fd = ClientSocket;
poll.events = POLLIN;
res = poll(&poll, 1, 1000); // 1000 ms timeout

if (res == 0)
{
    // timeout
}
else if (res == -1)
{
    // error
}
else
{
    // implies (poll.revents & POLLIN) != 0
    recv(ClientSocket, recvbuffer, recvbuflen,0); // we can read ...
}