0

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

Moon
  • 329
  • 2
  • 3
  • 20
  • 1
    possible duplicate of [how to interrupt a thread which is waiting on recv function?](http://stackoverflow.com/questions/5168372/how-to-interrupt-a-thread-which-is-waiting-on-recv-function) – pilcrow Nov 11 '14 at 15:03
  • Whoops. Believe me, I tried searching for an answer... – Moon Nov 11 '14 at 15:06
  • But it seems that post is not of much use to me anyway. – Moon Nov 11 '14 at 15:13

3 Answers3

1

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.

Jeremy Friesner
  • 70,199
  • 15
  • 131
  • 234
Arno
  • 4,994
  • 3
  • 39
  • 63
0

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 ...
} 
Sridhar DD
  • 1,972
  • 1
  • 10
  • 17
  • It seems that code isn't liked by my compiler. `call of an object of a class type without appropriate operator()` I'm not quite advanced enough to chase down an answer for that. Specifically, this error is from `poll` on the sixth line of your code. – Moon Nov 11 '14 at 15:13
0

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.

Community
  • 1
  • 1
Jeremy Friesner
  • 70,199
  • 15
  • 131
  • 234
  • The ideas presented here look promising. I'll try them out as soon as I can. :) – Moon Nov 11 '14 at 16:43
  • 2
    On Windows, you don't need a second socket. That would just waste a port unnecessarily. Use `WSACreateEvent()` instead to create two event objects, associate one with the real socket using `WSAEventSelect()`, then use `WSAWaitForMultipleEvents()` to wait on both events. When you need to terminate, use `WSASetEvent()` to signal the second event. `WSAWaitForMultipleEvents()` will tell you which event was signaled. If you do use a second socket, you don't have to send data to it. You can use `shutdown()` to close one side of the socket and that will put the other side into a ready-for-read state. – Remy Lebeau Nov 12 '14 at 03:26