Warning, file /include/absl/base/call_once.h was not indexed
or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025 #ifndef ABSL_BASE_CALL_ONCE_H_
0026 #define ABSL_BASE_CALL_ONCE_H_
0027
0028 #include <algorithm>
0029 #include <atomic>
0030 #include <cstdint>
0031 #include <type_traits>
0032 #include <utility>
0033
0034 #include "absl/base/internal/invoke.h"
0035 #include "absl/base/internal/low_level_scheduling.h"
0036 #include "absl/base/internal/raw_logging.h"
0037 #include "absl/base/internal/scheduling_mode.h"
0038 #include "absl/base/internal/spinlock_wait.h"
0039 #include "absl/base/macros.h"
0040 #include "absl/base/nullability.h"
0041 #include "absl/base/optimization.h"
0042 #include "absl/base/port.h"
0043
0044 namespace absl {
0045 ABSL_NAMESPACE_BEGIN
0046
0047 class once_flag;
0048
0049 namespace base_internal {
0050 absl::Nonnull<std::atomic<uint32_t>*> ControlWord(
0051 absl::Nonnull<absl::once_flag*> flag);
0052 }
0053
0054
0055
0056
0057
0058
0059
0060
0061
0062
0063
0064
0065
0066
0067
0068
0069
0070
0071
0072
0073
0074
0075
0076
0077
0078 template <typename Callable, typename... Args>
0079 void call_once(absl::once_flag& flag, Callable&& fn, Args&&... args);
0080
0081
0082
0083
0084
0085
0086
0087 class once_flag {
0088 public:
0089 constexpr once_flag() : control_(0) {}
0090 once_flag(const once_flag&) = delete;
0091 once_flag& operator=(const once_flag&) = delete;
0092
0093 private:
0094 friend absl::Nonnull<std::atomic<uint32_t>*> base_internal::ControlWord(
0095 absl::Nonnull<once_flag*> flag);
0096 std::atomic<uint32_t> control_;
0097 };
0098
0099
0100
0101
0102
0103
0104 namespace base_internal {
0105
0106
0107
0108 template <typename Callable, typename... Args>
0109 void LowLevelCallOnce(absl::Nonnull<absl::once_flag*> flag, Callable&& fn,
0110 Args&&... args);
0111
0112
0113
0114 class SchedulingHelper {
0115 public:
0116 explicit SchedulingHelper(base_internal::SchedulingMode mode) : mode_(mode) {
0117 if (mode_ == base_internal::SCHEDULE_KERNEL_ONLY) {
0118 guard_result_ = base_internal::SchedulingGuard::DisableRescheduling();
0119 }
0120 }
0121
0122 ~SchedulingHelper() {
0123 if (mode_ == base_internal::SCHEDULE_KERNEL_ONLY) {
0124 base_internal::SchedulingGuard::EnableRescheduling(guard_result_);
0125 }
0126 }
0127
0128 private:
0129 base_internal::SchedulingMode mode_;
0130 bool guard_result_ = false;
0131 };
0132
0133
0134
0135
0136
0137
0138
0139 enum {
0140 kOnceInit = 0,
0141 kOnceRunning = 0x65C2937B,
0142 kOnceWaiter = 0x05A308D2,
0143
0144
0145
0146 kOnceDone = 221,
0147 };
0148
0149 template <typename Callable, typename... Args>
0150 ABSL_ATTRIBUTE_NOINLINE void CallOnceImpl(
0151 absl::Nonnull<std::atomic<uint32_t>*> control,
0152 base_internal::SchedulingMode scheduling_mode, Callable&& fn,
0153 Args&&... args) {
0154 #ifndef NDEBUG
0155 {
0156 uint32_t old_control = control->load(std::memory_order_relaxed);
0157 if (old_control != kOnceInit &&
0158 old_control != kOnceRunning &&
0159 old_control != kOnceWaiter &&
0160 old_control != kOnceDone) {
0161 ABSL_RAW_LOG(FATAL, "Unexpected value for control word: 0x%lx",
0162 static_cast<unsigned long>(old_control));
0163 }
0164 }
0165 #endif
0166 static const base_internal::SpinLockWaitTransition trans[] = {
0167 {kOnceInit, kOnceRunning, true},
0168 {kOnceRunning, kOnceWaiter, false},
0169 {kOnceDone, kOnceDone, true}};
0170
0171
0172 base_internal::SchedulingHelper maybe_disable_scheduling(scheduling_mode);
0173
0174
0175
0176
0177 uint32_t old_control = kOnceInit;
0178 if (control->compare_exchange_strong(old_control, kOnceRunning,
0179 std::memory_order_relaxed) ||
0180 base_internal::SpinLockWait(control, ABSL_ARRAYSIZE(trans), trans,
0181 scheduling_mode) == kOnceInit) {
0182 base_internal::invoke(std::forward<Callable>(fn),
0183 std::forward<Args>(args)...);
0184 old_control =
0185 control->exchange(base_internal::kOnceDone, std::memory_order_release);
0186 if (old_control == base_internal::kOnceWaiter) {
0187 base_internal::SpinLockWake(control, true);
0188 }
0189 }
0190 }
0191
0192 inline absl::Nonnull<std::atomic<uint32_t>*> ControlWord(
0193 absl::Nonnull<once_flag*> flag) {
0194 return &flag->control_;
0195 }
0196
0197 template <typename Callable, typename... Args>
0198 void LowLevelCallOnce(absl::Nonnull<absl::once_flag*> flag, Callable&& fn,
0199 Args&&... args) {
0200 std::atomic<uint32_t>* once = base_internal::ControlWord(flag);
0201 uint32_t s = once->load(std::memory_order_acquire);
0202 if (ABSL_PREDICT_FALSE(s != base_internal::kOnceDone)) {
0203 base_internal::CallOnceImpl(once, base_internal::SCHEDULE_KERNEL_ONLY,
0204 std::forward<Callable>(fn),
0205 std::forward<Args>(args)...);
0206 }
0207 }
0208
0209 }
0210
0211 template <typename Callable, typename... Args>
0212 void call_once(absl::once_flag& flag, Callable&& fn, Args&&... args) {
0213 std::atomic<uint32_t>* once = base_internal::ControlWord(&flag);
0214 uint32_t s = once->load(std::memory_order_acquire);
0215 if (ABSL_PREDICT_FALSE(s != base_internal::kOnceDone)) {
0216 base_internal::CallOnceImpl(
0217 once, base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL,
0218 std::forward<Callable>(fn), std::forward<Args>(args)...);
0219 }
0220 }
0221
0222 ABSL_NAMESPACE_END
0223 }
0224
0225 #endif