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 | |
109 | template<typename T> |
110 | struct RemoveVolatile |
111 | { |
112 | typedef T type; |
113 | }; |
114 | |
115 | template<typename T> |
116 | struct 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 | |
133 | template<typename T> |
134 | inline |
135 | T 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 | |
162 | template<typename T> |
163 | inline |
164 | T 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 | |
176 | template <typename T> class Volatile; |
177 | |
178 | template<typename T> |
179 | inline |
180 | T 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 | // |
193 | template<typename T> |
194 | inline |
195 | void 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 | |
220 | template<typename T> |
221 | inline |
222 | void 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 | // |
250 | template <typename T> |
251 | class Volatile |
252 | { |
253 | private: |
254 | // |
255 | // The data which we are treating as volatile |
256 | // |
257 | T m_val; |
258 | |
259 | public: |
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 | // |
411 | template <typename T, typename P = T*> |
412 | class VolatilePtr : public Volatile<P> |
413 | { |
414 | public: |
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 |
509 | namespace 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 | |