1 // Copyright 2014 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // This file is an internal atomic implementation, use atomicops.h instead.
6 //
7 // This implementation uses C++11 atomics' member functions. The code base is
8 // currently written assuming atomicity revolves around accesses instead of
9 // C++11's memory locations. The burden is on the programmer to ensure that all
10 // memory locations accessed atomically are never accessed non-atomically (tsan
11 // should help with this).
12 //
13 // TODO(jfb) Modify the atomicops.h API and user code to declare atomic
14 // locations as truly atomic. See the static_assert below.
15 //
16 // Of note in this implementation:
17 // * All NoBarrier variants are implemented as relaxed.
18 // * All Barrier variants are implemented as sequentially-consistent.
19 // * Compare exchange's failure ordering is always the same as the success one
20 // (except for release, which fails as relaxed): using a weaker ordering is
21 // only valid under certain uses of compare exchange.
22 // * Atomic increment is expected to return the post-incremented value, whereas
23 // C11 fetch add returns the previous value. The implementation therefore
24 // needs to increment twice (which the compiler should be able to detect and
25 // optimize).
26
27 #ifndef BASE_ATOMICOPS_INTERNALS_PORTABLE_H_
28 #define BASE_ATOMICOPS_INTERNALS_PORTABLE_H_
29
30 #include <atomic>
31
32 #include "build/build_config.h"
33
34 namespace base {
35 namespace subtle {
36
37 // This implementation is transitional and maintains the original API for
38 // atomicops.h. This requires casting memory locations to the atomic types, and
39 // assumes that the API and the C++11 implementation are layout-compatible,
40 // which isn't true for all implementations or hardware platforms. The static
41 // assertion should detect this issue, were it to fire then this header
42 // shouldn't be used.
43 //
44 // TODO(jfb) If this header manages to stay committed then the API should be
45 // modified, and all call sites updated.
46 typedef volatile std::atomic<Atomic32>* AtomicLocation32;
47 static_assert(sizeof(*(AtomicLocation32) nullptr) == sizeof(Atomic32),
48 "incompatible 32-bit atomic layout");
49
NoBarrier_CompareAndSwap(volatile Atomic32 * ptr,Atomic32 old_value,Atomic32 new_value)50 inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
51 Atomic32 old_value,
52 Atomic32 new_value) {
53 ((AtomicLocation32)ptr)
54 ->compare_exchange_strong(old_value,
55 new_value,
56 std::memory_order_relaxed,
57 std::memory_order_relaxed);
58 return old_value;
59 }
60
NoBarrier_AtomicExchange(volatile Atomic32 * ptr,Atomic32 new_value)61 inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
62 Atomic32 new_value) {
63 return ((AtomicLocation32)ptr)
64 ->exchange(new_value, std::memory_order_relaxed);
65 }
66
NoBarrier_AtomicIncrement(volatile Atomic32 * ptr,Atomic32 increment)67 inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr,
68 Atomic32 increment) {
69 return increment +
70 ((AtomicLocation32)ptr)
71 ->fetch_add(increment, std::memory_order_relaxed);
72 }
73
Barrier_AtomicIncrement(volatile Atomic32 * ptr,Atomic32 increment)74 inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
75 Atomic32 increment) {
76 return increment + ((AtomicLocation32)ptr)->fetch_add(increment);
77 }
78
Acquire_CompareAndSwap(volatile Atomic32 * ptr,Atomic32 old_value,Atomic32 new_value)79 inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
80 Atomic32 old_value,
81 Atomic32 new_value) {
82 ((AtomicLocation32)ptr)
83 ->compare_exchange_strong(old_value,
84 new_value,
85 std::memory_order_acquire,
86 std::memory_order_acquire);
87 return old_value;
88 }
89
Release_CompareAndSwap(volatile Atomic32 * ptr,Atomic32 old_value,Atomic32 new_value)90 inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
91 Atomic32 old_value,
92 Atomic32 new_value) {
93 ((AtomicLocation32)ptr)
94 ->compare_exchange_strong(old_value,
95 new_value,
96 std::memory_order_release,
97 std::memory_order_relaxed);
98 return old_value;
99 }
100
NoBarrier_Store(volatile Atomic32 * ptr,Atomic32 value)101 inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
102 ((AtomicLocation32)ptr)->store(value, std::memory_order_relaxed);
103 }
104
Release_Store(volatile Atomic32 * ptr,Atomic32 value)105 inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
106 ((AtomicLocation32)ptr)->store(value, std::memory_order_release);
107 }
108
NoBarrier_Load(volatile const Atomic32 * ptr)109 inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) {
110 return ((AtomicLocation32)ptr)->load(std::memory_order_relaxed);
111 }
112
Acquire_Load(volatile const Atomic32 * ptr)113 inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
114 return ((AtomicLocation32)ptr)->load(std::memory_order_acquire);
115 }
116
117 #if defined(ARCH_CPU_64_BITS)
118
119 typedef volatile std::atomic<Atomic64>* AtomicLocation64;
120 static_assert(sizeof(*(AtomicLocation64) nullptr) == sizeof(Atomic64),
121 "incompatible 64-bit atomic layout");
122
NoBarrier_CompareAndSwap(volatile Atomic64 * ptr,Atomic64 old_value,Atomic64 new_value)123 inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
124 Atomic64 old_value,
125 Atomic64 new_value) {
126 ((AtomicLocation64)ptr)
127 ->compare_exchange_strong(old_value,
128 new_value,
129 std::memory_order_relaxed,
130 std::memory_order_relaxed);
131 return old_value;
132 }
133
NoBarrier_AtomicExchange(volatile Atomic64 * ptr,Atomic64 new_value)134 inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr,
135 Atomic64 new_value) {
136 return ((AtomicLocation64)ptr)
137 ->exchange(new_value, std::memory_order_relaxed);
138 }
139
NoBarrier_AtomicIncrement(volatile Atomic64 * ptr,Atomic64 increment)140 inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr,
141 Atomic64 increment) {
142 return increment +
143 ((AtomicLocation64)ptr)
144 ->fetch_add(increment, std::memory_order_relaxed);
145 }
146
Barrier_AtomicIncrement(volatile Atomic64 * ptr,Atomic64 increment)147 inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr,
148 Atomic64 increment) {
149 return increment + ((AtomicLocation64)ptr)->fetch_add(increment);
150 }
151
Acquire_CompareAndSwap(volatile Atomic64 * ptr,Atomic64 old_value,Atomic64 new_value)152 inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
153 Atomic64 old_value,
154 Atomic64 new_value) {
155 ((AtomicLocation64)ptr)
156 ->compare_exchange_strong(old_value,
157 new_value,
158 std::memory_order_acquire,
159 std::memory_order_acquire);
160 return old_value;
161 }
162
Release_CompareAndSwap(volatile Atomic64 * ptr,Atomic64 old_value,Atomic64 new_value)163 inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr,
164 Atomic64 old_value,
165 Atomic64 new_value) {
166 ((AtomicLocation64)ptr)
167 ->compare_exchange_strong(old_value,
168 new_value,
169 std::memory_order_release,
170 std::memory_order_relaxed);
171 return old_value;
172 }
173
NoBarrier_Store(volatile Atomic64 * ptr,Atomic64 value)174 inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
175 ((AtomicLocation64)ptr)->store(value, std::memory_order_relaxed);
176 }
177
Release_Store(volatile Atomic64 * ptr,Atomic64 value)178 inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
179 ((AtomicLocation64)ptr)->store(value, std::memory_order_release);
180 }
181
NoBarrier_Load(volatile const Atomic64 * ptr)182 inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) {
183 return ((AtomicLocation64)ptr)->load(std::memory_order_relaxed);
184 }
185
Acquire_Load(volatile const Atomic64 * ptr)186 inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
187 return ((AtomicLocation64)ptr)->load(std::memory_order_acquire);
188 }
189
190 #endif // defined(ARCH_CPU_64_BITS)
191 } // namespace subtle
192 } // namespace base
193
194 #endif // BASE_ATOMICOPS_INTERNALS_PORTABLE_H_
195