How to register an update in a callback from another thread without using locks?

This is one of those questions that seems to fall into the category of β€œnaively visible, but probably wrong.” Of course, I'm struggling to find a solution that works in all cases. It seems that this should be a problem that occurs all the time.

I have a "hardware" thread and a "processing" thread.

The hardware thread runs in a private binary library that I do not have access to.

The processing thread registers a callback with the hardware thread in order to receive notifications of some (rare) events associated with changes in the state of the hardware.

I want to be able to notify the event loop in a thread that has changed state. I would like to do this without requiring a dependency on the external library or in a non-portable way, and without using locks (since I have no idea when the hardware thread can again notify the processing thread).

So now I’m thinking about how to solve this problem, something like this:

#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 will break if ever the update wraps local_update and just reaches the local_update value before the processing_function is called (although I can almost certainly assume that this will never happen).

Am I missing something subtle (or not so subtle) here?

Is there a better way to solve this problem?

A callback can be considered called only from one thread (hardware thread).

+4
source share
3 answers

One option is to use the standard unix trick with the recorder . The advantage is that the read end of the pipe can be used with select() or epoll() , thus combining perfectly with event loops without the need to periodically query the value of the atomic variable.

 #include <unistd.h> #include <errno.h> int self_pipe[2]; // the read end is non-blocking // called from hardware thread int callback_function() { write(self_pipe[1], "", 1); // notify by writing a byte return 0; } // called only when self_pipe[0] is ready for read int processing_function() { for(;;) { char buf; ssize_t n = read(self_pipe[0], &buf, 1); if(n > 0) { // callback_function() has been called } else if(n < 0) { if(EAGAIN == errno) break; // handle an (unlikely) error } else { // handle pipe EOF if necessary } } return 0; } 

If you use Linux> = 2.6.22 instead of a pipe, you can use eventfd , which is basically a semaphore supported in the kernel and works with read() and write() syscalls. Again, the advantage of this is that it can be used with event loops.

+3
source

Do you have access to compare-and-swap operation? Then you can do something like this in your processing thread:

 int ticks_since_last_check = 0, ticks_ret = 0; do { ticks_since_last_check = ticks_ret; ticks_ret = compare_and_swap(&update, ticks_since_last_check, 0)); } while (ticks_since_last_check != ticks_ret) 

This will reset the tick counter to 0 every time the processing thread checks its value, so the counter will not overflow. After that, you just need to run the update_internal_hardware_state ticks_since_last_check times function.

Alternatively, you can use atomic decrement:

 int ticks_since_last_check = 0; while (atomic_read(&update) != 0) { atomic_decrement(&update); ++ticks_since_last_check; } 
+2
source

You seem to have implemented semaphore manually.

Instead, why not use the right semaphore for your platform? Posix Semaphores exist on the ARM platform. I am not sure why you do not want to use external libraries, if you insist on it then this can help .

+2
source

All Articles