As you may or may not be aware, one of the goodies that comes in C++11
is the introduction of an explicit memory model as well as proper
support for multithreaded code. One important piece of this is
std::atomic<T>, which provides an interoperable way to do (potentially
lockless) atomic operations and, under the new memory model, is the only
safe way to handle multithreaded accesses when not protected by locks
(if you use volatile to do this right now, your code is broken and you
don't know it yet). In bug 732043, I want to add a mozilla::Atomic class
that lets us use C++11 atomics where available and fallback to compiler
intrinsics where C++11 atomics are not implemented (which amounts to gcc
4.4 and Visual Studio 2010 or earlier). What would this give us?
1. Atomic operations on non-32-bit types. Well, not all platforms
support all atomic read-modify-write operations on all word sizes (ARM
doesn't have 64-bit atomic primitives; Windows only provides a subset of
operations on 8-bit/16-bit words), but we'd have more coverage than just
atomic increment/decrement/store on 32-bit words.
2. Compare-and-swap, particularly on pointer variables. An atomic
compare-and-swap primitive is probably the most important in terms of
practically implementing high-throughput lock-free concurrent data
structures.
3. Weaker memory ordering. Current compiler intrinsics--as used in
PR_ATOMIC_INCREMENT and friends--require full memory barriers around
every access. For things like reference counters, we can get away with
not needing memory barriers if the architecture (e.g., ARM) supports it.
Note that this does require as a prerequisite compilers that speak newer
atomic intrinsics (Clang 3.1, MSVC 2012, and gcc 4.6 are the min
versions here).
The problems are the following:
1. Not all architectures support all lock-free atomic operations on all
sizes of integers; in particular, ARM cannot do atomic operations on
64-bit variables. A compiler that supports C++11 atomics would fallback
to locking-based algorithms here, but as I want to put this in MFBT, I
don't have usable lock dependencies. And I'm not keen on hand-writing
locking algorithms based on primitives either. :-) My proposed solution
to this dilemma is to forbid use of mozilla::Atomic<T> if sizeof(T) >
sizeof(uintptr_t) [rationale being that all architectures should be able
to support at least lock-free atomic operations on pointers].
2. The API I'm proposing in this instance would not allow you to use an
atomic operation on a "regular" variable, which is a big difference from
the current APIs provided by pratom.h. The rationale here is that this
is much less prone to danger (all accesses to a variable are atomic), as
well as simplifying APIs. The biggest user of PR_ATOMIC_INCREMENT right
now is for threadsafe XPCOM refcounting; to move to the new API, we
would have to restructure the nsISupports helper macros by providing an
NS_DECL_ISUPPORTS_THREADSAFE macro, although we would be able to lose
all the NS_IMPL_THREADSAFE_* variants.
3. Similar to #2, the ideal version of a reference counter would be
mozilla::Atomic<nsrefcnt, mozilla::Unordered> (which would make
threadsafe refcounting cheaper on our ARM platforms if we compiled with
gcc 4.6 or clang 3.1 or newer). However, I'm not sure that no one has
written code that relies on atomic operations having memory ordering
properties, and I don't want to go through and audit every thread-safe
XPCOM class.
4. Also, in a similar problem to the above, what should the default
consistence be for mozilla::Atomic<T>? C++11 opts to default to
sequentially-consistent, but right now, most of our uses of atomic
macros are for counter variables, which tend to want to be unordered.
5. A last technical point: there is one case in which a C++11 atomics
class can have a non-atomic access to a variable. The
value-initialization constructor of std::atomic does not store the value
atomically. This may seem crazy, but I believe that the primary purpose
is to be able to declare static-storage variables with initial values
and not require adding a global initializer. Since I think this is a
useful feature, I've been considering copying these semantics over,
although I do admit that it does appear arbitrary and incongruous.
What are your opinions? Or other
thoughts/questions/flames/comments/concerns?
_______________________________________________
dev-platform mailing list
dev-platform@lists.mozilla.org
https://lists.mozilla.org/listinfo/dev-platform