| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474 |
- //
- // Copyright (c) Microsoft. All rights reserved.
- // Licensed under the MIT license. See LICENSE file in the project root for full license information.
- //
- //
- // Volatile.h
- //
- //
- // Defines the Volatile<T> type, which provides uniform volatile-ness on
- // Visual C++ and GNU C++.
- //
- // Visual C++ treats accesses to volatile variables as follows: no read or write
- // can be removed by the compiler, no global memory access can be moved backwards past
- // a volatile read, and no global memory access can be moved forward past a volatile
- // write.
- //
- // The GCC volatile semantic is straight out of the C standard: the compiler is not
- // allowed to remove accesses to volatile variables, and it is not allowed to reorder
- // volatile accesses relative to other volatile accesses. It is allowed to freely
- // reorder non-volatile accesses relative to volatile accesses.
- //
- // We have lots of code that assumes that ordering of non-volatile accesses will be
- // constrained relative to volatile accesses. For example, this pattern appears all
- // over the place:
- //
- // static volatile int lock = 0;
- //
- // while (InterlockedCompareExchange(&lock, 0, 1))
- // {
- // //spin
- // }
- //
- // //read and write variables protected by the lock
- //
- // lock = 0;
- //
- // This depends on the reads and writes in the critical section not moving past the
- // final statement, which releases the lock. If this should happen, then you have an
- // unintended race.
- //
- // The solution is to ban the use of the "volatile" keyword, and instead define our
- // own type Volatile<T>, which acts like a variable of type T except that accesses to
- // the variable are always given VC++'s volatile semantics.
- //
- // (NOTE: The code above is not intended to be an example of how a spinlock should be
- // implemented; it has many flaws, and should not be used. This code is intended only
- // to illustrate where we might get into trouble with GCC's volatile semantics.)
- //
- // @TODO: many of the variables marked volatile in the CLR do not actually need to be
- // volatile. For example, if a variable is just always passed to Interlocked functions
- // (such as a refcount variable), there is no need for it to be volatile. A future
- // cleanup task should be to examine each volatile variable and make them non-volatile
- // if possible.
- //
- // @TODO: link to a "Memory Models for CLR Devs" doc here (this doc does not yet exist).
- //
- #ifndef _VOLATILE_H_
- #define _VOLATILE_H_
- // xplat-todo: remove?
- // #ifndef CLR_STANDALONE_BINDER
- // #include "staticcontract.h"
- // #endif
- //
- // This code is extremely compiler- and CPU-specific, and will need to be altered to
- // support new compilers and/or CPUs. Here we enforce that we can only compile using
- // VC++, or GCC on x86 or AMD64.
- //
- #if !defined(_MSC_VER) && !defined(__GNUC__)
- #error The Volatile type is currently only defined for Visual C++ and GNU C++
- #endif
- #if defined(__GNUC__) && !defined(_X86_) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64_)
- #error The Volatile type is currently only defined for GCC when targeting x86, AMD64, ARM or ARM64 CPUs
- #endif
- #if defined(__GNUC__)
- #if defined(_ARM_) || defined(_ARM64_)
- // This is functionally equivalent to the MemoryBarrier() macro used on ARM on Windows.
- #define VOLATILE_MEMORY_BARRIER() asm volatile ("dmb sy" : : : "memory")
- #else
- //
- // For GCC, we prevent reordering by the compiler by inserting the following after a volatile
- // load (to prevent subsequent operations from moving before the read), and before a volatile
- // write (to prevent prior operations from moving past the write). We don't need to do anything
- // special to prevent CPU reorderings, because the x86 and AMD64 architectures are already
- // sufficiently constrained for our purposes. If we ever need to run on weaker CPU architectures
- // (such as PowerPC), then we will need to do more work.
- //
- // Please do not use this macro outside of this file. It is subject to change or removal without
- // notice.
- //
- #define VOLATILE_MEMORY_BARRIER() asm volatile ("" : : : "memory")
- #endif // !_ARM_
- #elif defined(_ARM_) && _ISO_VOLATILE
- // ARM has a very weak memory model and very few tools to control that model. We're forced to perform a full
- // memory barrier to preserve the volatile semantics. Technically this is only necessary on MP systems but we
- // currently don't have a cheap way to determine the number of CPUs from this header file. Revisit this if it
- // turns out to be a performance issue for the uni-proc case.
- #define VOLATILE_MEMORY_BARRIER() MemoryBarrier()
- #else
- //
- // On VC++, reorderings at the compiler and machine level are prevented by the use of the
- // "volatile" keyword in VolatileLoad and VolatileStore. This should work on any CPU architecture
- // targeted by VC++ with /iso_volatile-.
- //
- #define VOLATILE_MEMORY_BARRIER()
- #endif
- //
- // VolatileLoad loads a T from a pointer to T. It is guaranteed that this load will not be optimized
- // away by the compiler, and that any operation that occurs after this load, in program order, will
- // not be moved before this load. In general it is not guaranteed that the load will be atomic, though
- // this is the case for most aligned scalar data types. If you need atomic loads or stores, you need
- // to consult the compiler and CPU manuals to find which circumstances allow atomicity.
- //
- template<typename T>
- inline
- T VolatileLoad(T const * pt)
- {
- // STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY;
- T val = *(T volatile const *)pt;
- VOLATILE_MEMORY_BARRIER();
- return val;
- }
- template<typename T>
- inline
- T VolatileLoadWithoutBarrier(T const * pt)
- {
- // STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY;
- T val = *(T volatile const *)pt;
- return val;
- }
- template <typename T> class Volatile;
- template<typename T>
- inline
- T VolatileLoad(Volatile<T> const * pt)
- {
- // STATIC_CONTRACT_SUPPORTS_DAC;
- return pt->Load();
- }
- //
- // VolatileStore stores a T into the target of a pointer to T. Is is guaranteed that this store will
- // not be optimized away by the compiler, and that any operation that occurs before this store, in program
- // order, will not be moved after this store. In general, it is not guaranteed that the store will be
- // atomic, though this is the case for most aligned scalar data types. If you need atomic loads or stores,
- // you need to consult the compiler and CPU manuals to find which circumstances allow atomicity.
- //
- template<typename T>
- inline
- void VolatileStore(T* pt, T val)
- {
- // STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY;
- VOLATILE_MEMORY_BARRIER();
- *(T volatile *)pt = val;
- }
- template<typename T>
- inline
- void VolatileStoreWithoutBarrier(T* pt, T val)
- {
- // STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY;
- *(T volatile *)pt = val;
- }
- //
- // Volatile<T> implements accesses with our volatile semantics over a variable of type T.
- // Wherever you would have used a "volatile Foo" or, equivalently, "Foo volatile", use Volatile<Foo>
- // instead. If Foo is a pointer type, use VolatilePtr.
- //
- // Note that there are still some things that don't work with a Volatile<T>,
- // that would have worked with a "volatile T". For example, you can't cast a Volatile<int> to a float.
- // You must instead cast to an int, then to a float. Or you can call Load on the Volatile<int>, and
- // cast the result to a float. In general, calling Load or Store explicitly will work around
- // any problems that can't be solved by operator overloading.
- //
- // @TODO: it's not clear that we actually *want* any operator overloading here. It's in here primarily
- // to ease the task of converting all of the old uses of the volatile keyword, but in the long
- // run it's probably better if users of this class are forced to call Load() and Store() explicitly.
- // This would make it much more clear where the memory barriers are, and which operations are actually
- // being performed, but it will have to wait for another cleanup effort.
- //
- template <typename T>
- class Volatile
- {
- private:
- //
- // The data which we are treating as volatile
- //
- T m_val;
- public:
- //
- // Default constructor. Results in an unitialized value!
- //
- inline Volatile()
- {
- // STATIC_CONTRACT_SUPPORTS_DAC;
- }
- //
- // Allow initialization of Volatile<T> from a T
- //
- inline Volatile(const T& val)
- {
- // STATIC_CONTRACT_SUPPORTS_DAC;
- ((volatile T &)m_val) = val;
- }
- //
- // Copy constructor
- //
- inline Volatile(const Volatile<T>& other)
- {
- // STATIC_CONTRACT_SUPPORTS_DAC;
- ((volatile T &)m_val) = other.Load();
- }
- //
- // Loads the value of the volatile variable. See code:VolatileLoad for the semantics of this operation.
- //
- inline T Load() const
- {
- // STATIC_CONTRACT_SUPPORTS_DAC;
- return VolatileLoad(&m_val);
- }
- //
- // Loads the value of the volatile variable atomically without erecting the memory barrier.
- //
- inline T LoadWithoutBarrier() const
- {
- // STATIC_CONTRACT_SUPPORTS_DAC;
- return ((volatile T &)m_val);
- }
- //
- // Stores a new value to the volatile variable. See code:VolatileStore for the semantics of this
- // operation.
- //
- inline void Store(const T& val)
- {
- // STATIC_CONTRACT_SUPPORTS_DAC;
- VolatileStore(&m_val, val);
- }
- //
- // Stores a new value to the volatile variable atomically without erecting the memory barrier.
- //
- inline void StoreWithoutBarrier(const T& val) const
- {
- // STATIC_CONTRACT_SUPPORTS_DAC;
- ((volatile T &)m_val) = val;
- }
- //
- // Gets a pointer to the volatile variable. This is dangerous, as it permits the variable to be
- // accessed without using Load and Store, but it is necessary for passing Volatile<T> to APIs like
- // InterlockedIncrement.
- //
- inline volatile T* GetPointer() { return (volatile T*)&m_val; }
- //
- // Gets the raw value of the variable. This is dangerous, as it permits the variable to be
- // accessed without using Load and Store
- //
- inline T& RawValue() { return m_val; }
- //
- // Allow casts from Volatile<T> to T. Note that this allows implicit casts, so you can
- // pass a Volatile<T> directly to a method that expects a T.
- //
- inline operator T() const
- {
- // STATIC_CONTRACT_SUPPORTS_DAC;
- return this->Load();
- }
- //
- // Assignment from T
- //
- inline Volatile<T>& operator=(T val) {Store(val); return *this;}
- //
- // Get the address of the volatile variable. This is dangerous, as it allows the value of the
- // volatile variable to be accessed directly, without going through Load and Store, but it is
- // necessary for passing Volatile<T> to APIs like InterlockedIncrement. Note that we are returning
- // a pointer to a volatile T here, so we cannot accidentally pass this pointer to an API that
- // expects a normal pointer.
- //
- inline T volatile * operator&() {return this->GetPointer();}
- inline T volatile const * operator&() const {return this->GetPointer();}
- //
- // Comparison operators
- //
- template<typename TOther>
- inline bool operator==(const TOther& other) const {return this->Load() == other;}
- template<typename TOther>
- inline bool operator!=(const TOther& other) const {return this->Load() != other;}
- //
- // Miscellaneous operators. Add more as necessary.
- //
- inline Volatile<T>& operator+=(T val) {Store(this->Load() + val); return *this;}
- inline Volatile<T>& operator-=(T val) {Store(this->Load() - val); return *this;}
- inline Volatile<T>& operator|=(T val) {Store(this->Load() | val); return *this;}
- inline Volatile<T>& operator&=(T val) {Store(this->Load() & val); return *this;}
- inline bool operator!() const
- {
- // STATIC_CONTRACT_SUPPORTS_DAC;
- return !this->Load();
- }
- //
- // Prefix increment
- //
- inline Volatile& operator++() {this->Store(this->Load()+1); return *this;}
- //
- // Postfix increment
- //
- inline T operator++(int) {T val = this->Load(); this->Store(val+1); return val;}
- //
- // Prefix decrement
- //
- inline Volatile& operator--() {this->Store(this->Load()-1); return *this;}
- //
- // Postfix decrement
- //
- inline T operator--(int) {T val = this->Load(); this->Store(val-1); return val;}
- };
- //
- // A VolatilePtr builds on Volatile<T> by adding operators appropriate to pointers.
- // Wherever you would have used "Foo * volatile", use "VolatilePtr<Foo>" instead.
- //
- // VolatilePtr also allows the substution of other types for the underlying pointer. This
- // allows you to wrap a VolatilePtr around a custom type that looks like a pointer. For example,
- // if what you want is a "volatile DPTR<Foo>", use "VolatilePtr<Foo, DPTR<Foo>>".
- //
- template <typename T, typename P = T*>
- class VolatilePtr : public Volatile<P>
- {
- public:
- //
- // Default constructor. Results in an uninitialized pointer!
- //
- inline VolatilePtr()
- {
- // STATIC_CONTRACT_SUPPORTS_DAC;
- }
- //
- // Allow assignment from the pointer type.
- //
- inline VolatilePtr(P val) : Volatile<P>(val)
- {
- // STATIC_CONTRACT_SUPPORTS_DAC;
- }
- //
- // Copy constructor
- //
- inline VolatilePtr(const VolatilePtr& other) : Volatile<P>(other)
- {
- // STATIC_CONTRACT_SUPPORTS_DAC;
- }
- //
- // Cast to the pointer type
- //
- inline operator P() const
- {
- // STATIC_CONTRACT_SUPPORTS_DAC;
- return (P)this->Load();
- }
- //
- // Member access
- //
- inline P operator->() const
- {
- // STATIC_CONTRACT_SUPPORTS_DAC;
- return (P)this->Load();
- }
- //
- // Dereference the pointer
- //
- inline T& operator*() const
- {
- // STATIC_CONTRACT_SUPPORTS_DAC;
- return *(P)this->Load();
- }
- //
- // Access the pointer as an array
- //
- template <typename TIndex>
- inline T& operator[](TIndex index)
- {
- // STATIC_CONTRACT_SUPPORTS_DAC;
- return ((P)this->Load())[index];
- }
- };
- //
- // Warning: workaround
- //
- // At the bottom of this file, we are going to #define the "volatile" keyword such that it is illegal
- // to use it. Unfortunately, VC++ uses the volatile keyword in stddef.h, in the definition of "offsetof".
- // GCC does not use volatile in its definition.
- //
- // To get around this, we include stddef.h here (even if we're on GCC, for consistency). We then need
- // to redefine offsetof such that it does not use volatile, if we're building with VC++.
- //
- #include <stddef.h>
- #ifdef _MSC_VER
- #undef offsetof
- #ifdef _WIN64
- #define offsetof(s,m) (size_t)( (ptrdiff_t)&reinterpret_cast<const char&>((((s *)0)->m)) )
- #else
- #define offsetof(s,m) (size_t)&reinterpret_cast<const char&>((((s *)0)->m))
- #endif //_WIN64
- // These also use volatile, so we'll include them here.
- //#include <intrin.h>
- //#include <memory>
- #endif //_MSC_VER
- //
- // From here on out, we ban the use of the "volatile" keyword. If you found this while trying to define
- // a volatile variable, go to the top of this file and start reading.
- //
- #ifdef volatile
- #undef volatile
- #endif
- // ***** Temporarily removing this to unblock integration with new VC++ bits
- //#define volatile (DoNotUseVolatileKeyword) volatile
- // The substitution for volatile above is defined in such a way that we can still explicitly access the
- // volatile keyword without error using the macros below. Use with care.
- //#define REMOVE_DONOTUSE_ERROR(x)
- //#define RAW_KEYWORD(x) REMOVE_DONOTUSE_ERROR x
- #define RAW_KEYWORD(x) x
- // Disable use of Volatile<T> for GC/HandleTable code except on platforms where it's absolutely necessary.
- #if defined(_MSC_VER) && !defined(_ARM_)
- #define VOLATILE(T) T RAW_KEYWORD(volatile)
- #else
- #define VOLATILE(T) Volatile<T>
- #endif
- #endif //_VOLATILE_H_
|