1/*
2 * Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 *
23 */
24
25#ifndef SHARE_RUNTIME_ATOMIC_HPP
26#define SHARE_RUNTIME_ATOMIC_HPP
27
28#include "memory/allocation.hpp"
29#include "metaprogramming/conditional.hpp"
30#include "metaprogramming/enableIf.hpp"
31#include "metaprogramming/isIntegral.hpp"
32#include "metaprogramming/isPointer.hpp"
33#include "metaprogramming/isSame.hpp"
34#include "metaprogramming/primitiveConversions.hpp"
35#include "metaprogramming/removeCV.hpp"
36#include "metaprogramming/removePointer.hpp"
37#include "utilities/align.hpp"
38#include "utilities/macros.hpp"
39
40enum atomic_memory_order {
41 // The modes that align with C++11 are intended to
42 // follow the same semantics.
43 memory_order_relaxed = 0,
44 memory_order_acquire = 2,
45 memory_order_release = 3,
46 memory_order_acq_rel = 4,
47 // Strong two-way memory barrier.
48 memory_order_conservative = 8
49};
50
51class Atomic : AllStatic {
52public:
53 // Atomic operations on int64 types are not available on all 32-bit
54 // platforms. If atomic ops on int64 are defined here they must only
55 // be used from code that verifies they are available at runtime and
56 // can provide an alternative action if not - see supports_cx8() for
57 // a means to test availability.
58
59 // The memory operations that are mentioned with each of the atomic
60 // function families come from src/share/vm/runtime/orderAccess.hpp,
61 // e.g., <fence> is described in that file and is implemented by the
62 // OrderAccess::fence() function. See that file for the gory details
63 // on the Memory Access Ordering Model.
64
65 // All of the atomic operations that imply a read-modify-write action
66 // guarantee a two-way memory barrier across that operation. Historically
67 // these semantics reflect the strength of atomic operations that are
68 // provided on SPARC/X86. We assume that strength is necessary unless
69 // we can prove that a weaker form is sufficiently safe.
70
71 // Atomically store to a location
72 // The type T must be either a pointer type convertible to or equal
73 // to D, an integral/enum type equal to D, or a type equal to D that
74 // is primitive convertible using PrimitiveConversions.
75 template<typename T, typename D>
76 inline static void store(T store_value, volatile D* dest);
77
78 // Atomically load from a location
79 // The type T must be either a pointer type, an integral/enum type,
80 // or a type that is primitive convertible using PrimitiveConversions.
81 template<typename T>
82 inline static T load(const volatile T* dest);
83
84 // Atomically add to a location. Returns updated value. add*() provide:
85 // <fence> add-value-to-dest <membar StoreLoad|StoreStore>
86
87 template<typename I, typename D>
88 inline static D add(I add_value, D volatile* dest,
89 atomic_memory_order order = memory_order_conservative);
90
91 template<typename I, typename D>
92 inline static D sub(I sub_value, D volatile* dest,
93 atomic_memory_order order = memory_order_conservative);
94
95 // Atomically increment location. inc() provide:
96 // <fence> increment-dest <membar StoreLoad|StoreStore>
97 // The type D may be either a pointer type, or an integral
98 // type. If it is a pointer type, then the increment is
99 // scaled to the size of the type pointed to by the pointer.
100 template<typename D>
101 inline static void inc(D volatile* dest,
102 atomic_memory_order order = memory_order_conservative);
103
104 // Atomically decrement a location. dec() provide:
105 // <fence> decrement-dest <membar StoreLoad|StoreStore>
106 // The type D may be either a pointer type, or an integral
107 // type. If it is a pointer type, then the decrement is
108 // scaled to the size of the type pointed to by the pointer.
109 template<typename D>
110 inline static void dec(D volatile* dest,
111 atomic_memory_order order = memory_order_conservative);
112
113 // Performs atomic exchange of *dest with exchange_value. Returns old
114 // prior value of *dest. xchg*() provide:
115 // <fence> exchange-value-with-dest <membar StoreLoad|StoreStore>
116 // The type T must be either a pointer type convertible to or equal
117 // to D, an integral/enum type equal to D, or a type equal to D that
118 // is primitive convertible using PrimitiveConversions.
119 template<typename T, typename D>
120 inline static D xchg(T exchange_value, volatile D* dest,
121 atomic_memory_order order = memory_order_conservative);
122
123 // Performs atomic compare of *dest and compare_value, and exchanges
124 // *dest with exchange_value if the comparison succeeded. Returns prior
125 // value of *dest. cmpxchg*() provide:
126 // <fence> compare-and-exchange <membar StoreLoad|StoreStore>
127
128 template<typename T, typename D, typename U>
129 inline static D cmpxchg(T exchange_value,
130 D volatile* dest,
131 U compare_value,
132 atomic_memory_order order = memory_order_conservative);
133
134 // Performs atomic compare of *dest and NULL, and replaces *dest
135 // with exchange_value if the comparison succeeded. Returns true if
136 // the comparison succeeded and the exchange occurred. This is
137 // often used as part of lazy initialization, as a lock-free
138 // alternative to the Double-Checked Locking Pattern.
139 template<typename T, typename D>
140 inline static bool replace_if_null(T* value, D* volatile* dest,
141 atomic_memory_order order = memory_order_conservative);
142
143private:
144WINDOWS_ONLY(public:) // VS2017 warns (C2027) use of undefined type if IsPointerConvertible is declared private
145 // Test whether From is implicitly convertible to To.
146 // From and To must be pointer types.
147 // Note: Provides the limited subset of C++11 std::is_convertible
148 // that is needed here.
149 template<typename From, typename To> struct IsPointerConvertible;
150
151protected:
152 // Dispatch handler for store. Provides type-based validity
153 // checking and limited conversions around calls to the platform-
154 // specific implementation layer provided by PlatformOp.
155 template<typename T, typename D, typename PlatformOp, typename Enable = void>
156 struct StoreImpl;
157
158 // Platform-specific implementation of store. Support for sizes
159 // of 1, 2, 4, and (if different) pointer size bytes are required.
160 // The class is a function object that must be default constructable,
161 // with these requirements:
162 //
163 // either:
164 // - dest is of type D*, an integral, enum or pointer type.
165 // - new_value are of type T, an integral, enum or pointer type D or
166 // pointer type convertible to D.
167 // or:
168 // - T and D are the same and are primitive convertible using PrimitiveConversions
169 // and either way:
170 // - platform_store is an object of type PlatformStore<sizeof(T)>.
171 //
172 // Then
173 // platform_store(new_value, dest)
174 // must be a valid expression.
175 //
176 // The default implementation is a volatile store. If a platform
177 // requires more for e.g. 64 bit stores, a specialization is required
178 template<size_t byte_size> struct PlatformStore;
179
180 // Dispatch handler for load. Provides type-based validity
181 // checking and limited conversions around calls to the platform-
182 // specific implementation layer provided by PlatformOp.
183 template<typename T, typename PlatformOp, typename Enable = void>
184 struct LoadImpl;
185
186 // Platform-specific implementation of load. Support for sizes of
187 // 1, 2, 4 bytes and (if different) pointer size bytes are required.
188 // The class is a function object that must be default
189 // constructable, with these requirements:
190 //
191 // - dest is of type T*, an integral, enum or pointer type, or
192 // T is convertible to a primitive type using PrimitiveConversions
193 // - platform_load is an object of type PlatformLoad<sizeof(T)>.
194 //
195 // Then
196 // platform_load(src)
197 // must be a valid expression, returning a result convertible to T.
198 //
199 // The default implementation is a volatile load. If a platform
200 // requires more for e.g. 64 bit loads, a specialization is required
201 template<size_t byte_size> struct PlatformLoad;
202
203private:
204 // Dispatch handler for add. Provides type-based validity checking
205 // and limited conversions around calls to the platform-specific
206 // implementation layer provided by PlatformAdd.
207 template<typename I, typename D, typename Enable = void>
208 struct AddImpl;
209
210 // Platform-specific implementation of add. Support for sizes of 4
211 // bytes and (if different) pointer size bytes are required. The
212 // class is a function object that must be default constructable,
213 // with these requirements:
214 //
215 // - dest is of type D*, an integral or pointer type.
216 // - add_value is of type I, an integral type.
217 // - sizeof(I) == sizeof(D).
218 // - if D is an integral type, I == D.
219 // - platform_add is an object of type PlatformAdd<sizeof(D)>.
220 //
221 // Then
222 // platform_add(add_value, dest)
223 // must be a valid expression, returning a result convertible to D.
224 //
225 // No definition is provided; all platforms must explicitly define
226 // this class and any needed specializations.
227 template<size_t byte_size> struct PlatformAdd;
228
229 // Helper base classes for defining PlatformAdd. To use, define
230 // PlatformAdd or a specialization that derives from one of these,
231 // and include in the PlatformAdd definition the support function
232 // (described below) required by the base class.
233 //
234 // These classes implement the required function object protocol for
235 // PlatformAdd, using a support function template provided by the
236 // derived class. Let add_value (of type I) and dest (of type D) be
237 // the arguments the object is called with. If D is a pointer type
238 // P*, then let addend (of type I) be add_value * sizeof(P);
239 // otherwise, addend is add_value.
240 //
241 // FetchAndAdd requires the derived class to provide
242 // fetch_and_add(addend, dest)
243 // atomically adding addend to the value of dest, and returning the
244 // old value.
245 //
246 // AddAndFetch requires the derived class to provide
247 // add_and_fetch(addend, dest)
248 // atomically adding addend to the value of dest, and returning the
249 // new value.
250 //
251 // When D is a pointer type P*, both fetch_and_add and add_and_fetch
252 // treat it as if it were a uintptr_t; they do not perform any
253 // scaling of the addend, as that has already been done by the
254 // caller.
255public: // Temporary, can't be private: C++03 11.4/2. Fixed by C++11.
256 template<typename Derived> struct FetchAndAdd;
257 template<typename Derived> struct AddAndFetch;
258private:
259
260 // Support for platforms that implement some variants of add using a
261 // (typically out of line) non-template helper function. The
262 // generic arguments passed to PlatformAdd need to be translated to
263 // the appropriate type for the helper function, the helper function
264 // invoked on the translated arguments, and the result translated
265 // back. Type is the parameter / return type of the helper
266 // function. No scaling of add_value is performed when D is a pointer
267 // type, so this function can be used to implement the support function
268 // required by AddAndFetch.
269 template<typename Type, typename Fn, typename I, typename D>
270 static D add_using_helper(Fn fn, I add_value, D volatile* dest);
271
272 // Dispatch handler for cmpxchg. Provides type-based validity
273 // checking and limited conversions around calls to the
274 // platform-specific implementation layer provided by
275 // PlatformCmpxchg.
276 template<typename T, typename D, typename U, typename Enable = void>
277 struct CmpxchgImpl;
278
279 // Platform-specific implementation of cmpxchg. Support for sizes
280 // of 1, 4, and 8 are required. The class is a function object that
281 // must be default constructable, with these requirements:
282 //
283 // - dest is of type T*.
284 // - exchange_value and compare_value are of type T.
285 // - order is of type atomic_memory_order.
286 // - platform_cmpxchg is an object of type PlatformCmpxchg<sizeof(T)>.
287 //
288 // Then
289 // platform_cmpxchg(exchange_value, dest, compare_value, order)
290 // must be a valid expression, returning a result convertible to T.
291 //
292 // A default definition is provided, which declares a function template
293 // T operator()(T, T volatile*, T, atomic_memory_order) const
294 //
295 // For each required size, a platform must either provide an
296 // appropriate definition of that function, or must entirely
297 // specialize the class template for that size.
298 template<size_t byte_size> struct PlatformCmpxchg;
299
300 // Support for platforms that implement some variants of cmpxchg
301 // using a (typically out of line) non-template helper function.
302 // The generic arguments passed to PlatformCmpxchg need to be
303 // translated to the appropriate type for the helper function, the
304 // helper invoked on the translated arguments, and the result
305 // translated back. Type is the parameter / return type of the
306 // helper function.
307 template<typename Type, typename Fn, typename T>
308 static T cmpxchg_using_helper(Fn fn,
309 T exchange_value,
310 T volatile* dest,
311 T compare_value);
312
313 // Support platforms that do not provide Read-Modify-Write
314 // byte-level atomic access. To use, derive PlatformCmpxchg<1> from
315 // this class.
316public: // Temporary, can't be private: C++03 11.4/2. Fixed by C++11.
317 struct CmpxchgByteUsingInt;
318private:
319
320 // Dispatch handler for xchg. Provides type-based validity
321 // checking and limited conversions around calls to the
322 // platform-specific implementation layer provided by
323 // PlatformXchg.
324 template<typename T, typename D, typename Enable = void>
325 struct XchgImpl;
326
327 // Platform-specific implementation of xchg. Support for sizes
328 // of 4, and sizeof(intptr_t) are required. The class is a function
329 // object that must be default constructable, with these requirements:
330 //
331 // - dest is of type T*.
332 // - exchange_value is of type T.
333 // - platform_xchg is an object of type PlatformXchg<sizeof(T)>.
334 //
335 // Then
336 // platform_xchg(exchange_value, dest)
337 // must be a valid expression, returning a result convertible to T.
338 //
339 // A default definition is provided, which declares a function template
340 // T operator()(T, T volatile*, T, atomic_memory_order) const
341 //
342 // For each required size, a platform must either provide an
343 // appropriate definition of that function, or must entirely
344 // specialize the class template for that size.
345 template<size_t byte_size> struct PlatformXchg;
346
347 // Support for platforms that implement some variants of xchg
348 // using a (typically out of line) non-template helper function.
349 // The generic arguments passed to PlatformXchg need to be
350 // translated to the appropriate type for the helper function, the
351 // helper invoked on the translated arguments, and the result
352 // translated back. Type is the parameter / return type of the
353 // helper function.
354 template<typename Type, typename Fn, typename T>
355 static T xchg_using_helper(Fn fn,
356 T exchange_value,
357 T volatile* dest);
358};
359
360template<typename From, typename To>
361struct Atomic::IsPointerConvertible<From*, To*> : AllStatic {
362 // Determine whether From* is implicitly convertible to To*, using
363 // the "sizeof trick".
364 typedef char yes;
365 typedef char (&no)[2];
366
367 static yes test(To*);
368 static no test(...);
369 static From* test_value;
370
371 static const bool value = (sizeof(yes) == sizeof(test(test_value)));
372};
373
374// Handle load for pointer, integral and enum types.
375template<typename T, typename PlatformOp>
376struct Atomic::LoadImpl<
377 T,
378 PlatformOp,
379 typename EnableIf<IsIntegral<T>::value || IsRegisteredEnum<T>::value || IsPointer<T>::value>::type>
380{
381 T operator()(T const volatile* dest) const {
382 // Forward to the platform handler for the size of T.
383 return PlatformOp()(dest);
384 }
385};
386
387// Handle load for types that have a translator.
388//
389// All the involved types must be identical.
390//
391// This translates the original call into a call on the decayed
392// arguments, and returns the recovered result of that translated
393// call.
394template<typename T, typename PlatformOp>
395struct Atomic::LoadImpl<
396 T,
397 PlatformOp,
398 typename EnableIf<PrimitiveConversions::Translate<T>::value>::type>
399{
400 T operator()(T const volatile* dest) const {
401 typedef PrimitiveConversions::Translate<T> Translator;
402 typedef typename Translator::Decayed Decayed;
403 STATIC_ASSERT(sizeof(T) == sizeof(Decayed));
404 Decayed result = PlatformOp()(reinterpret_cast<Decayed const volatile*>(dest));
405 return Translator::recover(result);
406 }
407};
408
409// Default implementation of atomic load if a specific platform
410// does not provide a specialization for a certain size class.
411// For increased safety, the default implementation only allows
412// load types that are pointer sized or smaller. If a platform still
413// supports wide atomics, then it has to use specialization
414// of Atomic::PlatformLoad for that wider size class.
415template<size_t byte_size>
416struct Atomic::PlatformLoad {
417 template<typename T>
418 T operator()(T const volatile* dest) const {
419 STATIC_ASSERT(sizeof(T) <= sizeof(void*)); // wide atomics need specialization
420 return *dest;
421 }
422};
423
424// Handle store for integral and enum types.
425//
426// All the involved types must be identical.
427template<typename T, typename PlatformOp>
428struct Atomic::StoreImpl<
429 T, T,
430 PlatformOp,
431 typename EnableIf<IsIntegral<T>::value || IsRegisteredEnum<T>::value>::type>
432{
433 void operator()(T new_value, T volatile* dest) const {
434 // Forward to the platform handler for the size of T.
435 PlatformOp()(new_value, dest);
436 }
437};
438
439// Handle store for pointer types.
440//
441// The new_value must be implicitly convertible to the
442// destination's type; it must be type-correct to store the
443// new_value in the destination.
444template<typename T, typename D, typename PlatformOp>
445struct Atomic::StoreImpl<
446 T*, D*,
447 PlatformOp,
448 typename EnableIf<Atomic::IsPointerConvertible<T*, D*>::value>::type>
449{
450 void operator()(T* new_value, D* volatile* dest) const {
451 // Allow derived to base conversion, and adding cv-qualifiers.
452 D* value = new_value;
453 PlatformOp()(value, dest);
454 }
455};
456
457// Handle store for types that have a translator.
458//
459// All the involved types must be identical.
460//
461// This translates the original call into a call on the decayed
462// arguments.
463template<typename T, typename PlatformOp>
464struct Atomic::StoreImpl<
465 T, T,
466 PlatformOp,
467 typename EnableIf<PrimitiveConversions::Translate<T>::value>::type>
468{
469 void operator()(T new_value, T volatile* dest) const {
470 typedef PrimitiveConversions::Translate<T> Translator;
471 typedef typename Translator::Decayed Decayed;
472 STATIC_ASSERT(sizeof(T) == sizeof(Decayed));
473 PlatformOp()(Translator::decay(new_value),
474 reinterpret_cast<Decayed volatile*>(dest));
475 }
476};
477
478// Default implementation of atomic store if a specific platform
479// does not provide a specialization for a certain size class.
480// For increased safety, the default implementation only allows
481// storing types that are pointer sized or smaller. If a platform still
482// supports wide atomics, then it has to use specialization
483// of Atomic::PlatformStore for that wider size class.
484template<size_t byte_size>
485struct Atomic::PlatformStore {
486 template<typename T>
487 void operator()(T new_value,
488 T volatile* dest) const {
489 STATIC_ASSERT(sizeof(T) <= sizeof(void*)); // wide atomics need specialization
490 (void)const_cast<T&>(*dest = new_value);
491 }
492};
493
494// Define FetchAndAdd and AddAndFetch helper classes before including
495// platform file, which may use these as base classes, requiring they
496// be complete.
497
498template<typename Derived>
499struct Atomic::FetchAndAdd {
500 template<typename I, typename D>
501 D operator()(I add_value, D volatile* dest, atomic_memory_order order) const;
502};
503
504template<typename Derived>
505struct Atomic::AddAndFetch {
506 template<typename I, typename D>
507 D operator()(I add_value, D volatile* dest, atomic_memory_order order) const;
508};
509
510template<typename D>
511inline void Atomic::inc(D volatile* dest, atomic_memory_order order) {
512 STATIC_ASSERT(IsPointer<D>::value || IsIntegral<D>::value);
513 typedef typename Conditional<IsPointer<D>::value, ptrdiff_t, D>::type I;
514 Atomic::add(I(1), dest, order);
515}
516
517template<typename D>
518inline void Atomic::dec(D volatile* dest, atomic_memory_order order) {
519 STATIC_ASSERT(IsPointer<D>::value || IsIntegral<D>::value);
520 typedef typename Conditional<IsPointer<D>::value, ptrdiff_t, D>::type I;
521 // Assumes two's complement integer representation.
522 #pragma warning(suppress: 4146)
523 Atomic::add(I(-1), dest, order);
524}
525
526template<typename I, typename D>
527inline D Atomic::sub(I sub_value, D volatile* dest, atomic_memory_order order) {
528 STATIC_ASSERT(IsPointer<D>::value || IsIntegral<D>::value);
529 STATIC_ASSERT(IsIntegral<I>::value);
530 // If D is a pointer type, use [u]intptr_t as the addend type,
531 // matching signedness of I. Otherwise, use D as the addend type.
532 typedef typename Conditional<IsSigned<I>::value, intptr_t, uintptr_t>::type PI;
533 typedef typename Conditional<IsPointer<D>::value, PI, D>::type AddendType;
534 // Only allow conversions that can't change the value.
535 STATIC_ASSERT(IsSigned<I>::value == IsSigned<AddendType>::value);
536 STATIC_ASSERT(sizeof(I) <= sizeof(AddendType));
537 AddendType addend = sub_value;
538 // Assumes two's complement integer representation.
539 #pragma warning(suppress: 4146) // In case AddendType is not signed.
540 return Atomic::add(-addend, dest, order);
541}
542
543// Define the class before including platform file, which may specialize
544// the operator definition. No generic definition of specializations
545// of the operator template are provided, nor are there any generic
546// specializations of the class. The platform file is responsible for
547// providing those.
548template<size_t byte_size>
549struct Atomic::PlatformCmpxchg {
550 template<typename T>
551 T operator()(T exchange_value,
552 T volatile* dest,
553 T compare_value,
554 atomic_memory_order order) const;
555};
556
557// Define the class before including platform file, which may use this
558// as a base class, requiring it be complete. The definition is later
559// in this file, near the other definitions related to cmpxchg.
560struct Atomic::CmpxchgByteUsingInt {
561 template<typename T>
562 T operator()(T exchange_value,
563 T volatile* dest,
564 T compare_value,
565 atomic_memory_order order) const;
566};
567
568// Define the class before including platform file, which may specialize
569// the operator definition. No generic definition of specializations
570// of the operator template are provided, nor are there any generic
571// specializations of the class. The platform file is responsible for
572// providing those.
573template<size_t byte_size>
574struct Atomic::PlatformXchg {
575 template<typename T>
576 T operator()(T exchange_value,
577 T volatile* dest,
578 atomic_memory_order order) const;
579};
580
581// platform specific in-line definitions - must come before shared definitions
582
583#include OS_CPU_HEADER(atomic)
584
585// shared in-line definitions
586
587// size_t casts...
588#if (SIZE_MAX != UINTPTR_MAX)
589#error size_t is not WORD_SIZE, interesting platform, but missing implementation here
590#endif
591
592template<typename T>
593inline T Atomic::load(const volatile T* dest) {
594 return LoadImpl<T, PlatformLoad<sizeof(T)> >()(dest);
595}
596
597template<typename T, typename D>
598inline void Atomic::store(T store_value, volatile D* dest) {
599 StoreImpl<T, D, PlatformStore<sizeof(D)> >()(store_value, dest);
600}
601
602template<typename I, typename D>
603inline D Atomic::add(I add_value, D volatile* dest,
604 atomic_memory_order order) {
605 return AddImpl<I, D>()(add_value, dest, order);
606}
607
608template<typename I, typename D>
609struct Atomic::AddImpl<
610 I, D,
611 typename EnableIf<IsIntegral<I>::value &&
612 IsIntegral<D>::value &&
613 (sizeof(I) <= sizeof(D)) &&
614 (IsSigned<I>::value == IsSigned<D>::value)>::type>
615{
616 D operator()(I add_value, D volatile* dest, atomic_memory_order order) const {
617 D addend = add_value;
618 return PlatformAdd<sizeof(D)>()(addend, dest, order);
619 }
620};
621
622template<typename I, typename P>
623struct Atomic::AddImpl<
624 I, P*,
625 typename EnableIf<IsIntegral<I>::value && (sizeof(I) <= sizeof(P*))>::type>
626{
627 P* operator()(I add_value, P* volatile* dest, atomic_memory_order order) const {
628 STATIC_ASSERT(sizeof(intptr_t) == sizeof(P*));
629 STATIC_ASSERT(sizeof(uintptr_t) == sizeof(P*));
630 typedef typename Conditional<IsSigned<I>::value,
631 intptr_t,
632 uintptr_t>::type CI;
633 CI addend = add_value;
634 return PlatformAdd<sizeof(P*)>()(addend, dest, order);
635 }
636};
637
638template<typename Derived>
639template<typename I, typename D>
640inline D Atomic::FetchAndAdd<Derived>::operator()(I add_value, D volatile* dest,
641 atomic_memory_order order) const {
642 I addend = add_value;
643 // If D is a pointer type P*, scale by sizeof(P).
644 if (IsPointer<D>::value) {
645 addend *= sizeof(typename RemovePointer<D>::type);
646 }
647 D old = static_cast<const Derived*>(this)->fetch_and_add(addend, dest, order);
648 return old + add_value;
649}
650
651template<typename Derived>
652template<typename I, typename D>
653inline D Atomic::AddAndFetch<Derived>::operator()(I add_value, D volatile* dest,
654 atomic_memory_order order) const {
655 // If D is a pointer type P*, scale by sizeof(P).
656 if (IsPointer<D>::value) {
657 add_value *= sizeof(typename RemovePointer<D>::type);
658 }
659 return static_cast<const Derived*>(this)->add_and_fetch(add_value, dest, order);
660}
661
662template<typename Type, typename Fn, typename I, typename D>
663inline D Atomic::add_using_helper(Fn fn, I add_value, D volatile* dest) {
664 return PrimitiveConversions::cast<D>(
665 fn(PrimitiveConversions::cast<Type>(add_value),
666 reinterpret_cast<Type volatile*>(dest)));
667}
668
669template<typename T, typename D, typename U>
670inline D Atomic::cmpxchg(T exchange_value,
671 D volatile* dest,
672 U compare_value,
673 atomic_memory_order order) {
674 return CmpxchgImpl<T, D, U>()(exchange_value, dest, compare_value, order);
675}
676
677template<typename T, typename D>
678inline bool Atomic::replace_if_null(T* value, D* volatile* dest,
679 atomic_memory_order order) {
680 // Presently using a trivial implementation in terms of cmpxchg.
681 // Consider adding platform support, to permit the use of compiler
682 // intrinsics like gcc's __sync_bool_compare_and_swap.
683 D* expected_null = NULL;
684 return expected_null == cmpxchg(value, dest, expected_null, order);
685}
686
687// Handle cmpxchg for integral and enum types.
688//
689// All the involved types must be identical.
690template<typename T>
691struct Atomic::CmpxchgImpl<
692 T, T, T,
693 typename EnableIf<IsIntegral<T>::value || IsRegisteredEnum<T>::value>::type>
694{
695 T operator()(T exchange_value, T volatile* dest, T compare_value,
696 atomic_memory_order order) const {
697 // Forward to the platform handler for the size of T.
698 return PlatformCmpxchg<sizeof(T)>()(exchange_value,
699 dest,
700 compare_value,
701 order);
702 }
703};
704
705// Handle cmpxchg for pointer types.
706//
707// The destination's type and the compare_value type must be the same,
708// ignoring cv-qualifiers; we don't care about the cv-qualifiers of
709// the compare_value.
710//
711// The exchange_value must be implicitly convertible to the
712// destination's type; it must be type-correct to store the
713// exchange_value in the destination.
714template<typename T, typename D, typename U>
715struct Atomic::CmpxchgImpl<
716 T*, D*, U*,
717 typename EnableIf<Atomic::IsPointerConvertible<T*, D*>::value &&
718 IsSame<typename RemoveCV<D>::type,
719 typename RemoveCV<U>::type>::value>::type>
720{
721 D* operator()(T* exchange_value, D* volatile* dest, U* compare_value,
722 atomic_memory_order order) const {
723 // Allow derived to base conversion, and adding cv-qualifiers.
724 D* new_value = exchange_value;
725 // Don't care what the CV qualifiers for compare_value are,
726 // but we need to match D* when calling platform support.
727 D* old_value = const_cast<D*>(compare_value);
728 return PlatformCmpxchg<sizeof(D*)>()(new_value, dest, old_value, order);
729 }
730};
731
732// Handle cmpxchg for types that have a translator.
733//
734// All the involved types must be identical.
735//
736// This translates the original call into a call on the decayed
737// arguments, and returns the recovered result of that translated
738// call.
739template<typename T>
740struct Atomic::CmpxchgImpl<
741 T, T, T,
742 typename EnableIf<PrimitiveConversions::Translate<T>::value>::type>
743{
744 T operator()(T exchange_value, T volatile* dest, T compare_value,
745 atomic_memory_order order) const {
746 typedef PrimitiveConversions::Translate<T> Translator;
747 typedef typename Translator::Decayed Decayed;
748 STATIC_ASSERT(sizeof(T) == sizeof(Decayed));
749 return Translator::recover(
750 cmpxchg(Translator::decay(exchange_value),
751 reinterpret_cast<Decayed volatile*>(dest),
752 Translator::decay(compare_value),
753 order));
754 }
755};
756
757template<typename Type, typename Fn, typename T>
758inline T Atomic::cmpxchg_using_helper(Fn fn,
759 T exchange_value,
760 T volatile* dest,
761 T compare_value) {
762 STATIC_ASSERT(sizeof(Type) == sizeof(T));
763 return PrimitiveConversions::cast<T>(
764 fn(PrimitiveConversions::cast<Type>(exchange_value),
765 reinterpret_cast<Type volatile*>(dest),
766 PrimitiveConversions::cast<Type>(compare_value)));
767}
768
769template<typename T>
770inline T Atomic::CmpxchgByteUsingInt::operator()(T exchange_value,
771 T volatile* dest,
772 T compare_value,
773 atomic_memory_order order) const {
774 STATIC_ASSERT(sizeof(T) == sizeof(uint8_t));
775 uint8_t canon_exchange_value = exchange_value;
776 uint8_t canon_compare_value = compare_value;
777 volatile uint32_t* aligned_dest
778 = reinterpret_cast<volatile uint32_t*>(align_down(dest, sizeof(uint32_t)));
779 size_t offset = pointer_delta(dest, aligned_dest, 1);
780 uint32_t cur = *aligned_dest;
781 uint8_t* cur_as_bytes = reinterpret_cast<uint8_t*>(&cur);
782
783 // current value may not be what we are looking for, so force it
784 // to that value so the initial cmpxchg will fail if it is different
785 cur_as_bytes[offset] = canon_compare_value;
786
787 // always execute a real cmpxchg so that we get the required memory
788 // barriers even on initial failure
789 do {
790 // value to swap in matches current value ...
791 uint32_t new_value = cur;
792 // ... except for the one byte we want to update
793 reinterpret_cast<uint8_t*>(&new_value)[offset] = canon_exchange_value;
794
795 uint32_t res = cmpxchg(new_value, aligned_dest, cur, order);
796 if (res == cur) break; // success
797
798 // at least one byte in the int changed value, so update
799 // our view of the current int
800 cur = res;
801 // if our byte is still as cur we loop and try again
802 } while (cur_as_bytes[offset] == canon_compare_value);
803
804 return PrimitiveConversions::cast<T>(cur_as_bytes[offset]);
805}
806
807// Handle xchg for integral and enum types.
808//
809// All the involved types must be identical.
810template<typename T>
811struct Atomic::XchgImpl<
812 T, T,
813 typename EnableIf<IsIntegral<T>::value || IsRegisteredEnum<T>::value>::type>
814{
815 T operator()(T exchange_value, T volatile* dest, atomic_memory_order order) const {
816 // Forward to the platform handler for the size of T.
817 return PlatformXchg<sizeof(T)>()(exchange_value, dest, order);
818 }
819};
820
821// Handle xchg for pointer types.
822//
823// The exchange_value must be implicitly convertible to the
824// destination's type; it must be type-correct to store the
825// exchange_value in the destination.
826template<typename T, typename D>
827struct Atomic::XchgImpl<
828 T*, D*,
829 typename EnableIf<Atomic::IsPointerConvertible<T*, D*>::value>::type>
830{
831 D* operator()(T* exchange_value, D* volatile* dest, atomic_memory_order order) const {
832 // Allow derived to base conversion, and adding cv-qualifiers.
833 D* new_value = exchange_value;
834 return PlatformXchg<sizeof(D*)>()(new_value, dest, order);
835 }
836};
837
838// Handle xchg for types that have a translator.
839//
840// All the involved types must be identical.
841//
842// This translates the original call into a call on the decayed
843// arguments, and returns the recovered result of that translated
844// call.
845template<typename T>
846struct Atomic::XchgImpl<
847 T, T,
848 typename EnableIf<PrimitiveConversions::Translate<T>::value>::type>
849{
850 T operator()(T exchange_value, T volatile* dest, atomic_memory_order order) const {
851 typedef PrimitiveConversions::Translate<T> Translator;
852 typedef typename Translator::Decayed Decayed;
853 STATIC_ASSERT(sizeof(T) == sizeof(Decayed));
854 return Translator::recover(
855 xchg(Translator::decay(exchange_value),
856 reinterpret_cast<Decayed volatile*>(dest),
857 order));
858 }
859};
860
861template<typename Type, typename Fn, typename T>
862inline T Atomic::xchg_using_helper(Fn fn,
863 T exchange_value,
864 T volatile* dest) {
865 STATIC_ASSERT(sizeof(Type) == sizeof(T));
866 return PrimitiveConversions::cast<T>(
867 fn(PrimitiveConversions::cast<Type>(exchange_value),
868 reinterpret_cast<Type volatile*>(dest)));
869}
870
871template<typename T, typename D>
872inline D Atomic::xchg(T exchange_value, volatile D* dest, atomic_memory_order order) {
873 return XchgImpl<T, D>()(exchange_value, dest, order);
874}
875
876#endif // SHARE_RUNTIME_ATOMIC_HPP
877