9

There is a thread likes this:

{  
    ......    
    while (1)
    {
        recv(socket, buffer, sizeof(buffer), 0);
        ......
    }
    close(socket);           
}  

Because the thread is blocking on recv() call, how can I let the thread exit gracefully?

alk
  • 69,737
  • 10
  • 105
  • 255
Nan Xiao
  • 16,671
  • 18
  • 103
  • 164

7 Answers7

5

You can call:-

shutdown(sock, SHUT_RDWR)    //on the remote end

Check this out.

Rahul Tripathi
  • 168,305
  • 31
  • 280
  • 331
5

Do not block on recv. Rather implement a block on 'select' and test the input fdset to determine if there is anything to read in the first place.

If there is data to be read, call recv. If not, loop, check 'isrunning' volitile Boolean variable that gets set by the other thread when shutdown has been initiated.

Sass
  • 560
  • 4
  • 17
  • 2
    Actually it's very high. It allows you to service multiple sockets simultaneously per thread. I used this method on servers hosting thousands of connections. Blocks of 64 sockets were managed via select per thread. Even at full thoughput the CPU load remained at less that ten percent. – Sass Aug 16 '13 at 06:43
  • :thanks! Per my understaning, your program model isn't every thread serving every socket, but a thread serving many sockets. Do you think every thread serving every socket ismore efficiency? – Nan Xiao Aug 16 '13 at 06:49
  • No I don't. Based on the network app however, the correct per-thread socket loading is variable. None the less, on average a 'bunch' of sockets are generally waiting on hardware to deliver data. Depending on the nature of the connection, it is statistically likely (in my experience) for some sockets to be idle. Even all sockets batched in one thread WILL sleep some of the time. A single thread waiting on a bunch of hardware interrupts is more efficient than servicing one thread per socket. – Sass Aug 16 '13 at 06:59
  • OK, thanks for your detailed explanation! If possible, could you provide your program model source code for my reference? If it is confidential, leave it at that. – Nan Xiao Aug 16 '13 at 07:14
  • You are right about select. The volatile part is not so good. – Maxim Egorushkin Aug 16 '13 at 10:04
  • 1
    please explain why you think it's not so good. the volatile part is necessary in a multithreaded environment to insure the recv thread gets the most uptodate state of the 'isrunning' flag. if another thread issues the 'stop' call on the recv thread, this guarantees the recv thread acts promptly rather than running off a likely cached value of the bool. – Sass Aug 16 '13 at 19:14
  • select() doesn't 'wait on a bunch of hardware interrupts'. It waits on software-defined events. – user207421 Aug 18 '13 at 08:14
  • Misquote. I said 'a single thread waiting on hardware interrupts'. At ring 0, ready to read states by the network card notify the OS kernel via interrupt. At the app layer, select indirectly waits on interrupts as far as I'm concerned. – Sass Aug 19 '13 at 03:49
4

Either:

  1. Set a read timeout, with setsockopt() and SO_RCVTIMEO, and whenever it triggers check a state variable to see if you've told yourself to stop reading.
  2. If you want to stop reading the socket forever, shut it down for input with shutdown(sd, SHUT_RD). This will cause recv() to return zero from now on.
  3. Set the socket into non-blocking mode and use select() with a timeout to tell you when to read, adopting the same strategy with a state variable as at (1) above. However non-blocking mode introduces considerable complications into the send operation, so you should prefer (1) or (2) above.

Your pseudo-code lacks EOS- and error-checking. I hope it doesn't really look like that.

user207421
  • 305,947
  • 44
  • 307
  • 483
2

Declare a global exit flag:

int bExit = 0;

Let the cirtical parts test it:

 while(1)
 {
   ssize_t result = recv(socket, buffer, sizeof(buffer), 0);
   if ((-1 == result) && (EINTR == error) && bExit) 
   {
     break;
   }

   ...
 }

To break your reader, first set the exit flag

bExit = 1;

then send a signal to the reader thread

pthread_kill(pthreadReader, SIGUSR1);

Note: What I left out in this example is the protection of bExit against concurrent access.

This might be achieved by using a mutex or an appropriate declaration.

