xref: /third_party/node/deps/v8/src/base/atomicops.h (revision 1cb0ef41)
1// Copyright 2010 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// The routines exported by this module are subtle.  If you use them, even if
6// you get the code right, it will depend on careful reasoning about atomicity
7// and memory ordering; it will be less readable, and harder to maintain.  If
8// you plan to use these routines, you should have a good reason, such as solid
9// evidence that performance would otherwise suffer, or there being no
10// alternative.  You should assume only properties explicitly guaranteed by the
11// specifications in this file.  You are almost certainly _not_ writing code
12// just for the x86; if you assume x86 semantics, x86 hardware bugs and
13// implementations on other archtectures will cause your code to break.  If you
14// do not know what you are doing, avoid these routines, and use a Mutex.
15//
16// It is incorrect to make direct assignments to/from an atomic variable.
17// You should use one of the Load or Store routines.  The Relaxed  versions
18// are provided when no fences are needed:
19//   Relaxed_Store()
20//   Relaxed_Load()
21// Although there are currently no compiler enforcement, you are encouraged
22// to use these.
23//
24
25#ifndef V8_BASE_ATOMICOPS_H_
26#define V8_BASE_ATOMICOPS_H_
27
28#include <stdint.h>
29
30#include <atomic>
31
32// Small C++ header which defines implementation specific macros used to
33// identify the STL implementation.
34// - libc++: captures __config for _LIBCPP_VERSION
35// - libstdc++: captures bits/c++config.h for __GLIBCXX__
36#include <cstddef>
37
38#include "src/base/base-export.h"
39#include "src/base/build_config.h"
40#include "src/base/macros.h"
41
42#if defined(V8_OS_STARBOARD)
43#include "starboard/atomic.h"
44#endif  // V8_OS_STARBOARD
45
46namespace v8 {
47namespace base {
48
49#ifdef V8_OS_STARBOARD
50using Atomic8 = SbAtomic8;
51using Atomic16 = int16_t;
52using Atomic32 = SbAtomic32;
53#if SB_IS_64_BIT
54using Atomic64 = SbAtomic64;
55#endif
56#else
57using Atomic8 = char;
58using Atomic16 = int16_t;
59using Atomic32 = int32_t;
60#if defined(V8_HOST_ARCH_64_BIT)
61// We need to be able to go between Atomic64 and AtomicWord implicitly.  This
62// means Atomic64 and AtomicWord should be the same type on 64-bit.
63#if defined(__ILP32__)
64using Atomic64 = int64_t;
65#else
66using Atomic64 = intptr_t;
67#endif  // defined(__ILP32__)
68#endif  // defined(V8_HOST_ARCH_64_BIT)
69#endif  // V8_OS_STARBOARD
70
71// Use AtomicWord for a machine-sized pointer.  It will use the Atomic32 or
72// Atomic64 routines below, depending on your architecture.
73#if defined(V8_OS_STARBOARD)
74using AtomicWord = SbAtomicPtr;
75#else
76using AtomicWord = intptr_t;
77#endif
78
79namespace helper {
80template <typename T>
81volatile std::atomic<T>* to_std_atomic(volatile T* ptr) {
82  return reinterpret_cast<volatile std::atomic<T>*>(ptr);
83}
84template <typename T>
85volatile const std::atomic<T>* to_std_atomic_const(volatile const T* ptr) {
86  return reinterpret_cast<volatile const std::atomic<T>*>(ptr);
87}
88}  // namespace helper
89
90inline void SeqCst_MemoryFence() {
91  std::atomic_thread_fence(std::memory_order_seq_cst);
92}
93
94// Atomically execute:
95//   result = *ptr;
96//   if (result == old_value)
97//     *ptr = new_value;
98//   return result;
99//
100// I.e. replace |*ptr| with |new_value| if |*ptr| used to be |old_value|.
101// Always return the value of |*ptr| before the operation.
102// Acquire, Relaxed, Release correspond to standard C++ memory orders.
103inline Atomic8 Relaxed_CompareAndSwap(volatile Atomic8* ptr, Atomic8 old_value,
104                                      Atomic8 new_value) {
105  std::atomic_compare_exchange_strong_explicit(
106      helper::to_std_atomic(ptr), &old_value, new_value,
107      std::memory_order_relaxed, std::memory_order_relaxed);
108  return old_value;
109}
110
111inline Atomic16 Relaxed_CompareAndSwap(volatile Atomic16* ptr,
112                                       Atomic16 old_value, Atomic16 new_value) {
113  std::atomic_compare_exchange_strong_explicit(
114      helper::to_std_atomic(ptr), &old_value, new_value,
115      std::memory_order_relaxed, std::memory_order_relaxed);
116  return old_value;
117}
118
119inline Atomic32 Relaxed_CompareAndSwap(volatile Atomic32* ptr,
120                                       Atomic32 old_value, Atomic32 new_value) {
121  std::atomic_compare_exchange_strong_explicit(
122      helper::to_std_atomic(ptr), &old_value, new_value,
123      std::memory_order_relaxed, std::memory_order_relaxed);
124  return old_value;
125}
126
127inline Atomic32 Relaxed_AtomicExchange(volatile Atomic32* ptr,
128                                       Atomic32 new_value) {
129  return std::atomic_exchange_explicit(helper::to_std_atomic(ptr), new_value,
130                                       std::memory_order_relaxed);
131}
132
133inline Atomic32 SeqCst_AtomicExchange(volatile Atomic32* ptr,
134                                      Atomic32 new_value) {
135  return std::atomic_exchange_explicit(helper::to_std_atomic(ptr), new_value,
136                                       std::memory_order_seq_cst);
137}
138
139inline Atomic32 Relaxed_AtomicIncrement(volatile Atomic32* ptr,
140                                        Atomic32 increment) {
141  return increment + std::atomic_fetch_add_explicit(helper::to_std_atomic(ptr),
142                                                    increment,
143                                                    std::memory_order_relaxed);
144}
145
146inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
147                                       Atomic32 old_value, Atomic32 new_value) {
148  atomic_compare_exchange_strong_explicit(
149      helper::to_std_atomic(ptr), &old_value, new_value,
150      std::memory_order_acquire, std::memory_order_acquire);
151  return old_value;
152}
153
154inline Atomic8 Release_CompareAndSwap(volatile Atomic8* ptr, Atomic8 old_value,
155                                      Atomic8 new_value) {
156  bool result = atomic_compare_exchange_strong_explicit(
157      helper::to_std_atomic(ptr), &old_value, new_value,
158      std::memory_order_release, std::memory_order_relaxed);
159  USE(result);  // Make gcc compiler happy.
160  return old_value;
161}
162
163inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
164                                       Atomic32 old_value, Atomic32 new_value) {
165  atomic_compare_exchange_strong_explicit(
166      helper::to_std_atomic(ptr), &old_value, new_value,
167      std::memory_order_release, std::memory_order_relaxed);
168  return old_value;
169}
170
171inline Atomic32 AcquireRelease_CompareAndSwap(volatile Atomic32* ptr,
172                                              Atomic32 old_value,
173                                              Atomic32 new_value) {
174  atomic_compare_exchange_strong_explicit(
175      helper::to_std_atomic(ptr), &old_value, new_value,
176      std::memory_order_acq_rel, std::memory_order_acquire);
177  return old_value;
178}
179
180inline void Relaxed_Store(volatile Atomic8* ptr, Atomic8 value) {
181  std::atomic_store_explicit(helper::to_std_atomic(ptr), value,
182                             std::memory_order_relaxed);
183}
184
185inline void Relaxed_Store(volatile Atomic16* ptr, Atomic16 value) {
186  std::atomic_store_explicit(helper::to_std_atomic(ptr), value,
187                             std::memory_order_relaxed);
188}
189
190inline void Relaxed_Store(volatile Atomic32* ptr, Atomic32 value) {
191  std::atomic_store_explicit(helper::to_std_atomic(ptr), value,
192                             std::memory_order_relaxed);
193}
194
195inline void Release_Store(volatile Atomic8* ptr, Atomic8 value) {
196  std::atomic_store_explicit(helper::to_std_atomic(ptr), value,
197                             std::memory_order_release);
198}
199
200inline void Release_Store(volatile Atomic16* ptr, Atomic16 value) {
201  std::atomic_store_explicit(helper::to_std_atomic(ptr), value,
202                             std::memory_order_release);
203}
204
205inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
206  std::atomic_store_explicit(helper::to_std_atomic(ptr), value,
207                             std::memory_order_release);
208}
209
210inline void SeqCst_Store(volatile Atomic8* ptr, Atomic8 value) {
211  std::atomic_store_explicit(helper::to_std_atomic(ptr), value,
212                             std::memory_order_seq_cst);
213}
214
215inline void SeqCst_Store(volatile Atomic16* ptr, Atomic16 value) {
216  std::atomic_store_explicit(helper::to_std_atomic(ptr), value,
217                             std::memory_order_seq_cst);
218}
219
220inline void SeqCst_Store(volatile Atomic32* ptr, Atomic32 value) {
221  std::atomic_store_explicit(helper::to_std_atomic(ptr), value,
222                             std::memory_order_seq_cst);
223}
224
225inline Atomic8 Relaxed_Load(volatile const Atomic8* ptr) {
226  return std::atomic_load_explicit(helper::to_std_atomic_const(ptr),
227                                   std::memory_order_relaxed);
228}
229
230inline Atomic16 Relaxed_Load(volatile const Atomic16* ptr) {
231  return std::atomic_load_explicit(helper::to_std_atomic_const(ptr),
232                                   std::memory_order_relaxed);
233}
234
235inline Atomic32 Relaxed_Load(volatile const Atomic32* ptr) {
236  return std::atomic_load_explicit(helper::to_std_atomic_const(ptr),
237                                   std::memory_order_relaxed);
238}
239
240inline Atomic8 Acquire_Load(volatile const Atomic8* ptr) {
241  return std::atomic_load_explicit(helper::to_std_atomic_const(ptr),
242                                   std::memory_order_acquire);
243}
244
245inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
246  return std::atomic_load_explicit(helper::to_std_atomic_const(ptr),
247                                   std::memory_order_acquire);
248}
249
250inline Atomic8 SeqCst_Load(volatile const Atomic8* ptr) {
251  return std::atomic_load_explicit(helper::to_std_atomic_const(ptr),
252                                   std::memory_order_seq_cst);
253}
254
255inline Atomic32 SeqCst_Load(volatile const Atomic32* ptr) {
256  return std::atomic_load_explicit(helper::to_std_atomic_const(ptr),
257                                   std::memory_order_seq_cst);
258}
259
260#if defined(V8_HOST_ARCH_64_BIT)
261
262inline Atomic64 Relaxed_CompareAndSwap(volatile Atomic64* ptr,
263                                       Atomic64 old_value, Atomic64 new_value) {
264  std::atomic_compare_exchange_strong_explicit(
265      helper::to_std_atomic(ptr), &old_value, new_value,
266      std::memory_order_relaxed, std::memory_order_relaxed);
267  return old_value;
268}
269
270inline Atomic64 Relaxed_AtomicExchange(volatile Atomic64* ptr,
271                                       Atomic64 new_value) {
272  return std::atomic_exchange_explicit(helper::to_std_atomic(ptr), new_value,
273                                       std::memory_order_relaxed);
274}
275
276inline Atomic64 SeqCst_AtomicExchange(volatile Atomic64* ptr,
277                                      Atomic64 new_value) {
278  return std::atomic_exchange_explicit(helper::to_std_atomic(ptr), new_value,
279                                       std::memory_order_seq_cst);
280}
281
282inline Atomic64 Relaxed_AtomicIncrement(volatile Atomic64* ptr,
283                                        Atomic64 increment) {
284  return increment + std::atomic_fetch_add_explicit(helper::to_std_atomic(ptr),
285                                                    increment,
286                                                    std::memory_order_relaxed);
287}
288
289inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
290                                       Atomic64 old_value, Atomic64 new_value) {
291  std::atomic_compare_exchange_strong_explicit(
292      helper::to_std_atomic(ptr), &old_value, new_value,
293      std::memory_order_acquire, std::memory_order_acquire);
294  return old_value;
295}
296
297inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr,
298                                       Atomic64 old_value, Atomic64 new_value) {
299  std::atomic_compare_exchange_strong_explicit(
300      helper::to_std_atomic(ptr), &old_value, new_value,
301      std::memory_order_release, std::memory_order_relaxed);
302  return old_value;
303}
304
305inline Atomic64 AcquireRelease_CompareAndSwap(volatile Atomic64* ptr,
306                                              Atomic64 old_value,
307                                              Atomic64 new_value) {
308  std::atomic_compare_exchange_strong_explicit(
309      helper::to_std_atomic(ptr), &old_value, new_value,
310      std::memory_order_acq_rel, std::memory_order_acquire);
311  return old_value;
312}
313
314inline void Relaxed_Store(volatile Atomic64* ptr, Atomic64 value) {
315  std::atomic_store_explicit(helper::to_std_atomic(ptr), value,
316                             std::memory_order_relaxed);
317}
318
319inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
320  std::atomic_store_explicit(helper::to_std_atomic(ptr), value,
321                             std::memory_order_release);
322}
323
324inline void SeqCst_Store(volatile Atomic64* ptr, Atomic64 value) {
325  std::atomic_store_explicit(helper::to_std_atomic(ptr), value,
326                             std::memory_order_seq_cst);
327}
328
329inline Atomic64 Relaxed_Load(volatile const Atomic64* ptr) {
330  return std::atomic_load_explicit(helper::to_std_atomic_const(ptr),
331                                   std::memory_order_relaxed);
332}
333
334inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
335  return std::atomic_load_explicit(helper::to_std_atomic_const(ptr),
336                                   std::memory_order_acquire);
337}
338
339inline Atomic64 SeqCst_Load(volatile const Atomic64* ptr) {
340  return std::atomic_load_explicit(helper::to_std_atomic_const(ptr),
341                                   std::memory_order_seq_cst);
342}
343
344#endif  // defined(V8_HOST_ARCH_64_BIT)
345
346inline void Relaxed_Memcpy(volatile Atomic8* dst, volatile const Atomic8* src,
347                           size_t bytes) {
348  constexpr size_t kAtomicWordSize = sizeof(AtomicWord);
349  while (bytes > 0 &&
350         !IsAligned(reinterpret_cast<uintptr_t>(dst), kAtomicWordSize)) {
351    Relaxed_Store(dst++, Relaxed_Load(src++));
352    --bytes;
353  }
354  if (IsAligned(reinterpret_cast<uintptr_t>(src), kAtomicWordSize) &&
355      IsAligned(reinterpret_cast<uintptr_t>(dst), kAtomicWordSize)) {
356    while (bytes >= kAtomicWordSize) {
357      Relaxed_Store(
358          reinterpret_cast<volatile AtomicWord*>(dst),
359          Relaxed_Load(reinterpret_cast<const volatile AtomicWord*>(src)));
360      dst += kAtomicWordSize;
361      src += kAtomicWordSize;
362      bytes -= kAtomicWordSize;
363    }
364  }
365  while (bytes > 0) {
366    Relaxed_Store(dst++, Relaxed_Load(src++));
367    --bytes;
368  }
369}
370
371inline void Relaxed_Memmove(volatile Atomic8* dst, volatile const Atomic8* src,
372                            size_t bytes) {
373  // Use Relaxed_Memcpy if copying forwards is safe. This is the case if there
374  // is no overlap, or {dst} lies before {src}.
375  // This single check checks for both:
376  if (reinterpret_cast<uintptr_t>(dst) - reinterpret_cast<uintptr_t>(src) >=
377      bytes) {
378    Relaxed_Memcpy(dst, src, bytes);
379    return;
380  }
381
382  // Otherwise copy backwards.
383  dst += bytes;
384  src += bytes;
385  constexpr size_t kAtomicWordSize = sizeof(AtomicWord);
386  while (bytes > 0 &&
387         !IsAligned(reinterpret_cast<uintptr_t>(dst), kAtomicWordSize)) {
388    Relaxed_Store(--dst, Relaxed_Load(--src));
389    --bytes;
390  }
391  if (IsAligned(reinterpret_cast<uintptr_t>(src), kAtomicWordSize) &&
392      IsAligned(reinterpret_cast<uintptr_t>(dst), kAtomicWordSize)) {
393    while (bytes >= kAtomicWordSize) {
394      dst -= kAtomicWordSize;
395      src -= kAtomicWordSize;
396      bytes -= kAtomicWordSize;
397      Relaxed_Store(
398          reinterpret_cast<volatile AtomicWord*>(dst),
399          Relaxed_Load(reinterpret_cast<const volatile AtomicWord*>(src)));
400    }
401  }
402  while (bytes > 0) {
403    Relaxed_Store(--dst, Relaxed_Load(--src));
404    --bytes;
405  }
406}
407
408namespace helper {
409inline int MemcmpNotEqualFundamental(Atomic8 u1, Atomic8 u2) {
410  DCHECK_NE(u1, u2);
411  return u1 < u2 ? -1 : 1;
412}
413inline int MemcmpNotEqualFundamental(AtomicWord u1, AtomicWord u2) {
414  DCHECK_NE(u1, u2);
415#if defined(V8_TARGET_BIG_ENDIAN)
416  return u1 < u2 ? -1 : 1;
417#else
418  for (size_t i = 0; i < sizeof(AtomicWord); ++i) {
419    uint8_t byte1 = u1 & 0xFF;
420    uint8_t byte2 = u2 & 0xFF;
421    if (byte1 != byte2) return byte1 < byte2 ? -1 : 1;
422    u1 >>= 8;
423    u2 >>= 8;
424  }
425  UNREACHABLE();
426#endif
427}
428}  // namespace helper
429
430inline int Relaxed_Memcmp(volatile const Atomic8* s1,
431                          volatile const Atomic8* s2, size_t len) {
432  constexpr size_t kAtomicWordSize = sizeof(AtomicWord);
433  while (len > 0 &&
434         !(IsAligned(reinterpret_cast<uintptr_t>(s1), kAtomicWordSize) &&
435           IsAligned(reinterpret_cast<uintptr_t>(s2), kAtomicWordSize))) {
436    Atomic8 u1 = Relaxed_Load(s1++);
437    Atomic8 u2 = Relaxed_Load(s2++);
438    if (u1 != u2) return helper::MemcmpNotEqualFundamental(u1, u2);
439    --len;
440  }
441
442  if (IsAligned(reinterpret_cast<uintptr_t>(s1), kAtomicWordSize) &&
443      IsAligned(reinterpret_cast<uintptr_t>(s2), kAtomicWordSize)) {
444    while (len >= kAtomicWordSize) {
445      AtomicWord u1 =
446          Relaxed_Load(reinterpret_cast<const volatile AtomicWord*>(s1));
447      AtomicWord u2 =
448          Relaxed_Load(reinterpret_cast<const volatile AtomicWord*>(s2));
449      if (u1 != u2) return helper::MemcmpNotEqualFundamental(u1, u2);
450      s1 += kAtomicWordSize;
451      s2 += kAtomicWordSize;
452      len -= kAtomicWordSize;
453    }
454  }
455
456  while (len > 0) {
457    Atomic8 u1 = Relaxed_Load(s1++);
458    Atomic8 u2 = Relaxed_Load(s2++);
459    if (u1 != u2) return helper::MemcmpNotEqualFundamental(u1, u2);
460    --len;
461  }
462
463  return 0;
464}
465
466}  // namespace base
467}  // namespace v8
468
469// On some platforms we need additional declarations to make
470// AtomicWord compatible with our other Atomic* types.
471#if defined(V8_OS_DARWIN) || defined(V8_OS_OPENBSD) || defined(V8_OS_AIX)
472#include "src/base/atomicops_internals_atomicword_compat.h"
473#endif
474
475#endif  // V8_BASE_ATOMICOPS_H_
476