/src/abseil-cpp/absl/base/call_once.h
Line  | Count  | Source  | 
1  |  | // Copyright 2017 The Abseil Authors.  | 
2  |  | //  | 
3  |  | // Licensed under the Apache License, Version 2.0 (the "License");  | 
4  |  | // you may not use this file except in compliance with the License.  | 
5  |  | // You may obtain a copy of the License at  | 
6  |  | //  | 
7  |  | //      https://www.apache.org/licenses/LICENSE-2.0  | 
8  |  | //  | 
9  |  | // Unless required by applicable law or agreed to in writing, software  | 
10  |  | // distributed under the License is distributed on an "AS IS" BASIS,  | 
11  |  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  | 
12  |  | // See the License for the specific language governing permissions and  | 
13  |  | // limitations under the License.  | 
14  |  | //  | 
15  |  | // -----------------------------------------------------------------------------  | 
16  |  | // File: call_once.h  | 
17  |  | // -----------------------------------------------------------------------------  | 
18  |  | //  | 
19  |  | // This header file provides an Abseil version of `std::call_once` for invoking  | 
20  |  | // a given function at most once, across all threads. This Abseil version is  | 
21  |  | // faster than the C++11 version and incorporates the C++17 argument-passing  | 
22  |  | // fix, so that (for example) non-const references may be passed to the invoked  | 
23  |  | // function.  | 
24  |  |  | 
25  |  | #ifndef ABSL_BASE_CALL_ONCE_H_  | 
26  |  | #define ABSL_BASE_CALL_ONCE_H_  | 
27  |  |  | 
28  |  | #include <algorithm>  | 
29  |  | #include <atomic>  | 
30  |  | #include <cstdint>  | 
31  |  | #include <functional>  | 
32  |  | #include <type_traits>  | 
33  |  | #include <utility>  | 
34  |  |  | 
35  |  | #include "absl/base/attributes.h"  | 
36  |  | #include "absl/base/config.h"  | 
37  |  | #include "absl/base/internal/low_level_scheduling.h"  | 
38  |  | #include "absl/base/internal/raw_logging.h"  | 
39  |  | #include "absl/base/internal/scheduling_mode.h"  | 
40  |  | #include "absl/base/internal/spinlock_wait.h"  | 
41  |  | #include "absl/base/macros.h"  | 
42  |  | #include "absl/base/nullability.h"  | 
43  |  | #include "absl/base/optimization.h"  | 
44  |  | #include "absl/base/port.h"  | 
45  |  |  | 
46  |  | namespace absl { | 
47  |  | ABSL_NAMESPACE_BEGIN  | 
48  |  |  | 
49  |  | class once_flag;  | 
50  |  |  | 
51  |  | namespace base_internal { | 
52  |  | std::atomic<uint32_t>* absl_nonnull ControlWord(  | 
53  |  |     absl::once_flag* absl_nonnull flag);  | 
54  |  | }  // namespace base_internal  | 
55  |  |  | 
56  |  | // call_once()  | 
57  |  | //  | 
58  |  | // For all invocations using a given `once_flag`, invokes a given `fn` exactly  | 
59  |  | // once across all threads. The first call to `call_once()` with a particular  | 
60  |  | // `once_flag` argument (that does not throw an exception) will run the  | 
61  |  | // specified function with the provided `args`; other calls with the same  | 
62  |  | // `once_flag` argument will not run the function, but will wait  | 
63  |  | // for the provided function to finish running (if it is still running).  | 
64  |  | //  | 
65  |  | // This mechanism provides a safe, simple, and fast mechanism for one-time  | 
66  |  | // initialization in a multi-threaded process.  | 
67  |  | //  | 
68  |  | // Example:  | 
69  |  | //  | 
70  |  | // class MyInitClass { | 
71  |  | //  public:  | 
72  |  | //  ...  | 
73  |  | //  mutable absl::once_flag once_;  | 
74  |  | //  | 
75  |  | //  MyInitClass* init() const { | 
76  |  | //    absl::call_once(once_, &MyInitClass::Init, this);  | 
77  |  | //    return ptr_;  | 
78  |  | //  }  | 
79  |  | //  | 
80  |  | template <typename Callable, typename... Args>  | 
81  |  | void call_once(absl::once_flag& flag, Callable&& fn, Args&&... args);  | 
82  |  |  | 
83  |  | // once_flag  | 
84  |  | //  | 
85  |  | // Objects of this type are used to distinguish calls to `call_once()` and  | 
86  |  | // ensure the provided function is only invoked once across all threads. This  | 
87  |  | // type is not copyable or movable. However, it has a `constexpr`  | 
88  |  | // constructor, and is safe to use as a namespace-scoped global variable.  | 
89  |  | class once_flag { | 
90  |  |  public:  | 
91  | 0  |   constexpr once_flag() : control_(0) {} | 
92  |  |   once_flag(const once_flag&) = delete;  | 
93  |  |   once_flag& operator=(const once_flag&) = delete;  | 
94  |  |  | 
95  |  |  private:  | 
96  |  |   friend std::atomic<uint32_t>* absl_nonnull base_internal::ControlWord(  | 
97  |  |       once_flag* absl_nonnull flag);  | 
98  |  |   std::atomic<uint32_t> control_;  | 
99  |  | };  | 
100  |  |  | 
101  |  | //------------------------------------------------------------------------------  | 
102  |  | // End of public interfaces.  | 
103  |  | // Implementation details follow.  | 
104  |  | //------------------------------------------------------------------------------  | 
105  |  |  | 
106  |  | namespace base_internal { | 
107  |  |  | 
108  |  | // Like call_once, but uses KERNEL_ONLY scheduling. Intended to be used to  | 
109  |  | // initialize entities used by the scheduler implementation.  | 
110  |  | template <typename Callable, typename... Args>  | 
111  |  | void LowLevelCallOnce(absl::once_flag* absl_nonnull flag, Callable&& fn,  | 
112  |  |                       Args&&... args);  | 
113  |  |  | 
114  |  | // Disables scheduling while on stack when scheduling mode is non-cooperative.  | 
115  |  | // No effect for cooperative scheduling modes.  | 
116  |  | class SchedulingHelper { | 
117  |  |  public:  | 
118  | 6  |   explicit SchedulingHelper(base_internal::SchedulingMode mode) : mode_(mode) { | 
119  | 6  |     if (mode_ == base_internal::SCHEDULE_KERNEL_ONLY) { | 
120  | 2  |       guard_result_ = base_internal::SchedulingGuard::DisableRescheduling();  | 
121  | 2  |     }  | 
122  | 6  |   }  | 
123  |  |  | 
124  | 6  |   ~SchedulingHelper() { | 
125  | 6  |     if (mode_ == base_internal::SCHEDULE_KERNEL_ONLY) { | 
126  | 2  |       base_internal::SchedulingGuard::EnableRescheduling(guard_result_);  | 
127  | 2  |     }  | 
128  | 6  |   }  | 
129  |  |  | 
130  |  |  private:  | 
131  |  |   base_internal::SchedulingMode mode_;  | 
132  |  |   bool guard_result_ = false;  | 
133  |  | };  | 
134  |  |  | 
135  |  | // Bit patterns for call_once state machine values.  Internal implementation  | 
136  |  | // detail, not for use by clients.  | 
137  |  | //  | 
138  |  | // The bit patterns are arbitrarily chosen from unlikely values, to aid in  | 
139  |  | // debugging.  However, kOnceInit must be 0, so that a zero-initialized  | 
140  |  | // once_flag will be valid for immediate use.  | 
141  |  | enum { | 
142  |  |   kOnceInit = 0,  | 
143  |  |   kOnceRunning = 0x65C2937B,  | 
144  |  |   kOnceWaiter = 0x05A308D2,  | 
145  |  |   // A very small constant is chosen for kOnceDone so that it fit in a single  | 
146  |  |   // compare with immediate instruction for most common ISAs.  This is verified  | 
147  |  |   // for x86, POWER and ARM.  | 
148  |  |   kOnceDone = 221,    // Random Number  | 
149  |  | };  | 
150  |  |  | 
151  |  | template <typename Callable, typename... Args>  | 
152  |  |     void  | 
153  |  |     CallOnceImpl(std::atomic<uint32_t>* absl_nonnull control,  | 
154  |  |                  base_internal::SchedulingMode scheduling_mode, Callable&& fn,  | 
155  | 6  |                  Args&&... args) { | 
156  | 6  | #ifndef NDEBUG  | 
157  | 6  |   { | 
158  | 6  |     uint32_t old_control = control->load(std::memory_order_relaxed);  | 
159  | 6  |     if (old_control != kOnceInit &&  | 
160  | 0  |         old_control != kOnceRunning &&  | 
161  | 0  |         old_control != kOnceWaiter &&  | 
162  | 0  |         old_control != kOnceDone) { | 
163  | 0  |       ABSL_RAW_LOG(FATAL, "Unexpected value for control word: 0x%lx",  | 
164  | 0  |                    static_cast<unsigned long>(old_control));  // NOLINT  | 
165  | 0  |     }  | 
166  | 6  |   }  | 
167  | 6  | #endif  // NDEBUG  | 
168  | 6  |   static const base_internal::SpinLockWaitTransition trans[] = { | 
169  | 6  |       {kOnceInit, kOnceRunning, true}, | 
170  | 6  |       {kOnceRunning, kOnceWaiter, false}, | 
171  | 6  |       {kOnceDone, kOnceDone, true}}; | 
172  |  |  | 
173  |  |   // Must do this before potentially modifying control word's state.  | 
174  | 6  |   base_internal::SchedulingHelper maybe_disable_scheduling(scheduling_mode);  | 
175  |  |   // Short circuit the simplest case to avoid procedure call overhead.  | 
176  |  |   // The base_internal::SpinLockWait() call returns either kOnceInit or  | 
177  |  |   // kOnceDone. If it returns kOnceDone, it must have loaded the control word  | 
178  |  |   // with std::memory_order_acquire and seen a value of kOnceDone.  | 
179  | 6  |   uint32_t old_control = kOnceInit;  | 
180  | 6  |   if (control->compare_exchange_strong(old_control, kOnceRunning,  | 
181  | 6  |                                        std::memory_order_relaxed) ||  | 
182  | 0  |       base_internal::SpinLockWait(control, ABSL_ARRAYSIZE(trans), trans,  | 
183  | 6  |                                   scheduling_mode) == kOnceInit) { | 
184  | 6  |     std::invoke(std::forward<Callable>(fn), std::forward<Args>(args)...);  | 
185  | 6  |     old_control =  | 
186  | 6  |         control->exchange(base_internal::kOnceDone, std::memory_order_release);  | 
187  | 6  |     if (old_control == base_internal::kOnceWaiter) { | 
188  | 0  |       base_internal::SpinLockWake(control, true);  | 
189  | 0  |     }  | 
190  | 6  |   }  // else *control is already kOnceDone  | 
191  | 6  | } void absl::base_internal::CallOnceImpl<void (absl::flags_internal::FlagImpl::*)(), absl::flags_internal::FlagImpl*>(std::__1::atomic<unsigned int>*, absl::base_internal::SchedulingMode, void (absl::flags_internal::FlagImpl::*&&)(), absl::flags_internal::FlagImpl*&&) Line  | Count  | Source  |  155  | 1  |                  Args&&... args) { |  156  | 1  | #ifndef NDEBUG  |  157  | 1  |   { |  158  | 1  |     uint32_t old_control = control->load(std::memory_order_relaxed);  |  159  | 1  |     if (old_control != kOnceInit &&  |  160  | 0  |         old_control != kOnceRunning &&  |  161  | 0  |         old_control != kOnceWaiter &&  |  162  | 0  |         old_control != kOnceDone) { |  163  | 0  |       ABSL_RAW_LOG(FATAL, "Unexpected value for control word: 0x%lx",  |  164  | 0  |                    static_cast<unsigned long>(old_control));  // NOLINT  |  165  | 0  |     }  |  166  | 1  |   }  |  167  | 1  | #endif  // NDEBUG  |  168  | 1  |   static const base_internal::SpinLockWaitTransition trans[] = { |  169  | 1  |       {kOnceInit, kOnceRunning, true}, |  170  | 1  |       {kOnceRunning, kOnceWaiter, false}, |  171  | 1  |       {kOnceDone, kOnceDone, true}}; |  172  |  |  |  173  |  |   // Must do this before potentially modifying control word's state.  |  174  | 1  |   base_internal::SchedulingHelper maybe_disable_scheduling(scheduling_mode);  |  175  |  |   // Short circuit the simplest case to avoid procedure call overhead.  |  176  |  |   // The base_internal::SpinLockWait() call returns either kOnceInit or  |  177  |  |   // kOnceDone. If it returns kOnceDone, it must have loaded the control word  |  178  |  |   // with std::memory_order_acquire and seen a value of kOnceDone.  |  179  | 1  |   uint32_t old_control = kOnceInit;  |  180  | 1  |   if (control->compare_exchange_strong(old_control, kOnceRunning,  |  181  | 1  |                                        std::memory_order_relaxed) ||  |  182  | 0  |       base_internal::SpinLockWait(control, ABSL_ARRAYSIZE(trans), trans,  |  183  | 1  |                                   scheduling_mode) == kOnceInit) { |  184  | 1  |     std::invoke(std::forward<Callable>(fn), std::forward<Args>(args)...);  |  185  | 1  |     old_control =  |  186  | 1  |         control->exchange(base_internal::kOnceDone, std::memory_order_release);  |  187  | 1  |     if (old_control == base_internal::kOnceWaiter) { |  188  | 0  |       base_internal::SpinLockWake(control, true);  |  189  | 0  |     }  |  190  | 1  |   }  // else *control is already kOnceDone  |  191  | 1  | }  |  
 log_sink_set.cc:void absl::base_internal::CallOnceImpl<absl::log_internal::(anonymous namespace)::StderrLogSink::Send(absl::LogEntry const&)::{lambda()#1}>(std::__1::atomic<unsigned int>*, absl::base_internal::SchedulingMode, absl::log_internal::(anonymous namespace)::StderrLogSink::Send(absl::LogEntry const&)::{lambda()#1}&&)Line  | Count  | Source  |  155  | 1  |                  Args&&... args) { |  156  | 1  | #ifndef NDEBUG  |  157  | 1  |   { |  158  | 1  |     uint32_t old_control = control->load(std::memory_order_relaxed);  |  159  | 1  |     if (old_control != kOnceInit &&  |  160  | 0  |         old_control != kOnceRunning &&  |  161  | 0  |         old_control != kOnceWaiter &&  |  162  | 0  |         old_control != kOnceDone) { |  163  | 0  |       ABSL_RAW_LOG(FATAL, "Unexpected value for control word: 0x%lx",  |  164  | 0  |                    static_cast<unsigned long>(old_control));  // NOLINT  |  165  | 0  |     }  |  166  | 1  |   }  |  167  | 1  | #endif  // NDEBUG  |  168  | 1  |   static const base_internal::SpinLockWaitTransition trans[] = { |  169  | 1  |       {kOnceInit, kOnceRunning, true}, |  170  | 1  |       {kOnceRunning, kOnceWaiter, false}, |  171  | 1  |       {kOnceDone, kOnceDone, true}}; |  172  |  |  |  173  |  |   // Must do this before potentially modifying control word's state.  |  174  | 1  |   base_internal::SchedulingHelper maybe_disable_scheduling(scheduling_mode);  |  175  |  |   // Short circuit the simplest case to avoid procedure call overhead.  |  176  |  |   // The base_internal::SpinLockWait() call returns either kOnceInit or  |  177  |  |   // kOnceDone. If it returns kOnceDone, it must have loaded the control word  |  178  |  |   // with std::memory_order_acquire and seen a value of kOnceDone.  |  179  | 1  |   uint32_t old_control = kOnceInit;  |  180  | 1  |   if (control->compare_exchange_strong(old_control, kOnceRunning,  |  181  | 1  |                                        std::memory_order_relaxed) ||  |  182  | 0  |       base_internal::SpinLockWait(control, ABSL_ARRAYSIZE(trans), trans,  |  183  | 1  |                                   scheduling_mode) == kOnceInit) { |  184  | 1  |     std::invoke(std::forward<Callable>(fn), std::forward<Args>(args)...);  |  185  | 1  |     old_control =  |  186  | 1  |         control->exchange(base_internal::kOnceDone, std::memory_order_release);  |  187  | 1  |     if (old_control == base_internal::kOnceWaiter) { |  188  | 0  |       base_internal::SpinLockWake(control, true);  |  189  | 0  |     }  |  190  | 1  |   }  // else *control is already kOnceDone  |  191  | 1  | }  |  
 Unexecuted instantiation: mutex.cc:void absl::base_internal::CallOnceImpl<absl::(anonymous namespace)::GetMutexGlobals()::$_0>(std::__1::atomic<unsigned int>*, absl::base_internal::SchedulingMode, absl::(anonymous namespace)::GetMutexGlobals()::$_0&&) void absl::base_internal::CallOnceImpl<void (&)()>(std::__1::atomic<unsigned int>*, absl::base_internal::SchedulingMode, void (&)()) Line  | Count  | Source  |  155  | 2  |                  Args&&... args) { |  156  | 2  | #ifndef NDEBUG  |  157  | 2  |   { |  158  | 2  |     uint32_t old_control = control->load(std::memory_order_relaxed);  |  159  | 2  |     if (old_control != kOnceInit &&  |  160  | 0  |         old_control != kOnceRunning &&  |  161  | 0  |         old_control != kOnceWaiter &&  |  162  | 0  |         old_control != kOnceDone) { |  163  | 0  |       ABSL_RAW_LOG(FATAL, "Unexpected value for control word: 0x%lx",  |  164  | 0  |                    static_cast<unsigned long>(old_control));  // NOLINT  |  165  | 0  |     }  |  166  | 2  |   }  |  167  | 2  | #endif  // NDEBUG  |  168  | 2  |   static const base_internal::SpinLockWaitTransition trans[] = { |  169  | 2  |       {kOnceInit, kOnceRunning, true}, |  170  | 2  |       {kOnceRunning, kOnceWaiter, false}, |  171  | 2  |       {kOnceDone, kOnceDone, true}}; |  172  |  |  |  173  |  |   // Must do this before potentially modifying control word's state.  |  174  | 2  |   base_internal::SchedulingHelper maybe_disable_scheduling(scheduling_mode);  |  175  |  |   // Short circuit the simplest case to avoid procedure call overhead.  |  176  |  |   // The base_internal::SpinLockWait() call returns either kOnceInit or  |  177  |  |   // kOnceDone. If it returns kOnceDone, it must have loaded the control word  |  178  |  |   // with std::memory_order_acquire and seen a value of kOnceDone.  |  179  | 2  |   uint32_t old_control = kOnceInit;  |  180  | 2  |   if (control->compare_exchange_strong(old_control, kOnceRunning,  |  181  | 2  |                                        std::memory_order_relaxed) ||  |  182  | 0  |       base_internal::SpinLockWait(control, ABSL_ARRAYSIZE(trans), trans,  |  183  | 2  |                                   scheduling_mode) == kOnceInit) { |  184  | 2  |     std::invoke(std::forward<Callable>(fn), std::forward<Args>(args)...);  |  185  | 2  |     old_control =  |  186  | 2  |         control->exchange(base_internal::kOnceDone, std::memory_order_release);  |  187  | 2  |     if (old_control == base_internal::kOnceWaiter) { |  188  | 0  |       base_internal::SpinLockWake(control, true);  |  189  | 0  |     }  |  190  | 2  |   }  // else *control is already kOnceDone  |  191  | 2  | }  |  
 Unexecuted instantiation: spinlock.cc:void absl::base_internal::CallOnceImpl<absl::base_internal::SpinLock::SpinLoop()::$_0>(std::__1::atomic<unsigned int>*, absl::base_internal::SchedulingMode, absl::base_internal::SpinLock::SpinLoop()::$_0&&) Unexecuted instantiation: sysinfo.cc:void absl::base_internal::CallOnceImpl<absl::base_internal::NumCPUs()::$_0>(std::__1::atomic<unsigned int>*, absl::base_internal::SchedulingMode, absl::base_internal::NumCPUs()::$_0&&) Unexecuted instantiation: sysinfo.cc:void absl::base_internal::CallOnceImpl<absl::base_internal::NominalCPUFrequency()::$_0>(std::__1::atomic<unsigned int>*, absl::base_internal::SchedulingMode, absl::base_internal::NominalCPUFrequency()::$_0&&) void absl::base_internal::CallOnceImpl<void (&)(void (*)(void*)), void (*&)(void*)>(std::__1::atomic<unsigned int>*, absl::base_internal::SchedulingMode, void (&)(void (*)(void*)), void (*&)(void*)) Line  | Count  | Source  |  155  | 2  |                  Args&&... args) { |  156  | 2  | #ifndef NDEBUG  |  157  | 2  |   { |  158  | 2  |     uint32_t old_control = control->load(std::memory_order_relaxed);  |  159  | 2  |     if (old_control != kOnceInit &&  |  160  | 0  |         old_control != kOnceRunning &&  |  161  | 0  |         old_control != kOnceWaiter &&  |  162  | 0  |         old_control != kOnceDone) { |  163  | 0  |       ABSL_RAW_LOG(FATAL, "Unexpected value for control word: 0x%lx",  |  164  | 0  |                    static_cast<unsigned long>(old_control));  // NOLINT  |  165  | 0  |     }  |  166  | 2  |   }  |  167  | 2  | #endif  // NDEBUG  |  168  | 2  |   static const base_internal::SpinLockWaitTransition trans[] = { |  169  | 2  |       {kOnceInit, kOnceRunning, true}, |  170  | 2  |       {kOnceRunning, kOnceWaiter, false}, |  171  | 2  |       {kOnceDone, kOnceDone, true}}; |  172  |  |  |  173  |  |   // Must do this before potentially modifying control word's state.  |  174  | 2  |   base_internal::SchedulingHelper maybe_disable_scheduling(scheduling_mode);  |  175  |  |   // Short circuit the simplest case to avoid procedure call overhead.  |  176  |  |   // The base_internal::SpinLockWait() call returns either kOnceInit or  |  177  |  |   // kOnceDone. If it returns kOnceDone, it must have loaded the control word  |  178  |  |   // with std::memory_order_acquire and seen a value of kOnceDone.  |  179  | 2  |   uint32_t old_control = kOnceInit;  |  180  | 2  |   if (control->compare_exchange_strong(old_control, kOnceRunning,  |  181  | 2  |                                        std::memory_order_relaxed) ||  |  182  | 0  |       base_internal::SpinLockWait(control, ABSL_ARRAYSIZE(trans), trans,  |  183  | 2  |                                   scheduling_mode) == kOnceInit) { |  184  | 2  |     std::invoke(std::forward<Callable>(fn), std::forward<Args>(args)...);  |  185  | 2  |     old_control =  |  186  | 2  |         control->exchange(base_internal::kOnceDone, std::memory_order_release);  |  187  | 2  |     if (old_control == base_internal::kOnceWaiter) { |  188  | 0  |       base_internal::SpinLockWake(control, true);  |  189  | 0  |     }  |  190  | 2  |   }  // else *control is already kOnceDone  |  191  | 2  | }  |  
  | 
192  |  |  | 
193  |  | inline std::atomic<uint32_t>* absl_nonnull ControlWord(  | 
194  | 4.12M  |     once_flag* absl_nonnull flag) { | 
195  | 4.12M  |   return &flag->control_;  | 
196  | 4.12M  | }  | 
197  |  |  | 
198  |  | template <typename Callable, typename... Args>  | 
199  |  | void LowLevelCallOnce(absl::once_flag* absl_nonnull flag, Callable&& fn,  | 
200  | 10  |                       Args&&... args) { | 
201  | 10  |   std::atomic<uint32_t>* once = base_internal::ControlWord(flag);  | 
202  | 10  |   uint32_t s = once->load(std::memory_order_acquire);  | 
203  | 10  |   if (ABSL_PREDICT_FALSE(s != base_internal::kOnceDone)) { | 
204  | 2  |     base_internal::CallOnceImpl(once, base_internal::SCHEDULE_KERNEL_ONLY,  | 
205  | 2  |                                 std::forward<Callable>(fn),  | 
206  | 2  |                                 std::forward<Args>(args)...);  | 
207  | 2  |   }  | 
208  | 10  | } Unexecuted instantiation: mutex.cc:void absl::base_internal::LowLevelCallOnce<absl::(anonymous namespace)::GetMutexGlobals()::$_0>(absl::once_flag*, absl::(anonymous namespace)::GetMutexGlobals()::$_0&&) void absl::base_internal::LowLevelCallOnce<void (&)()>(absl::once_flag*, void (&)()) Line  | Count  | Source  |  200  | 10  |                       Args&&... args) { |  201  | 10  |   std::atomic<uint32_t>* once = base_internal::ControlWord(flag);  |  202  | 10  |   uint32_t s = once->load(std::memory_order_acquire);  |  203  | 10  |   if (ABSL_PREDICT_FALSE(s != base_internal::kOnceDone)) { |  204  | 2  |     base_internal::CallOnceImpl(once, base_internal::SCHEDULE_KERNEL_ONLY,  |  205  | 2  |                                 std::forward<Callable>(fn),  |  206  | 2  |                                 std::forward<Args>(args)...);  |  207  | 2  |   }  |  208  | 10  | }  |  
 Unexecuted instantiation: spinlock.cc:void absl::base_internal::LowLevelCallOnce<absl::base_internal::SpinLock::SpinLoop()::$_0>(absl::once_flag*, absl::base_internal::SpinLock::SpinLoop()::$_0&&) Unexecuted instantiation: sysinfo.cc:void absl::base_internal::LowLevelCallOnce<absl::base_internal::NumCPUs()::$_0>(absl::once_flag*, absl::base_internal::NumCPUs()::$_0&&) Unexecuted instantiation: sysinfo.cc:void absl::base_internal::LowLevelCallOnce<absl::base_internal::NominalCPUFrequency()::$_0>(absl::once_flag*, absl::base_internal::NominalCPUFrequency()::$_0&&)  | 
209  |  |  | 
210  |  | }  // namespace base_internal  | 
211  |  |  | 
212  |  | template <typename Callable, typename... Args>  | 
213  |  |     void  | 
214  | 4.12M  |     call_once(absl::once_flag& flag, Callable&& fn, Args&&... args) { | 
215  | 4.12M  |   std::atomic<uint32_t>* once = base_internal::ControlWord(&flag);  | 
216  | 4.12M  |   uint32_t s = once->load(std::memory_order_acquire);  | 
217  | 4.12M  |   if (ABSL_PREDICT_FALSE(s != base_internal::kOnceDone)) { | 
218  | 4  |     base_internal::CallOnceImpl(  | 
219  | 4  |         once, base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL,  | 
220  | 4  |         std::forward<Callable>(fn), std::forward<Args>(args)...);  | 
221  | 4  |   }  | 
222  | 4.12M  | } void absl::call_once<void (absl::flags_internal::FlagImpl::*)(), absl::flags_internal::FlagImpl*>(absl::once_flag&, void (absl::flags_internal::FlagImpl::*&&)(), absl::flags_internal::FlagImpl*&&) Line  | Count  | Source  |  214  | 1  |     call_once(absl::once_flag& flag, Callable&& fn, Args&&... args) { |  215  | 1  |   std::atomic<uint32_t>* once = base_internal::ControlWord(&flag);  |  216  | 1  |   uint32_t s = once->load(std::memory_order_acquire);  |  217  | 1  |   if (ABSL_PREDICT_FALSE(s != base_internal::kOnceDone)) { |  218  | 1  |     base_internal::CallOnceImpl(  |  219  | 1  |         once, base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL,  |  220  | 1  |         std::forward<Callable>(fn), std::forward<Args>(args)...);  |  221  | 1  |   }  |  222  | 1  | }  |  
 log_sink_set.cc:void absl::call_once<absl::log_internal::(anonymous namespace)::StderrLogSink::Send(absl::LogEntry const&)::{lambda()#1}>(absl::once_flag&, absl::log_internal::(anonymous namespace)::StderrLogSink::Send(absl::LogEntry const&)::{lambda()#1}&&)Line  | Count  | Source  |  214  | 4.12M  |     call_once(absl::once_flag& flag, Callable&& fn, Args&&... args) { |  215  | 4.12M  |   std::atomic<uint32_t>* once = base_internal::ControlWord(&flag);  |  216  | 4.12M  |   uint32_t s = once->load(std::memory_order_acquire);  |  217  | 4.12M  |   if (ABSL_PREDICT_FALSE(s != base_internal::kOnceDone)) { |  218  | 1  |     base_internal::CallOnceImpl(  |  219  | 1  |         once, base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL,  |  220  | 1  |         std::forward<Callable>(fn), std::forward<Args>(args)...);  |  221  | 1  |   }  |  222  | 4.12M  | }  |  
 void absl::call_once<void (&)(void (*)(void*)), void (*&)(void*)>(absl::once_flag&, void (&)(void (*)(void*)), void (*&)(void*)) Line  | Count  | Source  |  214  | 2  |     call_once(absl::once_flag& flag, Callable&& fn, Args&&... args) { |  215  | 2  |   std::atomic<uint32_t>* once = base_internal::ControlWord(&flag);  |  216  | 2  |   uint32_t s = once->load(std::memory_order_acquire);  |  217  | 2  |   if (ABSL_PREDICT_FALSE(s != base_internal::kOnceDone)) { |  218  | 2  |     base_internal::CallOnceImpl(  |  219  | 2  |         once, base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL,  |  220  | 2  |         std::forward<Callable>(fn), std::forward<Args>(args)...);  |  221  | 2  |   }  |  222  | 2  | }  |  
  | 
223  |  |  | 
224  |  | ABSL_NAMESPACE_END  | 
225  |  | }  // namespace absl  | 
226  |  |  | 
227  |  | #endif  // ABSL_BASE_CALL_ONCE_H_  |