volatile.h 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  1. //
  2. // Copyright (c) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE file in the project root for full license information.
  4. //
  5. //
  6. // Volatile.h
  7. //
  8. //
  9. // Defines the Volatile<T> type, which provides uniform volatile-ness on
  10. // Visual C++ and GNU C++.
  11. //
  12. // Visual C++ treats accesses to volatile variables as follows: no read or write
  13. // can be removed by the compiler, no global memory access can be moved backwards past
  14. // a volatile read, and no global memory access can be moved forward past a volatile
  15. // write.
  16. //
  17. // The GCC volatile semantic is straight out of the C standard: the compiler is not
  18. // allowed to remove accesses to volatile variables, and it is not allowed to reorder
  19. // volatile accesses relative to other volatile accesses. It is allowed to freely
  20. // reorder non-volatile accesses relative to volatile accesses.
  21. //
  22. // We have lots of code that assumes that ordering of non-volatile accesses will be
  23. // constrained relative to volatile accesses. For example, this pattern appears all
  24. // over the place:
  25. //
  26. // static volatile int lock = 0;
  27. //
  28. // while (InterlockedCompareExchange(&lock, 0, 1))
  29. // {
  30. // //spin
  31. // }
  32. //
  33. // //read and write variables protected by the lock
  34. //
  35. // lock = 0;
  36. //
  37. // This depends on the reads and writes in the critical section not moving past the
  38. // final statement, which releases the lock. If this should happen, then you have an
  39. // unintended race.
  40. //
  41. // The solution is to ban the use of the "volatile" keyword, and instead define our
  42. // own type Volatile<T>, which acts like a variable of type T except that accesses to
  43. // the variable are always given VC++'s volatile semantics.
  44. //
  45. // (NOTE: The code above is not intended to be an example of how a spinlock should be
  46. // implemented; it has many flaws, and should not be used. This code is intended only
  47. // to illustrate where we might get into trouble with GCC's volatile semantics.)
  48. //
  49. // @TODO: many of the variables marked volatile in the CLR do not actually need to be
  50. // volatile. For example, if a variable is just always passed to Interlocked functions
  51. // (such as a refcount variable), there is no need for it to be volatile. A future
  52. // cleanup task should be to examine each volatile variable and make them non-volatile
  53. // if possible.
  54. //
  55. // @TODO: link to a "Memory Models for CLR Devs" doc here (this doc does not yet exist).
  56. //
  57. #ifndef _VOLATILE_H_
  58. #define _VOLATILE_H_
  59. // xplat-todo: remove?
  60. // #ifndef CLR_STANDALONE_BINDER
  61. // #include "staticcontract.h"
  62. // #endif
  63. //
  64. // This code is extremely compiler- and CPU-specific, and will need to be altered to
  65. // support new compilers and/or CPUs. Here we enforce that we can only compile using
  66. // VC++, or GCC on x86 or AMD64.
  67. //
  68. #if !defined(_MSC_VER) && !defined(__GNUC__)
  69. #error The Volatile type is currently only defined for Visual C++ and GNU C++
  70. #endif
  71. #if defined(__GNUC__) && !defined(_X86_) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64_)
  72. #error The Volatile type is currently only defined for GCC when targeting x86, AMD64, ARM or ARM64 CPUs
  73. #endif
  74. #if defined(__GNUC__)
  75. #if defined(_ARM_) || defined(_ARM64_)
  76. // This is functionally equivalent to the MemoryBarrier() macro used on ARM on Windows.
  77. #define VOLATILE_MEMORY_BARRIER() asm volatile ("dmb sy" : : : "memory")
  78. #else
  79. //
  80. // For GCC, we prevent reordering by the compiler by inserting the following after a volatile
  81. // load (to prevent subsequent operations from moving before the read), and before a volatile
  82. // write (to prevent prior operations from moving past the write). We don't need to do anything
  83. // special to prevent CPU reorderings, because the x86 and AMD64 architectures are already
  84. // sufficiently constrained for our purposes. If we ever need to run on weaker CPU architectures
  85. // (such as PowerPC), then we will need to do more work.
  86. //
  87. // Please do not use this macro outside of this file. It is subject to change or removal without
  88. // notice.
  89. //
  90. #define VOLATILE_MEMORY_BARRIER() asm volatile ("" : : : "memory")
  91. #endif // !_ARM_
  92. #elif defined(_ARM_) && _ISO_VOLATILE
  93. // ARM has a very weak memory model and very few tools to control that model. We're forced to perform a full
  94. // memory barrier to preserve the volatile semantics. Technically this is only necessary on MP systems but we
  95. // currently don't have a cheap way to determine the number of CPUs from this header file. Revisit this if it
  96. // turns out to be a performance issue for the uni-proc case.
  97. #define VOLATILE_MEMORY_BARRIER() MemoryBarrier()
  98. #else
  99. //
  100. // On VC++, reorderings at the compiler and machine level are prevented by the use of the
  101. // "volatile" keyword in VolatileLoad and VolatileStore. This should work on any CPU architecture
  102. // targeted by VC++ with /iso_volatile-.
  103. //
  104. #define VOLATILE_MEMORY_BARRIER()
  105. #endif
  106. //
  107. // VolatileLoad loads a T from a pointer to T. It is guaranteed that this load will not be optimized
  108. // away by the compiler, and that any operation that occurs after this load, in program order, will
  109. // not be moved before this load. In general it is not guaranteed that the load will be atomic, though
  110. // this is the case for most aligned scalar data types. If you need atomic loads or stores, you need
  111. // to consult the compiler and CPU manuals to find which circumstances allow atomicity.
  112. //
  113. template<typename T>
  114. inline
  115. T VolatileLoad(T const * pt)
  116. {
  117. // STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY;
  118. T val = *(T volatile const *)pt;
  119. VOLATILE_MEMORY_BARRIER();
  120. return val;
  121. }
  122. template<typename T>
  123. inline
  124. T VolatileLoadWithoutBarrier(T const * pt)
  125. {
  126. // STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY;
  127. T val = *(T volatile const *)pt;
  128. return val;
  129. }
  130. template <typename T> class Volatile;
  131. template<typename T>
  132. inline
  133. T VolatileLoad(Volatile<T> const * pt)
  134. {
  135. // STATIC_CONTRACT_SUPPORTS_DAC;
  136. return pt->Load();
  137. }
  138. //
  139. // VolatileStore stores a T into the target of a pointer to T. Is is guaranteed that this store will
  140. // not be optimized away by the compiler, and that any operation that occurs before this store, in program
  141. // order, will not be moved after this store. In general, it is not guaranteed that the store will be
  142. // atomic, though this is the case for most aligned scalar data types. If you need atomic loads or stores,
  143. // you need to consult the compiler and CPU manuals to find which circumstances allow atomicity.
  144. //
  145. template<typename T>
  146. inline
  147. void VolatileStore(T* pt, T val)
  148. {
  149. // STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY;
  150. VOLATILE_MEMORY_BARRIER();
  151. *(T volatile *)pt = val;
  152. }
  153. template<typename T>
  154. inline
  155. void VolatileStoreWithoutBarrier(T* pt, T val)
  156. {
  157. // STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY;
  158. *(T volatile *)pt = val;
  159. }
  160. //
  161. // Volatile<T> implements accesses with our volatile semantics over a variable of type T.
  162. // Wherever you would have used a "volatile Foo" or, equivalently, "Foo volatile", use Volatile<Foo>
  163. // instead. If Foo is a pointer type, use VolatilePtr.
  164. //
  165. // Note that there are still some things that don't work with a Volatile<T>,
  166. // that would have worked with a "volatile T". For example, you can't cast a Volatile<int> to a float.
  167. // You must instead cast to an int, then to a float. Or you can call Load on the Volatile<int>, and
  168. // cast the result to a float. In general, calling Load or Store explicitly will work around
  169. // any problems that can't be solved by operator overloading.
  170. //
  171. // @TODO: it's not clear that we actually *want* any operator overloading here. It's in here primarily
  172. // to ease the task of converting all of the old uses of the volatile keyword, but in the long
  173. // run it's probably better if users of this class are forced to call Load() and Store() explicitly.
  174. // This would make it much more clear where the memory barriers are, and which operations are actually
  175. // being performed, but it will have to wait for another cleanup effort.
  176. //
  177. template <typename T>
  178. class Volatile
  179. {
  180. private:
  181. //
  182. // The data which we are treating as volatile
  183. //
  184. T m_val;
  185. public:
  186. //
  187. // Default constructor. Results in an unitialized value!
  188. //
  189. inline Volatile()
  190. {
  191. // STATIC_CONTRACT_SUPPORTS_DAC;
  192. }
  193. //
  194. // Allow initialization of Volatile<T> from a T
  195. //
  196. inline Volatile(const T& val)
  197. {
  198. // STATIC_CONTRACT_SUPPORTS_DAC;
  199. ((volatile T &)m_val) = val;
  200. }
  201. //
  202. // Copy constructor
  203. //
  204. inline Volatile(const Volatile<T>& other)
  205. {
  206. // STATIC_CONTRACT_SUPPORTS_DAC;
  207. ((volatile T &)m_val) = other.Load();
  208. }
  209. //
  210. // Loads the value of the volatile variable. See code:VolatileLoad for the semantics of this operation.
  211. //
  212. inline T Load() const
  213. {
  214. // STATIC_CONTRACT_SUPPORTS_DAC;
  215. return VolatileLoad(&m_val);
  216. }
  217. //
  218. // Loads the value of the volatile variable atomically without erecting the memory barrier.
  219. //
  220. inline T LoadWithoutBarrier() const
  221. {
  222. // STATIC_CONTRACT_SUPPORTS_DAC;
  223. return ((volatile T &)m_val);
  224. }
  225. //
  226. // Stores a new value to the volatile variable. See code:VolatileStore for the semantics of this
  227. // operation.
  228. //
  229. inline void Store(const T& val)
  230. {
  231. // STATIC_CONTRACT_SUPPORTS_DAC;
  232. VolatileStore(&m_val, val);
  233. }
  234. //
  235. // Stores a new value to the volatile variable atomically without erecting the memory barrier.
  236. //
  237. inline void StoreWithoutBarrier(const T& val) const
  238. {
  239. // STATIC_CONTRACT_SUPPORTS_DAC;
  240. ((volatile T &)m_val) = val;
  241. }
  242. //
  243. // Gets a pointer to the volatile variable. This is dangerous, as it permits the variable to be
  244. // accessed without using Load and Store, but it is necessary for passing Volatile<T> to APIs like
  245. // InterlockedIncrement.
  246. //
  247. inline volatile T* GetPointer() { return (volatile T*)&m_val; }
  248. //
  249. // Gets the raw value of the variable. This is dangerous, as it permits the variable to be
  250. // accessed without using Load and Store
  251. //
  252. inline T& RawValue() { return m_val; }
  253. //
  254. // Allow casts from Volatile<T> to T. Note that this allows implicit casts, so you can
  255. // pass a Volatile<T> directly to a method that expects a T.
  256. //
  257. inline operator T() const
  258. {
  259. // STATIC_CONTRACT_SUPPORTS_DAC;
  260. return this->Load();
  261. }
  262. //
  263. // Assignment from T
  264. //
  265. inline Volatile<T>& operator=(T val) {Store(val); return *this;}
  266. //
  267. // Get the address of the volatile variable. This is dangerous, as it allows the value of the
  268. // volatile variable to be accessed directly, without going through Load and Store, but it is
  269. // necessary for passing Volatile<T> to APIs like InterlockedIncrement. Note that we are returning
  270. // a pointer to a volatile T here, so we cannot accidentally pass this pointer to an API that
  271. // expects a normal pointer.
  272. //
  273. inline T volatile * operator&() {return this->GetPointer();}
  274. inline T volatile const * operator&() const {return this->GetPointer();}
  275. //
  276. // Comparison operators
  277. //
  278. template<typename TOther>
  279. inline bool operator==(const TOther& other) const {return this->Load() == other;}
  280. template<typename TOther>
  281. inline bool operator!=(const TOther& other) const {return this->Load() != other;}
  282. //
  283. // Miscellaneous operators. Add more as necessary.
  284. //
  285. inline Volatile<T>& operator+=(T val) {Store(this->Load() + val); return *this;}
  286. inline Volatile<T>& operator-=(T val) {Store(this->Load() - val); return *this;}
  287. inline Volatile<T>& operator|=(T val) {Store(this->Load() | val); return *this;}
  288. inline Volatile<T>& operator&=(T val) {Store(this->Load() & val); return *this;}
  289. inline bool operator!() const
  290. {
  291. // STATIC_CONTRACT_SUPPORTS_DAC;
  292. return !this->Load();
  293. }
  294. //
  295. // Prefix increment
  296. //
  297. inline Volatile& operator++() {this->Store(this->Load()+1); return *this;}
  298. //
  299. // Postfix increment
  300. //
  301. inline T operator++(int) {T val = this->Load(); this->Store(val+1); return val;}
  302. //
  303. // Prefix decrement
  304. //
  305. inline Volatile& operator--() {this->Store(this->Load()-1); return *this;}
  306. //
  307. // Postfix decrement
  308. //
  309. inline T operator--(int) {T val = this->Load(); this->Store(val-1); return val;}
  310. };
  311. //
  312. // A VolatilePtr builds on Volatile<T> by adding operators appropriate to pointers.
  313. // Wherever you would have used "Foo * volatile", use "VolatilePtr<Foo>" instead.
  314. //
  315. // VolatilePtr also allows the substution of other types for the underlying pointer. This
  316. // allows you to wrap a VolatilePtr around a custom type that looks like a pointer. For example,
  317. // if what you want is a "volatile DPTR<Foo>", use "VolatilePtr<Foo, DPTR<Foo>>".
  318. //
  319. template <typename T, typename P = T*>
  320. class VolatilePtr : public Volatile<P>
  321. {
  322. public:
  323. //
  324. // Default constructor. Results in an uninitialized pointer!
  325. //
  326. inline VolatilePtr()
  327. {
  328. // STATIC_CONTRACT_SUPPORTS_DAC;
  329. }
  330. //
  331. // Allow assignment from the pointer type.
  332. //
  333. inline VolatilePtr(P val) : Volatile<P>(val)
  334. {
  335. // STATIC_CONTRACT_SUPPORTS_DAC;
  336. }
  337. //
  338. // Copy constructor
  339. //
  340. inline VolatilePtr(const VolatilePtr& other) : Volatile<P>(other)
  341. {
  342. // STATIC_CONTRACT_SUPPORTS_DAC;
  343. }
  344. //
  345. // Cast to the pointer type
  346. //
  347. inline operator P() const
  348. {
  349. // STATIC_CONTRACT_SUPPORTS_DAC;
  350. return (P)this->Load();
  351. }
  352. //
  353. // Member access
  354. //
  355. inline P operator->() const
  356. {
  357. // STATIC_CONTRACT_SUPPORTS_DAC;
  358. return (P)this->Load();
  359. }
  360. //
  361. // Dereference the pointer
  362. //
  363. inline T& operator*() const
  364. {
  365. // STATIC_CONTRACT_SUPPORTS_DAC;
  366. return *(P)this->Load();
  367. }
  368. //
  369. // Access the pointer as an array
  370. //
  371. template <typename TIndex>
  372. inline T& operator[](TIndex index)
  373. {
  374. // STATIC_CONTRACT_SUPPORTS_DAC;
  375. return ((P)this->Load())[index];
  376. }
  377. };
  378. //
  379. // Warning: workaround
  380. //
  381. // At the bottom of this file, we are going to #define the "volatile" keyword such that it is illegal
  382. // to use it. Unfortunately, VC++ uses the volatile keyword in stddef.h, in the definition of "offsetof".
  383. // GCC does not use volatile in its definition.
  384. //
  385. // To get around this, we include stddef.h here (even if we're on GCC, for consistency). We then need
  386. // to redefine offsetof such that it does not use volatile, if we're building with VC++.
  387. //
  388. #include <stddef.h>
  389. #ifdef _MSC_VER
  390. #undef offsetof
  391. #ifdef _WIN64
  392. #define offsetof(s,m) (size_t)( (ptrdiff_t)&reinterpret_cast<const char&>((((s *)0)->m)) )
  393. #else
  394. #define offsetof(s,m) (size_t)&reinterpret_cast<const char&>((((s *)0)->m))
  395. #endif //_WIN64
  396. // These also use volatile, so we'll include them here.
  397. //#include <intrin.h>
  398. //#include <memory>
  399. #endif //_MSC_VER
  400. //
  401. // From here on out, we ban the use of the "volatile" keyword. If you found this while trying to define
  402. // a volatile variable, go to the top of this file and start reading.
  403. //
  404. #ifdef volatile
  405. #undef volatile
  406. #endif
  407. // ***** Temporarily removing this to unblock integration with new VC++ bits
  408. //#define volatile (DoNotUseVolatileKeyword) volatile
  409. // The substitution for volatile above is defined in such a way that we can still explicitly access the
  410. // volatile keyword without error using the macros below. Use with care.
  411. //#define REMOVE_DONOTUSE_ERROR(x)
  412. //#define RAW_KEYWORD(x) REMOVE_DONOTUSE_ERROR x
  413. #define RAW_KEYWORD(x) x
  414. // Disable use of Volatile<T> for GC/HandleTable code except on platforms where it's absolutely necessary.
  415. #if defined(_MSC_VER) && !defined(_ARM_)
  416. #define VOLATILE(T) T RAW_KEYWORD(volatile)
  417. #else
  418. #define VOLATILE(T) Volatile<T>
  419. #endif
  420. #endif //_VOLATILE_H_