00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025 #ifndef ABSL_BASE_CALL_ONCE_H_
00026 #define ABSL_BASE_CALL_ONCE_H_
00027
00028 #include <algorithm>
00029 #include <atomic>
00030 #include <cstdint>
00031 #include <type_traits>
00032 #include <utility>
00033
00034 #include "absl/base/internal/invoke.h"
00035 #include "absl/base/internal/low_level_scheduling.h"
00036 #include "absl/base/internal/raw_logging.h"
00037 #include "absl/base/internal/scheduling_mode.h"
00038 #include "absl/base/internal/spinlock_wait.h"
00039 #include "absl/base/macros.h"
00040 #include "absl/base/optimization.h"
00041 #include "absl/base/port.h"
00042
00043 namespace absl {
00044
00045 class once_flag;
00046
00047 namespace base_internal {
00048 std::atomic<uint32_t>* ControlWord(absl::once_flag* flag);
00049 }
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075 template <typename Callable, typename... Args>
00076 void call_once(absl::once_flag& flag, Callable&& fn, Args&&... args);
00077
00078
00079
00080
00081
00082
00083
00084 class once_flag {
00085 public:
00086 constexpr once_flag() : control_(0) {}
00087 once_flag(const once_flag&) = delete;
00088 once_flag& operator=(const once_flag&) = delete;
00089
00090 private:
00091 friend std::atomic<uint32_t>* base_internal::ControlWord(once_flag* flag);
00092 std::atomic<uint32_t> control_;
00093 };
00094
00095
00096
00097
00098
00099
00100 namespace base_internal {
00101
00102
00103
00104 template <typename Callable, typename... Args>
00105 void LowLevelCallOnce(absl::once_flag* flag, Callable&& fn, Args&&... args);
00106
00107
00108
00109 class SchedulingHelper {
00110 public:
00111 explicit SchedulingHelper(base_internal::SchedulingMode mode) : mode_(mode) {
00112 if (mode_ == base_internal::SCHEDULE_KERNEL_ONLY) {
00113 guard_result_ = base_internal::SchedulingGuard::DisableRescheduling();
00114 }
00115 }
00116
00117 ~SchedulingHelper() {
00118 if (mode_ == base_internal::SCHEDULE_KERNEL_ONLY) {
00119 base_internal::SchedulingGuard::EnableRescheduling(guard_result_);
00120 }
00121 }
00122
00123 private:
00124 base_internal::SchedulingMode mode_;
00125 bool guard_result_;
00126 };
00127
00128
00129
00130
00131
00132
00133
00134 enum {
00135 kOnceInit = 0,
00136 kOnceRunning = 0x65C2937B,
00137 kOnceWaiter = 0x05A308D2,
00138
00139
00140
00141 kOnceDone = 221,
00142 };
00143
00144 template <typename Callable, typename... Args>
00145 void CallOnceImpl(std::atomic<uint32_t>* control,
00146 base_internal::SchedulingMode scheduling_mode, Callable&& fn,
00147 Args&&... args) {
00148 #ifndef NDEBUG
00149 {
00150 uint32_t old_control = control->load(std::memory_order_acquire);
00151 if (old_control != kOnceInit &&
00152 old_control != kOnceRunning &&
00153 old_control != kOnceWaiter &&
00154 old_control != kOnceDone) {
00155 ABSL_RAW_LOG(FATAL, "Unexpected value for control word: 0x%lx",
00156 static_cast<unsigned long>(old_control));
00157 }
00158 }
00159 #endif // NDEBUG
00160 static const base_internal::SpinLockWaitTransition trans[] = {
00161 {kOnceInit, kOnceRunning, true},
00162 {kOnceRunning, kOnceWaiter, false},
00163 {kOnceDone, kOnceDone, true}};
00164
00165
00166 base_internal::SchedulingHelper maybe_disable_scheduling(scheduling_mode);
00167
00168 uint32_t old_control = kOnceInit;
00169 if (control->compare_exchange_strong(old_control, kOnceRunning,
00170 std::memory_order_acquire,
00171 std::memory_order_relaxed) ||
00172 base_internal::SpinLockWait(control, ABSL_ARRAYSIZE(trans), trans,
00173 scheduling_mode) == kOnceInit) {
00174 base_internal::Invoke(std::forward<Callable>(fn),
00175 std::forward<Args>(args)...);
00176 old_control = control->load(std::memory_order_relaxed);
00177 control->store(base_internal::kOnceDone, std::memory_order_release);
00178 if (old_control == base_internal::kOnceWaiter) {
00179 base_internal::SpinLockWake(control, true);
00180 }
00181 }
00182 }
00183
00184 inline std::atomic<uint32_t>* ControlWord(once_flag* flag) {
00185 return &flag->control_;
00186 }
00187
00188 template <typename Callable, typename... Args>
00189 void LowLevelCallOnce(absl::once_flag* flag, Callable&& fn, Args&&... args) {
00190 std::atomic<uint32_t>* once = base_internal::ControlWord(flag);
00191 uint32_t s = once->load(std::memory_order_acquire);
00192 if (ABSL_PREDICT_FALSE(s != base_internal::kOnceDone)) {
00193 base_internal::CallOnceImpl(once, base_internal::SCHEDULE_KERNEL_ONLY,
00194 std::forward<Callable>(fn),
00195 std::forward<Args>(args)...);
00196 }
00197 }
00198
00199 }
00200
00201 template <typename Callable, typename... Args>
00202 void call_once(absl::once_flag& flag, Callable&& fn, Args&&... args) {
00203 std::atomic<uint32_t>* once = base_internal::ControlWord(&flag);
00204 uint32_t s = once->load(std::memory_order_acquire);
00205 if (ABSL_PREDICT_FALSE(s != base_internal::kOnceDone)) {
00206 base_internal::CallOnceImpl(
00207 once, base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL,
00208 std::forward<Callable>(fn), std::forward<Args>(args)...);
00209 }
00210 }
00211
00212 }
00213
00214 #endif // ABSL_BASE_CALL_ONCE_H_