I have 2 threads and a shared float
global. One thread only writes to the variable while the other only reads from it, do I need to lock access to this variable? In other words:
volatile float x;
void reader_thread() {
while (1) {
// Grab mutex here?
float local_x = x;
// Release mutex?
do_stuff_with_value(local_x);
}
}
void writer_thread() {
while (1) {
float local_x = get_new_value_from_somewhere();
// Grab mutex here?
x = local_x;
// Release mutex?
}
}
My main concern is that a load or store of a float
not being atomic, such that local_x
in reader_thread
ends up having a bogus, partially updated value.
- Is this a valid concern?
- Is there another way to guarantee atomicity without a mutex?
- Would using
sig_atomic_t
as the shared variable work, assuming it has enough bits for my purposes?
The language in question is C using pthreads.
According to section 24.4.7.2 of the GNU C library documentation:
float
technically doesn't count under these rules, although if afloat
is the same size as anint
on your architecture, what you could do is make your global variable anint
, and then convert it to a float with a union every time you read or write it.The safest course of action is to use some form of mutex to protect accesses to the shared variable. Since the critical sections are extremely small (reading/writing a single variable), you're almost certainly going to get better performance out of a light-weight mutex such as a spin lock, as opposed to a heavy-weight mutex that makes system calls to do its job.
I would lock it down. I'm not sure how large
float
is in your environment, but it might not be read/written in a single instruction so your reader could potentially read a half-written value. Remember thatvolatile
doesn't say anything about atomicity of operations, it simply states that the read will come from memory instead of being cached in a register or something like that.The assignment is not atomic, at least for some compilers, and in the sense that it takes a single instruction to perform. The following code was generated by Visual C++ 6.0 - f1 and f2 are of type float.
Different architectures have different rules, but in general, memory loads and stores of aligned,
int
-sized objects are atomic. Smaller and larger may be problematic. So ifsizeof(float) == sizeof(int)
you might be safe, but I still wouldn't depend on it in a portable program.Also, the behavior of
volatile
isn't particularly well-defined... The specification uses it as a way to prevent optimizing away accesses to memory-mapped device I/O, but says nothing about its behavior on any other memory accesses.In short, even if loads and stores are atomic on
float x
, I would use explicit memory barriers (though how varies by platform and compiler) in instead of depending onvolatile
. Without the guarantee of loads and stores being atomic, you would have to use locks, which do imply memory barriers.In all probability, no. Since you have no chance for write collision the only concern is whether you could read it while it's half-written. It's hugely unlikely that your code is going to be run on a platform where writing a float doesn't happen in a single operation if you're writing something with threads.
However it's possible because the definition of a float in C does not mandate that the underlying hardware storage be limited to the processor's word size. You could be compiling to machine code where, say, sign and mantissa are written in two different operations.
The real question, I think, is two questions: "what's the downside to having a mutex here?" and "What's the repercussions if I get a garbage read?"
Perhaps rather than a mutex you should write an assert that determines whether the storage size of a float is smaller or equal to the word size of the underlying CPU.
I believe you should use a mutex. Someday, your code might run on a system that does not have real floating point hardware and uses emulation instead, resulting in non-atomic float variables. For an example, look at -msoft-float here.My answer was not very useful.
gcc -msoft-float
is possibly just a specific case where loads and stores of floats are not atomic.