Search code examples
c++c++11c++14atomicmutable

Does “M&M rule” applies to std::atomic data-member?


"Mutable is used to specify that the member does not affect the externally visible state of the class (as often used for mutexes, memo caches, lazy evaluation, and access instrumentation)." [Reference: cv (const and volatile) type qualifiers, mutable specifier]

This sentence made me wonder:

"Guideline: Remember the “M&M rule”: For a member-variable, mutable and mutex (or atomic) go together." [Reference: GotW #6a Solution: Const-Correctness, Part 1 (updated for C ++11/14)]

I understand why “M&M rule” applies to std::mutex data-member: to allow const-functions to be thread-safe despite they lock/unlock the mutex data-member, but does “M&M rule” applies also to std::atomic data-member?


Solution

  • You got it partly backwards. The article does not suggest to make all atomic members mutable. Instead it says:

    (1) For a member variable, mutable implies mutex (or equivalent): A mutable member variable is presumed to be a mutable shared variable and so must be synchronized internally—protected with a mutex, made atomic, or similar.

    (2) For a member variable, mutex (or similar synchronization type) implies mutable: A member variable that is itself of a synchronization type, such as a mutex or a condition variable, naturally wants to be mutable, because you will want to use it in a non-const way (e.g., take a std::lock_guard) inside concurrent const member functions.

    (2) says that you want a mutex member mutable. Because typically you also want to lock the mutex in const methods. (2) does not mention atomic members.

    (1) on the other hand says that if a member is mutable, then you need to take care of synchronization internally, be it via a mutex or by making the member an atomic. That is because of the bullets the article mentions before:

    If you are implementing a type, unless you know objects of the type can never be shared (which is generally impossible), this means that each of your const member functions must be either:

    • truly physically/bitwise const with respect to this object, meaning that they perform no writes to the object’s data; or else
    • internally synchronized so that if it does perform any actual writes to the object’s data, that data is correctly protected with a mutex or equivalent (or if appropriate are atomic<>) so that any possible concurrent const accesses by multiple callers can’t tell the difference.

    A member that is mutable is not "truly const", hence you need to take care of synchronization internally (either via a mutex or by making the member atomic).

    TL;DR: The article does not suggest to make all atomic members mutable. It rather suggests to make mutex members mutable and to use internal synchronization for all mutable members.