| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| #ifndef ABSL_BASE_CALL_ONCE_H_ |
| #define ABSL_BASE_CALL_ONCE_H_ |
|
|
| #include <algorithm> |
| #include <atomic> |
| #include <cstdint> |
| #include <type_traits> |
| #include <utility> |
|
|
| #include "absl/base/internal/invoke.h" |
| #include "absl/base/internal/low_level_scheduling.h" |
| #include "absl/base/internal/raw_logging.h" |
| #include "absl/base/internal/scheduling_mode.h" |
| #include "absl/base/internal/spinlock_wait.h" |
| #include "absl/base/macros.h" |
| #include "absl/base/nullability.h" |
| #include "absl/base/optimization.h" |
| #include "absl/base/port.h" |
|
|
| namespace absl { |
| ABSL_NAMESPACE_BEGIN |
|
|
| class once_flag; |
|
|
| namespace base_internal { |
| absl::Nonnull<std::atomic<uint32_t>*> ControlWord( |
| absl::Nonnull<absl::once_flag*> flag); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| template <typename Callable, typename... Args> |
| void call_once(absl::once_flag& flag, Callable&& fn, Args&&... args); |
|
|
| |
| |
| |
| |
| |
| |
| class once_flag { |
| public: |
| constexpr once_flag() : control_(0) {} |
| once_flag(const once_flag&) = delete; |
| once_flag& operator=(const once_flag&) = delete; |
|
|
| private: |
| friend absl::Nonnull<std::atomic<uint32_t>*> base_internal::ControlWord( |
| absl::Nonnull<once_flag*> flag); |
| std::atomic<uint32_t> control_; |
| }; |
|
|
| |
| |
| |
| |
|
|
| namespace base_internal { |
|
|
| |
| |
| template <typename Callable, typename... Args> |
| void LowLevelCallOnce(absl::Nonnull<absl::once_flag*> flag, Callable&& fn, |
| Args&&... args); |
|
|
| |
| |
| class SchedulingHelper { |
| public: |
| explicit SchedulingHelper(base_internal::SchedulingMode mode) : mode_(mode) { |
| if (mode_ == base_internal::SCHEDULE_KERNEL_ONLY) { |
| guard_result_ = base_internal::SchedulingGuard::DisableRescheduling(); |
| } |
| } |
|
|
| ~SchedulingHelper() { |
| if (mode_ == base_internal::SCHEDULE_KERNEL_ONLY) { |
| base_internal::SchedulingGuard::EnableRescheduling(guard_result_); |
| } |
| } |
|
|
| private: |
| base_internal::SchedulingMode mode_; |
| bool guard_result_ = false; |
| }; |
|
|
| |
| |
| |
| |
| |
| |
| enum { |
| kOnceInit = 0, |
| kOnceRunning = 0x65C2937B, |
| kOnceWaiter = 0x05A308D2, |
| |
| |
| |
| kOnceDone = 221, |
| }; |
|
|
| template <typename Callable, typename... Args> |
| ABSL_ATTRIBUTE_NOINLINE void CallOnceImpl( |
| absl::Nonnull<std::atomic<uint32_t>*> control, |
| base_internal::SchedulingMode scheduling_mode, Callable&& fn, |
| Args&&... args) { |
| #ifndef NDEBUG |
| { |
| uint32_t old_control = control->load(std::memory_order_relaxed); |
| if (old_control != kOnceInit && |
| old_control != kOnceRunning && |
| old_control != kOnceWaiter && |
| old_control != kOnceDone) { |
| ABSL_RAW_LOG(FATAL, "Unexpected value for control word: 0x%lx", |
| static_cast<unsigned long>(old_control)); |
| } |
| } |
| #endif |
| static const base_internal::SpinLockWaitTransition trans[] = { |
| {kOnceInit, kOnceRunning, true}, |
| {kOnceRunning, kOnceWaiter, false}, |
| {kOnceDone, kOnceDone, true}}; |
|
|
| |
| base_internal::SchedulingHelper maybe_disable_scheduling(scheduling_mode); |
| |
| |
| |
| |
| uint32_t old_control = kOnceInit; |
| if (control->compare_exchange_strong(old_control, kOnceRunning, |
| std::memory_order_relaxed) || |
| base_internal::SpinLockWait(control, ABSL_ARRAYSIZE(trans), trans, |
| scheduling_mode) == kOnceInit) { |
| base_internal::invoke(std::forward<Callable>(fn), |
| std::forward<Args>(args)...); |
| old_control = |
| control->exchange(base_internal::kOnceDone, std::memory_order_release); |
| if (old_control == base_internal::kOnceWaiter) { |
| base_internal::SpinLockWake(control, true); |
| } |
| } |
| } |
|
|
| inline absl::Nonnull<std::atomic<uint32_t>*> ControlWord( |
| absl::Nonnull<once_flag*> flag) { |
| return &flag->control_; |
| } |
|
|
| template <typename Callable, typename... Args> |
| void LowLevelCallOnce(absl::Nonnull<absl::once_flag*> flag, Callable&& fn, |
| Args&&... args) { |
| std::atomic<uint32_t>* once = base_internal::ControlWord(flag); |
| uint32_t s = once->load(std::memory_order_acquire); |
| if (ABSL_PREDICT_FALSE(s != base_internal::kOnceDone)) { |
| base_internal::CallOnceImpl(once, base_internal::SCHEDULE_KERNEL_ONLY, |
| std::forward<Callable>(fn), |
| std::forward<Args>(args)...); |
| } |
| } |
|
|
| } |
|
|
| template <typename Callable, typename... Args> |
| void call_once(absl::once_flag& flag, Callable&& fn, Args&&... args) { |
| std::atomic<uint32_t>* once = base_internal::ControlWord(&flag); |
| uint32_t s = once->load(std::memory_order_acquire); |
| if (ABSL_PREDICT_FALSE(s != base_internal::kOnceDone)) { |
| base_internal::CallOnceImpl( |
| once, base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL, |
| std::forward<Callable>(fn), std::forward<Args>(args)...); |
| } |
| } |
|
|
| ABSL_NAMESPACE_END |
| } |
|
|
| #endif |
|
|