4

C++ 11x standard changed semantic of "const" keyword. Now it means real thread safety. As far as I understand const member variable is equal to java final field.

I want to check that on CPU icore 7 G++ 4.7.1.

I compiled following code with as -std=c++0x -pthread -DCONST and without. Both executable are identical. Assembler versions don't have *fence instructions. I expected to see *fence at the end of the constructor.

class Big {
public:
#ifdef CONST
  const
#endif
  long a;
  Big(long a) : a(a) {
  }
  void check()
 #ifdef CONST
  const 
 #endif
  {
    assert(a == 123L);
  }
};

int main() {
  Big b(123L);
  thread t([b] () { b.check(); });
  return 0;
}
Daniil Iaitskov
  • 5,525
  • 8
  • 39
  • 49
  • I think this is a misunderstanding. It is up to you to make sure that something labeled `const` is safe when called from many threads. – juanchopanza Apr 01 '14 at 14:33
  • There's no such thing like race conditions on accessing constant values! – πάντα ῥεῖ Apr 01 '14 at 14:38
  • 4
    Race conditions apply to read/write scenarios. Since `const` variables are read only by definition (or should be), there is no problem in accessing it simultaneously by multiple threads. Race condition could happen on initialisation. However, it's always a programmer's task to make sure this does not happen. – W.B. Apr 01 '14 at 14:40
  • 4
    http://stackoverflow.com/questions/14127379/does-const-mean-thread-safe-in-c11 – IdeaHat Apr 01 '14 at 14:43
  • @W.B. In C++, `const` access can also result in state change of `mutable` data members. Which is what I was what I had in mind when I posted my first comment. – juanchopanza Apr 01 '14 at 14:45
  • CPU doesn't know whether a memory cell const or not. What if memory of B was reclaimed and reused and some CPU core will see the old value from cache. That left after object existed before B? – Daniil Iaitskov Apr 01 '14 at 14:46
  • 5
    C++11 didn't change the meaning of `const`. It provides an additional guarantee about `const` members *within the standard library* being thread-safe. It does not mean "anything marked `const` in user code must be (or will be) thread-safe" (see also [here](http://stackoverflow.com/questions/14127379/does-const-mean-thread-safe-in-c11/14127380#14127380)) – jalf Apr 01 '14 at 14:47
  • @W.B. I don't have any questions. This is all clear to me :-) – juanchopanza Apr 01 '14 at 14:48
  • Here I meant thread safety is if a thread sees this of the complete object then it also sees right value of member a. – Daniil Iaitskov Apr 01 '14 at 14:48
  • @DaneelS.Yaitskov How could it see the wrong value in any scenario? Nothing is writing to `a` after initialization. – juanchopanza Apr 01 '14 at 14:59
  • Initialization is just a language abstraction. CPU see a write at address of a. What protect from the situation: another thread on another core change the same address before and new object is passed to that thread. – Daniil Iaitskov Apr 01 '14 at 15:04
  • @DaneelS.Yaitskov: how exactly are not completely constructed objects passed to other threads (when not using insane constructs)? – stefaanv Apr 01 '14 at 15:15
  • You would have to work hard to engineer such a situation. In your example, the object is created in one thread, and the next thread only reads from it. If you wanted to write, the compiler would complain that you are writing to something marked as `const`. If you marked the data member `mutable`, then the thread would be able to perform a write to a "const" object. – juanchopanza Apr 01 '14 at 15:18
  • I believe you may be referring to the "You dont know __ and __" talk by Herb Sutter. If so, he doesn't say that const *makes* something thread safe. He says that accessing the standard library classes *only* through const members is guaranteed to be thread safe and that because of an accidental (and lucky) combination of rules, your own classes must do that too (which luckily is normally the case by default since const implies no writes). You are effectively required to synchronize mutables to maintain this invariant. const *only* access is normally safe because races are caused by writes. – John5342 Apr 01 '14 at 18:01

2 Answers2

1

This is more of a general Computer Science question than a code question exactly, but that's fine, interesting even.

Let's establish some points, and if you have any issue with these points or my subsequent reasoning, then let me know. Just to be clear, I use the word observe to kind of mean "read", and modify to kind of mean "write". This is not a formal proof.

  1. Any number of threads may observe an object concurrently without incurring a race condition, as long as no thread concurrently modifies that object.

  2. A const object is modified during initialization, and not otherwise be modified during the lifetime of that object (except for destruction I suppose).

  3. Initialization happens once during the lifetime of an object, in exactly one thread, before that object can be otherwise modified or observed (you technically can access an uninitialized object, but if you manage to pull that off a lot of things are broken regardless of thread-safety).

#2 and #3 guarantee that a const member can not be concurrently modified and observed, which is thread safe by rule #1.

Now... mutable changes things and once you start making mutable members, the onus is upon the class writer to make sure const methods using mutable members are handled in a thread-safe manner.

So, the compiler does not guarantee thread safety... but logic does guarantee thread safety. As long as an object is const, all of its mutable members are handled in a thread-safe manner, and all of that object's const members observe these rules, you have a guarantee of thread safety.

The C++11 change as I understand is that where std:: objects did not previously guarantee well-behaved thread-safe const members, we now have that guarantee.

The video Herb Sutter - You don't know [blank] and [blank] is as far as I can tell the source for the claim that C++11 guarantees that const == thread-safe, and explains the claim well.

QuestionC
  • 10,006
  • 4
  • 26
  • 44
  • wrt. the first half of your post, it's not just mutable causing problems. It's also something as simple as pointers. Say you have a const object containing a pointer. Sure, because the object is const, so is the pointer. But the object it points *to* is not made const by this, and can be modified. Which is not thread-safe. (Think of a smart pointer class, for a simple example. Or a const vector of non-const objects). But you're right about the C++11 change, which is the important part. :) – jalf May 05 '14 at 12:24
0

It is not a direct synonym. Assume for example that you have a const pointer: it means that the adress itself is constant but not necessarily its content. So any function thatt would modify the content of this variable, when executed by several threads, is not considered to be thread-safe.

Gabriel
  • 3,564
  • 1
  • 27
  • 49