1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4//
5// Volatile.h
6//
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
58#ifndef _VOLATILE_H_
59#define _VOLATILE_H_
60
61#include "staticcontract.h"
62
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
72#if defined(__GNUC__) && !defined(_X86_) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64_)
73#error The Volatile type is currently only defined for GCC when targeting x86, AMD64, ARM or ARM64 CPUs
74#endif
75
76#if defined(__GNUC__)
77#if defined(_ARM_) || defined(_ARM64_)
78// This is functionally equivalent to the MemoryBarrier() macro used on ARM on Windows.
79#define VOLATILE_MEMORY_BARRIER() asm volatile ("dmb ish" : : : "memory")
80#else
81//
82// For GCC, we prevent reordering by the compiler by inserting the following after a volatile
83// load (to prevent subsequent operations from moving before the read), and before a volatile
84// write (to prevent prior operations from moving past the write). We don't need to do anything
85// special to prevent CPU reorderings, because the x86 and AMD64 architectures are already
86// sufficiently constrained for our purposes. If we ever need to run on weaker CPU architectures
87// (such as PowerPC), then we will need to do more work.
88//
89// Please do not use this macro outside of this file. It is subject to change or removal without
90// notice.
91//
92#define VOLATILE_MEMORY_BARRIER() asm volatile ("" : : : "memory")
93#endif // _ARM_ || _ARM64_
94#elif (defined(_ARM_) || defined(_ARM64_)) && _ISO_VOLATILE
95// ARM & ARM64 have a very weak memory model and very few tools to control that model. We're forced to perform a full
96// memory barrier to preserve the volatile semantics. Technically this is only necessary on MP systems but we
97// currently don't have a cheap way to determine the number of CPUs from this header file. Revisit this if it
98// turns out to be a performance issue for the uni-proc case.
99#define VOLATILE_MEMORY_BARRIER() MemoryBarrier()
100#else
101//
102// On VC++, reorderings at the compiler and machine level are prevented by the use of the
103// "volatile" keyword in VolatileLoad and VolatileStore. This should work on any CPU architecture
104// targeted by VC++ with /iso_volatile-.
105//
106#define VOLATILE_MEMORY_BARRIER()
107#endif // __GNUC__
108
109template<typename T>
110struct RemoveVolatile
111{
112 typedef T type;
113};
114
115template<typename T>
116struct RemoveVolatile<volatile T>
117{
118 typedef T type;
119};
120
121
122//
123// VolatileLoad loads a T from a pointer to T. It is guaranteed that this load will not be optimized
124// away by the compiler, and that any operation that occurs after this load, in program order, will
125// not be moved before this load. In general it is not guaranteed that the load will be atomic, though
126// this is the case for most aligned scalar data types. If you need atomic loads or stores, you need
127// to consult the compiler and CPU manuals to find which circumstances allow atomicity.
128//
129// Starting at version 3.8, clang errors out on initializing of type int * to volatile int *. To fix this, we add two templates to cast away volatility
130// Helper structures for casting away volatileness
131
132
133template<typename T>
134inline
135T VolatileLoad(T const * pt)
136{
137 STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY;
138
139#ifndef DACCESS_COMPILE
140#if defined(_ARM64_) && defined(__GNUC__)
141 T val;
142 static const unsigned lockFreeAtomicSizeMask = (1 << 1) | (1 << 2) | (1 << 4) | (1 << 8);
143 if((1 << sizeof(T)) & lockFreeAtomicSizeMask)
144 {
145 __atomic_load((T const *)pt, const_cast<typename RemoveVolatile<T>::type *>(&val), __ATOMIC_ACQUIRE);
146 }
147 else
148 {
149 val = *(T volatile const *)pt;
150 asm volatile ("dmb ishld" : : : "memory");
151 }
152#else
153 T val = *(T volatile const *)pt;
154 VOLATILE_MEMORY_BARRIER();
155#endif
156#else
157 T val = *pt;
158#endif
159 return val;
160}
161
162template<typename T>
163inline
164T VolatileLoadWithoutBarrier(T const * pt)
165{
166 STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY;
167
168#ifndef DACCESS_COMPILE
169 T val = *(T volatile const *)pt;
170#else
171 T val = *pt;
172#endif
173 return val;
174}
175
176template <typename T> class Volatile;
177
178template<typename T>
179inline
180T VolatileLoad(Volatile<T> const * pt)
181{
182 STATIC_CONTRACT_SUPPORTS_DAC;
183 return pt->Load();
184}
185
186//
187// VolatileStore stores a T into the target of a pointer to T. Is is guaranteed that this store will
188// not be optimized away by the compiler, and that any operation that occurs before this store, in program
189// order, will not be moved after this store. In general, it is not guaranteed that the store will be
190// atomic, though this is the case for most aligned scalar data types. If you need atomic loads or stores,
191// you need to consult the compiler and CPU manuals to find which circumstances allow atomicity.
192//
193template<typename T>
194inline
195void VolatileStore(T* pt, T val)
196{
197 STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY;
198
199#ifndef DACCESS_COMPILE
200#if defined(_ARM64_) && defined(__GNUC__)
201 static const unsigned lockFreeAtomicSizeMask = (1 << 1) | (1 << 2) | (1 << 4) | (1 << 8);
202 if((1 << sizeof(T)) & lockFreeAtomicSizeMask)
203 {
204 __atomic_store((T volatile *)pt, &val, __ATOMIC_RELEASE);
205 }
206 else
207 {
208 VOLATILE_MEMORY_BARRIER();
209 *(T volatile *)pt = val;
210 }
211#else
212 VOLATILE_MEMORY_BARRIER();
213 *(T volatile *)pt = val;
214#endif
215#else
216 *pt = val;
217#endif
218}
219
220template<typename T>
221inline
222void VolatileStoreWithoutBarrier(T* pt, T val)
223{
224 STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY;
225
226#ifndef DACCESS_COMPILE
227 *(T volatile *)pt = val;
228#else
229 *pt = val;
230#endif
231}
232
233//
234// Volatile<T> implements accesses with our volatile semantics over a variable of type T.
235// Wherever you would have used a "volatile Foo" or, equivalently, "Foo volatile", use Volatile<Foo>
236// instead. If Foo is a pointer type, use VolatilePtr.
237//
238// Note that there are still some things that don't work with a Volatile<T>,
239// that would have worked with a "volatile T". For example, you can't cast a Volatile<int> to a float.
240// You must instead cast to an int, then to a float. Or you can call Load on the Volatile<int>, and
241// cast the result to a float. In general, calling Load or Store explicitly will work around
242// any problems that can't be solved by operator overloading.
243//
244// @TODO: it's not clear that we actually *want* any operator overloading here. It's in here primarily
245// to ease the task of converting all of the old uses of the volatile keyword, but in the long
246// run it's probably better if users of this class are forced to call Load() and Store() explicitly.
247// This would make it much more clear where the memory barriers are, and which operations are actually
248// being performed, but it will have to wait for another cleanup effort.
249//
250template <typename T>
251class Volatile
252{
253private:
254 //
255 // The data which we are treating as volatile
256 //
257 T m_val;
258
259public:
260 //
261 // Default constructor. Results in an unitialized value!
262 //
263 inline Volatile()
264 {
265 STATIC_CONTRACT_SUPPORTS_DAC;
266 }
267
268 //
269 // Allow initialization of Volatile<T> from a T
270 //
271 inline Volatile(const T& val)
272 {
273 STATIC_CONTRACT_SUPPORTS_DAC;
274 ((volatile T &)m_val) = val;
275 }
276
277 //
278 // Copy constructor
279 //
280 inline Volatile(const Volatile<T>& other)
281 {
282 STATIC_CONTRACT_SUPPORTS_DAC;
283 ((volatile T &)m_val) = other.Load();
284 }
285
286 //
287 // Loads the value of the volatile variable. See code:VolatileLoad for the semantics of this operation.
288 //
289 inline T Load() const
290 {
291 STATIC_CONTRACT_SUPPORTS_DAC;
292 return VolatileLoad(&m_val);
293 }
294
295 //
296 // Loads the value of the volatile variable atomically without erecting the memory barrier.
297 //
298 inline T LoadWithoutBarrier() const
299 {
300 STATIC_CONTRACT_SUPPORTS_DAC;
301 return ((volatile T &)m_val);
302 }
303
304 //
305 // Stores a new value to the volatile variable. See code:VolatileStore for the semantics of this
306 // operation.
307 //
308 inline void Store(const T& val)
309 {
310 STATIC_CONTRACT_SUPPORTS_DAC;
311 VolatileStore(&m_val, val);
312 }
313
314
315 //
316 // Stores a new value to the volatile variable atomically without erecting the memory barrier.
317 //
318 inline void StoreWithoutBarrier(const T& val) const
319 {
320 STATIC_CONTRACT_SUPPORTS_DAC;
321 ((volatile T &)m_val) = val;
322 }
323
324
325 //
326 // Gets a pointer to the volatile variable. This is dangerous, as it permits the variable to be
327 // accessed without using Load and Store, but it is necessary for passing Volatile<T> to APIs like
328 // InterlockedIncrement.
329 //
330 inline volatile T* GetPointer() { return (volatile T*)&m_val; }
331
332
333 //
334 // Gets the raw value of the variable. This is dangerous, as it permits the variable to be
335 // accessed without using Load and Store
336 //
337 inline T& RawValue() { return m_val; }
338
339 //
340 // Allow casts from Volatile<T> to T. Note that this allows implicit casts, so you can
341 // pass a Volatile<T> directly to a method that expects a T.
342 //
343 inline operator T() const
344 {
345 STATIC_CONTRACT_SUPPORTS_DAC;
346 return this->Load();
347 }
348
349 //
350 // Assignment from T
351 //
352 inline Volatile<T>& operator=(T val) {Store(val); return *this;}
353
354 //
355 // Get the address of the volatile variable. This is dangerous, as it allows the value of the
356 // volatile variable to be accessed directly, without going through Load and Store, but it is
357 // necessary for passing Volatile<T> to APIs like InterlockedIncrement. Note that we are returning
358 // a pointer to a volatile T here, so we cannot accidentally pass this pointer to an API that
359 // expects a normal pointer.
360 //
361 inline T volatile * operator&() {return this->GetPointer();}
362 inline T volatile const * operator&() const {return this->GetPointer();}
363
364 //
365 // Comparison operators
366 //
367 template<typename TOther>
368 inline bool operator==(const TOther& other) const {return this->Load() == other;}
369
370 template<typename TOther>
371 inline bool operator!=(const TOther& other) const {return this->Load() != other;}
372
373 //
374 // Miscellaneous operators. Add more as necessary.
375 //
376 inline Volatile<T>& operator+=(T val) {Store(this->Load() + val); return *this;}
377 inline Volatile<T>& operator-=(T val) {Store(this->Load() - val); return *this;}
378 inline Volatile<T>& operator|=(T val) {Store(this->Load() | val); return *this;}
379 inline Volatile<T>& operator&=(T val) {Store(this->Load() & val); return *this;}
380 inline bool operator!() const { STATIC_CONTRACT_SUPPORTS_DAC; return !this->Load();}
381
382 //
383 // Prefix increment
384 //
385 inline Volatile& operator++() {this->Store(this->Load()+1); return *this;}
386
387 //
388 // Postfix increment
389 //
390 inline T operator++(int) {T val = this->Load(); this->Store(val+1); return val;}
391
392 //
393 // Prefix decrement
394 //
395 inline Volatile& operator--() {this->Store(this->Load()-1); return *this;}
396
397 //
398 // Postfix decrement
399 //
400 inline T operator--(int) {T val = this->Load(); this->Store(val-1); return val;}
401};
402
403//
404// A VolatilePtr builds on Volatile<T> by adding operators appropriate to pointers.
405// Wherever you would have used "Foo * volatile", use "VolatilePtr<Foo>" instead.
406//
407// VolatilePtr also allows the substution of other types for the underlying pointer. This
408// allows you to wrap a VolatilePtr around a custom type that looks like a pointer. For example,
409// if what you want is a "volatile DPTR<Foo>", use "VolatilePtr<Foo, DPTR<Foo>>".
410//
411template <typename T, typename P = T*>
412class VolatilePtr : public Volatile<P>
413{
414public:
415 //
416 // Default constructor. Results in an uninitialized pointer!
417 //
418 inline VolatilePtr()
419 {
420 STATIC_CONTRACT_SUPPORTS_DAC;
421 }
422
423 //
424 // Allow assignment from the pointer type.
425 //
426 inline VolatilePtr(P val) : Volatile<P>(val)
427 {
428 STATIC_CONTRACT_SUPPORTS_DAC;
429 }
430
431 //
432 // Copy constructor
433 //
434 inline VolatilePtr(const VolatilePtr& other) : Volatile<P>(other)
435 {
436 STATIC_CONTRACT_SUPPORTS_DAC;
437 }
438
439 //
440 // Cast to the pointer type
441 //
442 inline operator P() const
443 {
444 STATIC_CONTRACT_SUPPORTS_DAC;
445 return (P)this->Load();
446 }
447
448 //
449 // Member access
450 //
451 inline P operator->() const
452 {
453 STATIC_CONTRACT_SUPPORTS_DAC;
454 return (P)this->Load();
455 }
456
457 //
458 // Dereference the pointer
459 //
460 inline T& operator*() const
461 {
462 STATIC_CONTRACT_SUPPORTS_DAC;
463 return *(P)this->Load();
464 }
465
466 //
467 // Access the pointer as an array
468 //
469 template <typename TIndex>
470 inline T& operator[](TIndex index)
471 {
472 STATIC_CONTRACT_SUPPORTS_DAC;
473 return ((P)this->Load())[index];
474 }
475};
476
477//
478// From here on out, we ban the use of the "volatile" keyword. If you found this while trying to define
479// a volatile variable, go to the top of this file and start reading.
480//
481#ifdef volatile
482#undef volatile
483#endif
484// ***** Temporarily removing this to unblock integration with new VC++ bits
485//#define volatile (DoNotUseVolatileKeyword) volatile
486
487// The substitution for volatile above is defined in such a way that we can still explicitly access the
488// volatile keyword without error using the macros below. Use with care.
489//#define REMOVE_DONOTUSE_ERROR(x)
490//#define RAW_KEYWORD(x) REMOVE_DONOTUSE_ERROR x
491#define RAW_KEYWORD(x) x
492
493#ifdef DACCESS_COMPILE
494// No need to use volatile in DAC builds - DAC is single-threaded and the target
495// process is suspended.
496#define VOLATILE(T) T
497#else
498
499// Disable use of Volatile<T> for GC/HandleTable code except on platforms where it's absolutely necessary.
500#if defined(_MSC_VER) && !defined(_ARM_) && !defined(_ARM64_)
501#define VOLATILE(T) T RAW_KEYWORD(volatile)
502#else
503#define VOLATILE(T) Volatile<T>
504#endif
505
506#endif // DACCESS_COMPILE
507
508// VolatilePtr-specific clr::SafeAddRef and clr::SafeRelease
509namespace clr
510{
511 template < typename ItfT, typename PtrT > inline
512 #ifdef __checkReturn // Volatile.h is used in corunix headers, which don't define/nullify SAL.
513 __checkReturn
514 #endif
515 VolatilePtr<ItfT, PtrT>&
516 SafeAddRef(VolatilePtr<ItfT, PtrT>& pItf)
517 {
518 STATIC_CONTRACT_LIMITED_METHOD;
519 SafeAddRef(pItf.Load());
520 return pItf;
521 }
522
523 template < typename ItfT, typename PtrT > inline
524 ULONG
525 SafeRelease(VolatilePtr<ItfT, PtrT>& pItf)
526 {
527 STATIC_CONTRACT_LIMITED_METHOD;
528 return SafeRelease(pItf.Load());
529 }
530}
531
532#endif //_VOLATILE_H_
533