views:

181

answers:

6

Hi all, so I have a boolean type in C++ on a multiprocessor machine. The variable starts out life as true, and then there are a few threads, any one or more of which might write it to be false.

At the same time, these threads may also read this variable to check its state. I don't care if reading this variable is synchronized with any of the writes, they each happen at different places in the code, and it doesn't matter if it comes before or after any particular write. Now, do I need a lock for this boolean?

The only way I would need a lock is if at the very low level, memory can get corrupted by two competing writes. If, for example, an assembly instruction on processor A is writing 0 to the byte that represents the boolean at the same time as processor B is doing the same... and instead of having written 0, the memory ends up with value 22 or something. That could mess something up.

So, generally, if proc A is writing 3 to a memory location, while proc B is writing 7, with no synchronization, am I guaranteed to end up with at least either 3 or 7? Or is it that easy to break memory?

Edit:

Thanks for the comments guys. Some more info: There is synchronization in the program of course. To summarize, the flag in question is telling if a certain memory pool is "dirty" (needs to be compacted). Any thread can hence decide to set this flag to false (meaning pool is dirty). For example, freeing memory from the pool makes it dirty. Any thread can then also read this flag and set another flag to signal that a clean-up is needed -- this check is done when memory is allocated from the pool, the clean-up is signaled if we're low on memory. Somewhere in my master critical section between iterations, where each thread goes to look for more data to process, I will have the threads check this second flag, and do something appropriate to make sure that: all other theads finish their current iteration, one thread cleans up the memory, sets the first flag back to true (as in pool is not dirty), sets the second flag back to false, and then releases all the threads again.

So I don't think I need a lock because: a lock would ensure that a write doesn't happen at the same time as another write or a read. But who cares, as long as the hardware doesn't fail me, the worst-case-scenario is that the read happens randomly before or after the write -- this is the same thing that would happen if I protected it with a lock, just then we'd really be sure it came before or after...

I think the same argument applies to the second flag I mentioned above.

+1  A: 

It's an easy way to break things. With a boolean, you may be ok most of the time, but are provided no guarantees.

You have two options: use a mutex (lock), or use atomic primitives. Atomic primitives will utilize hardware instructions to do the test and set operations in a thread-safe fashion without requiring an actual mutex and are a lighter-weight solution. The GNU compiler provides access to atomic operations via architecture-specific extensions. There are also portable atomic operation libraries floating around; the Glib C library provides atomic operations that fall back to using a mutex if atomic primitives are not available, although it is a fairly heavy library with many other features as well.

There is the Boost.Atomic library which abstracts atomic operations for C++; based on its name, it looks like it is aiming to be incorporated into the Boost C++ library collection but hasn't yet made it.

Michael E
A: 

For a bool, the answer is generally no, a mutex is not needed, but (as Michael E noted) anything could happen, so you likely need to understand more about your arch before making such a decision. Another note: the code might still need a lock around the overall logic associated with the bool, especially if the bool is read more than once in the course of a routine's logic.

Some great blogs I read to keep me on my multithreaded toes:

http://blogs.msdn.com/b/nativeconcurrency/

http://herbsutter.com/2009/04/20/effective-concurrency-use-thread-pools-correctly-keep-tasks-short-and-nonblocking/

best regards,

M. Esh.
+4  A: 

On most commodity hardware a single word reads/writes are atomic, so no, two (or more) competing writes (and reads) to the same memory location are not going to corrupt the value. What is important here is cache coherence between CPUs.

Again, on commodity hardware you might get away with just marking that single boolean variable volatile (which has been declared useless for concurrent programming btw) to prevent compiler from optimizing it out into a register, but only if you really don't care about the order of writes.

Let me re-iterate this with a check-list:

  • Are you ready do lose some updates to that boolean?
  • Are you sure no other memory updates that come before the boolean flip in the source code but might get reordered after that flip are going to mess things up?
  • Are you sure you don't care about order of events in your application?

If you have three strong "yes" answers, you might get away with not protecting that flag. Still consider inserting acquire memory barrier before reading the variable, and release memory barrier before writing it. My suggestion though would be to re-think the design, and lay out clear synchronous inter-thread communications and event sequencing.

Hope this helps.

Nikolai N Fetissov
Great answer, thanks for mentioning cache coherence.
Chris O
1) You say lose updates to that boolean... do you mean that the hardware would ignore a write? I don't think so, so I'm not sure what you mean (you could see my addendum above)2) yes3) some things i care about. note i never both read and write this flag in the same block of code. always read once or write once. -- this is essentially why synchronization seems useless here. I think we found a use for volatile! :)
Scott
also important to note i'm not modelling a "wait for.. " scenario anywhere here. I never want a thread to wait for false, or wait for anything. go go go!
Scott
You mention cache coherence, but you don't say what it is about it that matters. Unless you're working on some pretty exotic hardware, cache coherence can be assumed to ensure that anything written by one core will be seen by others. Also, `volatile` does prevent reordering *with respect to other `volatile`* objects. @Scott: no, you've found a good place to use a memory barrier, not a use for `volatile`.
jalf
@Scott, hardware does not ignore writes, your threads might overwrite each other's updates though.@jalf, reading on cache coherence was left as an exercise to the reader :)
Nikolai N Fetissov
A: 

You are asking about two things.

First you are asking about atomicity of bool assignment. No there is no guarantee that boolean assignment will be atomic operation. In practice it usually is, but you shouldn't rely on this. Some strange architecture may implement bool assignment in many machine instructions...

Secondary you are asking about corruption of data due to parallel writes - in practice transport from CPU to memory is done by a bus, which almost always contains more bits than primitive types you are working on. So such corruption may happen for very strange architecture or when working on big numbers (not supported by the system natively). In practice you will usually get 3 or 7. But again, you can't rely on it.

To conclude this - you need a lock.

doc
+2  A: 

If only you're checking the state of the variable and setting it to false in one direction ever, there's not much to worry about except that some of the threads may be a little late to see the variable is already set to false. (this may be overcome to some degree by the use of 'volatile' keyword.) Then two threads may set it to false, which is no problem since the variable is set to a single value in one direction. Let's say boolean write to the memory location is not guaranteed to be atomic, what's the harm? The final value they both will write is the same.

Still, you would have to use a method of locking if:

  • Value setting is not one-direction only: you set it to false, then back to true, then false again, etc.
  • You take some dependent action on the information which thread had set the value to false. Because there obviously may be two winners.
Gorkem Pacaci
A: 

On most commodity hardware single word read and writes are not atomic operations; remember you have virtual memory machines here and either of those operations might cause a page fault.

In more general terms you'll probably find it easier and quicker to use mutexes as a matter of routine than scratch your head wondering if you are going to get away without one this time. Adding one where it isn't necessary doesn't cause a bug; leaving one out where it is might.

Brian Hooper