atomic.h 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. // Copyright (c) Meta Platforms, Inc. and affiliates.
  2. // This source code is licensed under both the GPLv2 (found in the
  3. // COPYING file in the root directory) and Apache 2.0 License
  4. // (found in the LICENSE.Apache file in the root directory).
  5. #pragma once
  6. #include <atomic>
  7. #include "rocksdb/rocksdb_namespace.h"
  8. namespace ROCKSDB_NAMESPACE {
  9. // Background:
  10. // std::atomic is somewhat easy to misuse:
  11. // * Implicit conversion to T using std::memory_order_seq_cst, along with
  12. // memory order parameter defaults, make it easy to accidentally mix sequential
  13. // consistency ordering with acquire/release memory ordering. See
  14. // "The single total order might not be consistent with happens-before" at
  15. // https://en.cppreference.com/w/cpp/atomic/memory_order
  16. // * It's easy to use nonsensical (UB) combinations like store with
  17. // std::memory_order_acquire.
  18. // * It is unlikely that anything in RocksDB will need std::memory_order_seq_cst
  19. // because sequential consistency for the user, potentially writing from
  20. // multiple threads, is provided by explicit versioning with sequence numbers.
  21. // If threads A & B update separate atomics, it's typically OK if threads C & D
  22. // see those updates in different orders.
  23. //
  24. // For such reasons, we provide wrappers below to make safe usage easier.
  25. // Wrapper around std::atomic to avoid certain bugs (see Background above).
  26. //
  27. // This relaxed-only wrapper is intended for atomics that do not need
  28. // ordering constraints with other data reads/writes aside from those
  29. // necessary for computing data values or given by other happens-before
  30. // relationships. For example, a cross-thread counter that never returns
  31. // the same result can be a RelaxedAtomic.
  32. template <typename T>
  33. class RelaxedAtomic {
  34. public:
  35. explicit RelaxedAtomic(T initial = {}) : v_(initial) {}
  36. void StoreRelaxed(T desired) { v_.store(desired, std::memory_order_relaxed); }
  37. T LoadRelaxed() const { return v_.load(std::memory_order_relaxed); }
  38. bool CasWeakRelaxed(T& expected, T desired) {
  39. return v_.compare_exchange_weak(expected, desired,
  40. std::memory_order_relaxed);
  41. }
  42. bool CasStrongRelaxed(T& expected, T desired) {
  43. return v_.compare_exchange_strong(expected, desired,
  44. std::memory_order_relaxed);
  45. }
  46. T ExchangeRelaxed(T desired) {
  47. return v_.exchange(desired, std::memory_order_relaxed);
  48. }
  49. T FetchAddRelaxed(T operand) {
  50. return v_.fetch_add(operand, std::memory_order_relaxed);
  51. }
  52. T FetchSubRelaxed(T operand) {
  53. return v_.fetch_sub(operand, std::memory_order_relaxed);
  54. }
  55. T FetchAndRelaxed(T operand) {
  56. return v_.fetch_and(operand, std::memory_order_relaxed);
  57. }
  58. T FetchOrRelaxed(T operand) {
  59. return v_.fetch_or(operand, std::memory_order_relaxed);
  60. }
  61. T FetchXorRelaxed(T operand) {
  62. return v_.fetch_xor(operand, std::memory_order_relaxed);
  63. }
  64. protected:
  65. std::atomic<T> v_;
  66. };
  67. // Wrapper around std::atomic to avoid certain bugs (see Background above).
  68. //
  69. // Except for some unusual cases requiring sequential consistency, this is
  70. // a general-purpose atomic. Relaxed operations can be mixed in as appropriate.
  71. template <typename T>
  72. class AcqRelAtomic : public RelaxedAtomic<T> {
  73. public:
  74. explicit AcqRelAtomic(T initial = {}) : RelaxedAtomic<T>(initial) {}
  75. void Store(T desired) {
  76. RelaxedAtomic<T>::v_.store(desired, std::memory_order_release);
  77. }
  78. T Load() const {
  79. return RelaxedAtomic<T>::v_.load(std::memory_order_acquire);
  80. }
  81. bool CasWeak(T& expected, T desired) {
  82. return RelaxedAtomic<T>::v_.compare_exchange_weak(
  83. expected, desired, std::memory_order_acq_rel);
  84. }
  85. bool CasStrong(T& expected, T desired) {
  86. return RelaxedAtomic<T>::v_.compare_exchange_strong(
  87. expected, desired, std::memory_order_acq_rel);
  88. }
  89. T Exchange(T desired) {
  90. return RelaxedAtomic<T>::v_.exchange(desired, std::memory_order_acq_rel);
  91. }
  92. T FetchAdd(T operand) {
  93. return RelaxedAtomic<T>::v_.fetch_add(operand, std::memory_order_acq_rel);
  94. }
  95. T FetchSub(T operand) {
  96. return RelaxedAtomic<T>::v_.fetch_sub(operand, std::memory_order_acq_rel);
  97. }
  98. T FetchAnd(T operand) {
  99. return RelaxedAtomic<T>::v_.fetch_and(operand, std::memory_order_acq_rel);
  100. }
  101. T FetchOr(T operand) {
  102. return RelaxedAtomic<T>::v_.fetch_or(operand, std::memory_order_acq_rel);
  103. }
  104. T FetchXor(T operand) {
  105. return RelaxedAtomic<T>::v_.fetch_xor(operand, std::memory_order_acq_rel);
  106. }
  107. };
  108. } // namespace ROCKSDB_NAMESPACE