3

This is a question similar to Proper way to close a blocking UDP socket. I have a thread in C which is reading from a UDP socket. The read is blocking. I would like to know if it is possible to be able to exit the thread, without relying on the recv() returning? For example can I close the socket from another thread and safely expect the socket read thread to exit? Didn't see any high voted answer on that thread, thats why I am asking it again.

Community
  • 1
  • 1
bobby
  • 2,629
  • 5
  • 30
  • 56

5 Answers5

4

This really depends on what system you're running under. For example, if you're running under a POSIX-compliant system and your thread is cancelable, the recv() call will be interrupted when you cancel the thread since it's a cancel point.

If you're using an older socket implementation, you could set a signal handler for your thread for something like SIGUSR1 and hope nobody else wanted it and signal, since recv() will interrupt on a signal. Your best option is not to block, if at all possible.

Jason Coco
  • 77,985
  • 20
  • 184
  • 180
  • The cancel seems to be a promising idea. Is there any documentation available on the cancel points for a thread? I saw [link](http://www.mkssoftware.com/docs/man3/pthread_cancel.3.asp) It doesnt list any socket related calls. Or does `recv()` in turn use some other call like `read()` for example – bobby Aug 19 '11 at 08:24
  • @jogabonito If your system is posix-compliant and uses posix-compliant sockets, it will be. [posix cancel points](http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_09_05_02) – Jason Coco Aug 19 '11 at 08:28
3

I don't think closing a socket involved in a blocking operation is a safe guaranteed way of terminating the operation. For instance, kernel.org warns darkly:

It is probably unwise to close file descriptors while they may be in use by system calls in other threads in the same process. Since a file descriptor may be reused, there are some obscure race conditions that may cause unintended side effects.

  • Instead you could use a signal and make recv fail with EINTR (make sure SA_RESTART is not enabled). You can send a signal to a specific thread with pthread_kill

  • You could enable SO_RCVTIMEO on the socket before starting the recv call

Personally I usually try to stay clear of all the signal nastiness but it's a viable option.

cnicutar
  • 178,505
  • 25
  • 365
  • 392
2

You've got a couple of options for that. A signal will interrupt the read operation, so all you need to do is make sure a signal goes off. The recv operation should fail with error number EINTR.

The simplest option is to set up a timer to interrupt your own process after some timeout e.g. 30 seconds:

itimerval timer
timeval time;
time.tv_sec = 30;
time.tv_usec = 0;
timer.it_value = time;
if( setitimer( ITIMER_REAL, &timer, NULL ) != 0 )
  printf( "failed to start timer\n" );

You'll get a SIGALRM after the specified time, which will interrupt your blocking operation, and give you the chance to repeat the operation or quit.

asc99c
  • 3,815
  • 3
  • 31
  • 54
2

You cannot deallocate a shared resource while another thread is or might be using it. In practice, you will find that you cannot even write code to do what you suggest.

Think about it. When you go to call close, how can you possibly know that the other thread is actually blocked in recv? What if it's about to call recv, but then another thread calls socket and gets the descriptor you just closed? Now, not only will that thread not detect any error, but it will be calling recv on the wrong socket!

There is probably a good way to solve your outer problem, the reason you need to exit from a blocking UDP socket read. There are also several ugly hacks available. The basic approach is to make the socket non-blocking and instead of making a blocking UDP socket read, fake a blocking read with select or poll. You can then abort this loop several ways:

One way is to have select time out and check an 'abort' flag when select returns.

Another way is to also select on the read end of a pipe. Send a single byte to the pipe to abort the select.

David Schwartz
  • 179,497
  • 17
  • 214
  • 278
1

If posix complient system, you can try to monitor your thread: pthread_create with a function that makes your recv and pthread_cond_signal just after, then returns.
The calling thread makes a pthread_cond_timedwait with the desired timeout and terminates the called thread if timed_out.