This is one of those questions that seems to fall into the "naively obvious but probably wrong" category. Certainly, I'm struggling to find a solution that works in all corner cases. It seems like this must be a problem that is encountered all the time.
I have a "hardware" thread and a "processing" thread.
The hardware thread is run in a closed binary library that I don't have access to.
The processing thread registers a callback with the hardware thread to be notified of certain (rare) events related to state changes in the hardware.
I wish to be able to notify the event loop in the processing thread that the state has changed. I'd like to do this without requiring a dependance on an external library or in a non portable way, and without using locks (as I have no idea when the hardware thread may want to notify the processing thread again).
So, my thinking at present as to how to solve this is something like as follows:
#include <signal.h>
// sig_atomic_t is used so update is always in a sane state
static volatile sig_atomic_t update = 0;
// called from hardware thread
int callback_function() {
update += 1;
}
// called regularly from processing thread
int processing_function() {
static sig_atomic_t local_update = 0; // The same type as update
if (local_update != update){
update_internal_hardware_state(); // We necessarily call once per callback
local_update += 1;
}
}
Clearly this is going to break if ever update
wraps around and just happens to reach the value of local_update
before processing_function
is next called (though almost certainly I can assume this will never happen).
Have I missed something subtle (or not so subtle) here?
Is there a better way to solve this whole problem?
The callback can be assumed to be only ever called from one thread (the hardware thread).
One options is to use the standard unix self-pipe trick. The benefit is that the read end of the pipe can be used with
select()
orepoll()
thus integrating nicely with event loops without having to periodically poll the value of an atomic variable.If you are on Linux >= 2.6.22 instead of pipe you can use an
eventfd
, which is basically a semaphore maintained in the kernel and operated withread()
andwrite()
syscalls. Again, the benefit of it is that it can be used with event loops.