views:

146

answers:

5

I have the following situation:

There is a thread that reads from a device with a fread call. This call is blocking as long as there is no data send from the device. When I stop this thread it remains hanging inside this thread.

Now I found the following inside the man page of fread:

ERRORS

On all systems that conform to the Single UNIX Specification, the fread() function sets errno as listed for the following conditions:

[EINTR] The read operation was terminated due to the receipt of a signal, and no data was transferred.

That would mean there is a way to interrupt the call from a different thread. But I have no idea how. Can someone tell me how to send a signal to interrupt the fread call? And what signal I need to send?


Update 08-10-10 09:25

I still haven't got it to work. I tryed the kill() and pthread_kill() with different signals. But nothing seems to interrupt the fread() call. The only thing that I got working is killing the entire application, but that's not what I want.

+3  A: 

Take a look at man 2 kill. (Or see here)

I get the feeling that you don't want to do this, though--most of the time people ignore errno EINTR and read again. You might want to look into non-blocking reads instead.

Platinum Azure
Can you tell me what signal I need to send with the kill() system call to interrupt the fread()?
Peerke
Generally speaking, any signal will interrupt the `fread()` call. That said, generally speaking, many signals will usually also kill your application (except SIGCONT). I would use SIGHUP or one of the user-defined signals personally. But like I said in my answer, I also don't usually like using signals at all for this purpose. (See John Marshall's answer for an alternative)
Platinum Azure
In addition to generating a signal from another thread with `kill`, the `alarm` function is often used to cause a blocking `read` call to time out.
Ben Voigt
@Ben Voigt: yes, and I still think it's ugly every time I see it.
ninjalj
@ninjalj: Not disagreeing with you, I much prefer to use `poll` or aio, which have all the benefits of `select` with none of the clunky argument setup logic. But it's useful to know patterns such as use of `alarm` to set a timeout on a blocking i/o call so you recognize them when reading existing code.
Ben Voigt
A: 

You can use the kill() syscall.

UPDATE

Turns out I misread your question. As R. pointed out below, kill() is only for killing processes, not threads.

Oli Charlesworth
Can you tell me what signal I need to send with the kill() system call to interrupt the fread()?
Peerke
It has to be a signal for which you have previously set up a signal handler (see `sigaction()`) and have *not* used the `SA_RESTART` flag.
mark4o
`kill` will not work. You need `pthread_kill` which can send the signal to a specific thread.
R..
Good point; answer updated...
Oli Charlesworth
`kill()` is ok if the signal is blocked in the other threads.
mark4o
+3  A: 

You really want to read about the select(2) system call, which will allow you to find out whether there is data available on that file descriptor, without blocking at all or without blocking on only that device.

John Marshall
While select() would be useful, it's not really answering the question.
Michael Foukarakis
@Michael Foukarakis - Given the context, the question may be asking for the [wrong solution to the larger problem](http://www.perlmonks.org/index.pl?node_id=542341). John gave a reasonable answer to help the OP reach his goal by addressing the larger problem. Downvote seems a bit harsh.
bstpierre
I can't judge the solution without knowing the problem, can I? Maybe the OP *has* to use `fread()`. I don't know, and I'd rather not assume.
Michael Foukarakis
+2  A: 

In the thread, instead of blocking with fread, block with select. When select returns, check an "am I done" variable. If not done, you can call fread to get the data.

From the other thread -- that wants to stop the fread thread -- you can set the "am I done" variable and then close the fd so that the fread thread will wake up from select immediately.

If your context prohibits you from closing the fd (you mention you're reading from a device, but say you had a socket you wanted kept open), you could open a second fd that you write to from the other thread to wake up select.

As suggested in the comments below, closing the fd to wake up select may not be portable. You can use the second-fd strategy mentioned above to achieve this more portably.

bstpierre
Or you could `close()` the fd passed to `select()` in order to cancel it immediately, instead of using context-switching cache-missing battery-wasting polling logic.
Ben Voigt
@Ben Voigt - Thanks for pointing that out. Edited my answer accordingly.
bstpierre
@Ben: très cool, but is it specified to work that way? I skimmed the select(2) and Threads sections of the SUS spec, but didn't notice any description of what to expect to see when an fd is disappeared out from under it while select() is blocking. OTOH I suppose something sensible had better happen or else there is a potential for exploits...
John Marshall
@John: seems that `select` is supposed to return `EBADF` under such conditions, but if that strikes you as non-portable bstpierre is already on the right track for another approach that is just slightly more work: pass a second file descriptor to `select` where the second one can be written to (instead of closed) in order to wake the thread early.
Ben Voigt
+5  A: 

1. Signals:

Using signals, as many others pointed out, would work. However, as many others also pointed out, the approach has its disadvantages.

2. Select():

Using select() (or other multiplexing function), you can block waiting for data to arrive from more than one file descriptor, and specify a timeout.

Use the timeout to your advantage. Whenever select() returns, check a global variable to see if you must terminate. If you want immediate reaction, keep reading.

3. Select() and pipes:

Multiple fds means you can wait for data arriving through the device you mentioned and, say, a pipe.

Before you create the thread, create a pipe, and then have the thread block on select() monitoring both the device and the pipe. Whenever you want to unblock select whether the device has new data or not, send a byte down the pipe.

If select() tells you it unblocked due to data arriving through the pipe, you can clean up and terminate. Note this method is much more flexible than the signaling method, since you can, besides just using the pipe as a wake-up method, use it to pass useful information or commands.

4. Select(), pipes and signals:

If you are using multiple processes and don't want to/can't pass around a pipe, you can combine both solutions. Create a pipe and install a signal handler for, say, SIGUSR1. In the signal handler, send a byte down the pipe.

Whenever a process sends SIGUSR1, the handler will be called and unblock select(). By examining the fdsets, you will know it was for no other reason than your own program signaling itself.

Santiago Lezica