PaulH
PaulH

Reputation: 7873

why read a reference counter value as a reference-to-a-volatile-constant?

In the Boost 1.5.1 source under smart_ptr\detail\atomic_count_win32.hpp is a neat little atomic reference counter boost::detail::atomic_count.

on line 48, they do a cast I'm curious about:

class atomic_count
{
public:

// ...

operator long() const
{
    return static_cast<long const volatile &>( value_ );
}

private:
long value_;

Why is the counter value cast to a-reference-to-a-volatile-constant-long (long const volatile&)?

Upvotes: 4

Views: 122

Answers (2)

David Schwartz
David Schwartz

Reputation: 182865

On x86 platforms, for aligned values of native width, this is known to be sufficient.

The problem they're trying to avoid is this:

  1. The variable has the hex value 0000FFFF.

  2. Thread A starts to read the value and gets the 0000xxxx part.

  3. Thread B increments the value from 0000FFFF to 00010000.

  4. Thread A finishes reading the value, getting the xxxx0000 part that it hadn't read yet.

  5. Thread A has now read a value of 00000000!

This is called word tearing. However, it is known that this doesn't happen for aligned types of native width on x86. So a mere cast through volatile (which is known to avoid problematic compiler optimizations) is all that is needed.

Note that this is not some general truth. This just happens to be a property of the platform. This isn't portable code.

Upvotes: 1

GManNickG
GManNickG

Reputation: 504293

MSVC provides a now-deprecated extension on volatile variables, giving them acquire and release semantics (memory ordering guarantees, with respect to multithreaded programming.)

This cast "enables" this extension on the variable, giving it read-acquire semantics (to match any release-writes that may also occur). Again, this is, non-standard. In C++11 code you should use std::atomic<>.

They need this because boost::shared_ptr gives guarantees of correctness for shared_ptr<T> in multithreaded (shared) use; this is their implementation of a lock-free counter.

(Also, this is only half the story: while this extension may provide the needed ordering and visibility guarantees, it does not guarantee atomicity. On Win32 this is guaranteed implicitly by the platforms it runs on: aligned word-sized integer reads and writes are atomic per the platform.)

To nip it in the bud before it starts: without this extension volatile is not useful for multithreaded programming. Don't even try. This extension is deprecated, so you should really avoid it if you can.

Upvotes: 5

Related Questions