10

I have a socket listener which hangs on recv function:

size_t recvLen = recv(sock, buf, 512, 0);

I would like to terminate this thread with interrupting it. MSDN says:

When issuing a blocking Winsock call such as recv, Winsock may need to wait for a network event before the call can complete. Winsock performs an alertable wait in this situation, which can be interrupted by an asynchronous procedure call (APC) scheduled on the same thread.

How can I do that?

MCA
  • 761
  • 2
  • 9
  • 15
  • use closesocket via queuing an APC to the thread, that will cause recv to return with an error because the socket is closed – paulm Jun 22 '13 at 14:48

6 Answers6

10

You can interrupt it by queuing an APC to it via QueueUserAPC. However, it's most likely unsafe to terminate the thread in the APC. Queuing an APC doesn't end the recv, it just interrupts it; once the APC returns, it will go back to waiting on recv again.

If you want to stop the recv completely, you should be using select with a timeout to wait until data is available. You can then check whether you should keep waiting for data or continue at each timeout.

Collin Dauphinee
  • 13,664
  • 1
  • 40
  • 71
  • 2
    call closesocket() in the APC call and it will return, no need for async socket or evil TerminateThread calls :) – paulm Jun 22 '13 at 14:49
4

If you don't want to receive any more data, you can kill the socket at anytime. Just call close() on it, the function in question will immediately return an error.

What I've done in the past is just run another thread with a timeout, after the waiting period if a "don't die" flag isn't set kill the socket.

ultifinitus
  • 1,813
  • 2
  • 19
  • 32
1

Checking the socket buffer prior to recv is more flexible rather than covering a lot for select() support, I think. You can call ioctlsocket(SockHandle, FIONREAD, Longint(CountInBuffer)) to see if there's data in network buffer to read and then call recv(SockHandle, buff, CountInBuffer, 0). This way you can make a single recv call to read the whole network read buffer if you allocate the buff itself enough with CountInBuffer. Otherwise, you need to call recv in a loop to read the network buffer, which is the traditional way. In both cases, you're still in the constraints of the CountInBuffer.

Hasan Manzak
  • 663
  • 6
  • 14
0

I think the best way to handle this problem is to put the socket into non-blocking I/O mode, so that the thread will never block inside recv() (or in send(), for that matter). The thread should only ever block inside select() (or WaitMultipleObjects()). That way the select() (or WaitMultipleObjects()) call will return if data arrives for the socket (in which case you can then call recv() to get the new data without blocking), but you can also have select()/WaitMultipleObjects() return when something else happens; e.g. when it gets a prompt from the main thread. If you are using select(), that prompt can be the main thread sending a byte on a different socket-pair (with the main thread holding one end of the socket-pair, and the I/O thread holding the other end); if you are using WaitMultipleObjects() then I believe you can use any of the standard Windows event/signaling methods that would cause WaitMultipleObjects() to return.

Jeremy Friesner
  • 70,199
  • 15
  • 131
  • 234
  • 1
    You don't need to put the socket into non-blocking mode in order to use `select()`. You can use `select()` with blocking sockets. Just call `select()` to make sure the socket is readable before before calling `recv()`, then `recv() will not block even if the socket is in blocking mode. – Remy Lebeau May 08 '12 at 02:01
  • I'm not sure how that is any easier/better than putting the socket into non-blocking mode. Now instead of adding one line of code, you have to add one line of code before every potentially-blocking call to the socket (and if you, or future code maintainers, ever forget to do so, you will have a subtle and hard-to-detect bug in your program that might cost someone a lot of time to track down) – Jeremy Friesner Dec 20 '13 at 20:03
  • Also, the check-with-select-before-recv() approach doesn't always work reliably: http://stackoverflow.com/a/5352634/131930 – Jeremy Friesner Dec 20 '13 at 20:04
-1

The only way to really interrupt a blocking recv() call and make it fully exit is to close the socket from another thread context than the one that is blocked. If that is not an option, then you need to re-write your socket logic. Best to use non-blocking or asynchronous I/O, that way recv() (or WSARecv() for the latter) will never block, and you can do whatever you need to do (like checking for thread termination conditions) while the reading is performed in the background.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • 1
    Just make sure you don't have a race condition where the other thread could have closed it for you already, or you may be closing a socket descriptor opened for some other socket (a new socket) already [?] – rogerdpack Dec 20 '13 at 19:12
  • re: async apparently that is possible using WaitForMultipleObjects http://ffmpeg.org/pipermail/ffmpeg-devel/2013-December/152211.html – rogerdpack Dec 31 '13 at 16:19
  • You cannot use any of the `WaitFor...Object(s)` functions with socket handles. You would have to use event handles from `CreateEvent()` instead, calling `SetEvent()` when socket operations complete. Or use `WSACreateEvent()` with `WSAEventSelect()` and `WSAWaitForMultipleEvents()`. Or use `CreateIoCompletionPort()` with `GetQueuedCompletionStatus()`. – Remy Lebeau Dec 31 '13 at 18:07
-1

Errrr... Perform an APC on the same thread? :-))

Seriously, though, it's not clear what your objective here is.
If you just want to end the thread, use the TerminateThread function.
If you want to interrupt this particular call, you can close the socket.

Fyodor Soikin
  • 78,590
  • 9
  • 125
  • 172
  • terminate thread should not be used as it doesnt notify DLLs and does not let the thread to cleanup. Closing the socket is possible, but you might still need the socket. – Tomer W Sep 01 '16 at 07:43