alk
  • 69,737
  • 10
  • 105
  • 255
  • 2
    Note that: _If a blocked call to one of the following interfaces (recv included), is interrupted by a signal handler, then the call will be automatically restarted after the signal handler returns if the SA_RESTART flag was used; otherwise the call will fail with the error EINTR._ Also, you are confusing `errno` with `recv()` return value. – Maxim Egorushkin Aug 16 '13 at 10:01
  • @MaximYegorushkin: Thanks for pointing me to the `result`/`errno` confusion .. I somehow still was with the `pthread_*`-API which returns `errno`-values but setting `errno` itself. Also the hint regarding `SA_RESTART` is important, I missed to mention it, thanks again for this one. – alk Aug 16 '13 at 11:57
  • This always has a race condition, right? Because the `pthread_kill` in one thread might run just before the call to `recv` in the other thread, leaving you stuck. (This race is why calls like `pselect` were invented; you need a way to unblock a signal and enter the system call atomically in order for the `EINTR` approach to work reliably.) – Nemo Aug 06 '19 at 23:17
2

Declare some 'stop' boolean, check it after every recv() return and terminate if it's set. To shut down, set the bool and close the socket from another thread. The blocking recv() will return 'immediately' with an error, but it does not matter 'cos you're about to terminate anyway:)

Martin James
  • 24,453
  • 3
  • 36
  • 60
  • 4
    Note the potential race condition here if the thread ever closes the socket itself: then the other thread's call to close() might accidentally close some other socket that had been subsequently created using the same socket number. – Jeremy Friesner Aug 16 '13 at 15:44
  • Might be better to send a signal to the other thread, if your OS doesn't restart syscalls after a signal or something wacky like that. – cHao Aug 18 '13 at 07:01
  • This answer would be improved by suggesting that the other thread (the one that sets the boolean) use shutdown instead of close. Using close is dangerous because if the call to close happens right before a recv on the worker thread, then the file descriptor might be recycled and recv would receive a different part of the application's data rather the the EBADFD that the programmer might expect. – Andrew Thaddeus Martin Sep 17 '21 at 18:37
2

I would likely use signals as in @alk's fine answer (also discussed here).

Alternatively, you may use multiplexed I/O.

At initialization, create a global pipe(2). When it is time to end the program, close the write-end of the pipe — now the read-end will instantly select(2)/poll(2) readable (for EOF). Meanwhile, have your recv-blocked threads include the read-end of this pipe along with their sockets in, for example, an indefinitely blocking select(2) call. If the read-end of the pipe returns readable, the I/O threads know it is time to terminate gracefully.

The major caveat of this technique is to ensure that the write-end is closed only once, as a subsequent, naïve close(2) might nip some innocent file that happens to have been given the same descriptor as the pipe's old write-end.

Community
  • 1
  • 1
pilcrow
  • 56,591
  • 13
  • 94
  • 135
  • This is called the [self-pipe trick](http://cr.yp.to/docs/selfpipe.html). The solution is immune to race conditions associated with signals. Another variant is to create an unbound `SOCK_DGRAM` socket and simply close it to signal shutdown. – rustyx Mar 05 '20 at 11:19
  • @rustyx, no, my answer doesn't involve signals. How the pipe is closed — signal, dedicated administrative thread, etc. — is up to the implementer. Additionally DJB's original use was to write bytes to the pipe so as not to lose signals, but that's not quite the problem here. – pilcrow Mar 05 '20 at 18:58
0

Also, set your socket to be nonblocking:

int listen_sd = socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP);
if (listen_sd < 0) {
    perror("socket() failed");
}

int on = 1;

//set socket to be non-blocking
int rc = ioctl(listen_sd, FIONBIO,(char *)&on);
if (rc < 0) {
    perror("ioctl() failed");
    close(listen_sd);
}

Then, you can call recv() on the socket, and it won't block. If there's nothing to read then the ERRNO global variable is set to the constant EWOULDBLOCK

int rc = recv(listen_sd, buffer, sizeof(buffer), 0);
if (rc < 0) {
    if (errno != EWOULDBLOCK) {
        perror("recv() failed");
    }
}
gwilkins
  • 526
  • 4
  • 4
  • `if (errno != EWOULDBLOCK) {` should better be `if ((errno != EWOULDBLOCK) && (errno != EAGAIN)) {`. – alk Aug 16 '13 at 06:54
  • @alk They hav the same value when they are both defined. – user207421 Oct 24 '15 at 07:17
  • [Sometimes](https://stackoverflow.com/questions/7003234/which-systems-define-eagain-and-ewouldblock-as-different-values/7003379#7003379) `EWOULDBLOCK` != `EAGAIN`... Unrelated: the POSIX way to set non-blocking is with `fcntl(...O_NONBLOCK)` – rustyx Mar 04 '20 at 21:59