7b46e1d31a
-- 07575526242a8e1275ac4223a3d2822795f46569 by CJ Johnson <johnsoncj@google.com>: Comment cleanup on InlinedVector PiperOrigin-RevId: 221322176 -- 49a5e643f85e34d53c41f5e6cc33357c55c9115d by Matt Kulukundis <kfm@google.com>: Internal cleanup PiperOrigin-RevId: 221309185 -- bb35be87ec9c74244b7d902e7e7d2d33ab139d76 by Abseil Team <absl-team@google.com>: Fix typo in comment. PiperOrigin-RevId: 221145354 -- afd4d7c106919708004e06aeea068a57c28aec44 by Derek Mauro <dmauro@google.com>: Update the debugging log message in CallOnceImpl() PiperOrigin-RevId: 221103254 -- 0b9dace8b88113777bf26a6d38f9bc0bcaf053a1 by Abseil Team <absl-team@google.com>: Workaround an MSVC 2015 bug in compile-time initialization. PiperOrigin-RevId: 220871483 -- ea0a3854511ed26beab827e5a5113766b334db86 by Marek Gilbert <mcg@google.com>: Fix ABSL_HAVE_THREAD_LOCAL when compiling for iOS 8 with Xcode 10. Xcode 10 has moved the check for thread_local to a link time, so clang reports __has_feature(cxx_thread_local) but then linking fails with messages like this: ld: targeted OS version does not support use of thread local variables PiperOrigin-RevId: 220815885 -- 485b6876c158c3dcf37eb32d7e512242d5d4ecc6 by Greg Falcon <gfalcon@google.com>: Make the absl::c_set_xxxx() algorithms refuse to compile when passed an unordered collection from std:: or absl::. These algorithms operate on sorted sequences; passing an unordered container to them is nearly certainly a bug. This change is technically an API break, but it only breaks incorrect code. We could try to be more clever and detect unordered collections from other libraries, but false positives will break legal code, and this would constitute an API break Abseil cannot afford. PiperOrigin-RevId: 220794190 -- c47cff7f9cc70a4c1604eee0131af552f40e46d6 by Jon Cohen <cohenjon@google.com>: MSVC 2017's STL throws a Structured Exception (not a C++ exception, essentially equivalent to SIGSEGV) when variant::emplace calls a throwing constructor when using the debug multithreaded MSVC runtime DLL. This manifests in dbg mode in Bazel builds. Disable tests which trigger this bug. It's impossible to specifically pull out MSVC 2017 -dbg modes because there's no way for Bazel to know when version of MSVC is being used -- you tell Bazel the directory where the MSVC tools live, not which version of MSVC tools to use. Thus the best we can do is switch on _DEBUG, which is set whenever the debug runtime is selected with the /MDd build flag, as in Bazel -dbg modes. See https://msdn.microsoft.com/en-us/library/b0084kay.aspx ctrl-f "_DEBUG" PiperOrigin-RevId: 220706161 -- 43993d4af309d92f4ebff38391dcc245f154ecc7 by Shaindel Schwartz <shaindel@google.com>: Internal change PiperOrigin-RevId: 220688429 -- 2448802972dcc261af153af464f2b022ef54a2a9 by Abseil Team <absl-team@google.com>: Speed up operator* for uint128 in WIN64. PiperOrigin-RevId: 220678790 -- 7b376403dd05ba10152fb52e40b29d8af79b58bb by Abseil Team <absl-team@google.com>: Import of CCTZ from GitHub. PiperOrigin-RevId: 220654834 -- ae08af58111c3f838b8d4de25f501c3559c86002 by Abseil Team <absl-team@google.com>: CMake: Add absl_cc_test function PiperOrigin-RevId: 220603940 GitOrigin-RevId: 07575526242a8e1275ac4223a3d2822795f46569 Change-Id: Iba7f53eb394c8a9de564582a976793f9bb0596d9
212 lines
7.7 KiB
C++
212 lines
7.7 KiB
C++
// Copyright 2017 The Abseil Authors.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
//
|
|
// -----------------------------------------------------------------------------
|
|
// File: call_once.h
|
|
// -----------------------------------------------------------------------------
|
|
//
|
|
// This header file provides an Abseil version of `std::call_once` for invoking
|
|
// a given function at most once, across all threads. This Abseil version is
|
|
// faster than the C++11 version and incorporates the C++17 argument-passing
|
|
// fix, so that (for example) non-const references may be passed to the invoked
|
|
// function.
|
|
|
|
#ifndef ABSL_BASE_CALL_ONCE_H_
|
|
#define ABSL_BASE_CALL_ONCE_H_
|
|
|
|
#include <algorithm>
|
|
#include <atomic>
|
|
#include <cstdint>
|
|
#include <type_traits>
|
|
|
|
#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/port.h"
|
|
|
|
namespace absl {
|
|
|
|
class once_flag;
|
|
|
|
namespace base_internal {
|
|
std::atomic<uint32_t>* ControlWord(absl::once_flag* flag);
|
|
} // namespace base_internal
|
|
|
|
// call_once()
|
|
//
|
|
// For all invocations using a given `once_flag`, invokes a given `fn` exactly
|
|
// once across all threads. The first call to `call_once()` with a particular
|
|
// `once_flag` argument (that does not throw an exception) will run the
|
|
// specified function with the provided `args`; other calls with the same
|
|
// `once_flag` argument will not run the function, but will wait
|
|
// for the provided function to finish running (if it is still running).
|
|
//
|
|
// This mechanism provides a safe, simple, and fast mechanism for one-time
|
|
// initialization in a multi-threaded process.
|
|
//
|
|
// Example:
|
|
//
|
|
// class MyInitClass {
|
|
// public:
|
|
// ...
|
|
// mutable absl::once_flag once_;
|
|
//
|
|
// MyInitClass* init() const {
|
|
// absl::call_once(once_, &MyInitClass::Init, this);
|
|
// return ptr_;
|
|
// }
|
|
//
|
|
template <typename Callable, typename... Args>
|
|
void call_once(absl::once_flag& flag, Callable&& fn, Args&&... args);
|
|
|
|
// once_flag
|
|
//
|
|
// Objects of this type are used to distinguish calls to `call_once()` and
|
|
// ensure the provided function is only invoked once across all threads. This
|
|
// type is not copyable or movable. However, it has a `constexpr`
|
|
// constructor, and is safe to use as a namespace-scoped global variable.
|
|
class once_flag {
|
|
public:
|
|
constexpr once_flag() : control_(0) {}
|
|
once_flag(const once_flag&) = delete;
|
|
once_flag& operator=(const once_flag&) = delete;
|
|
|
|
private:
|
|
friend std::atomic<uint32_t>* base_internal::ControlWord(once_flag* flag);
|
|
std::atomic<uint32_t> control_;
|
|
};
|
|
|
|
//------------------------------------------------------------------------------
|
|
// End of public interfaces.
|
|
// Implementation details follow.
|
|
//------------------------------------------------------------------------------
|
|
|
|
namespace base_internal {
|
|
|
|
// Like call_once, but uses KERNEL_ONLY scheduling. Intended to be used to
|
|
// initialize entities used by the scheduler implementation.
|
|
template <typename Callable, typename... Args>
|
|
void LowLevelCallOnce(absl::once_flag* flag, Callable&& fn, Args&&... args);
|
|
|
|
// Disables scheduling while on stack when scheduling mode is non-cooperative.
|
|
// No effect for cooperative scheduling modes.
|
|
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_;
|
|
};
|
|
|
|
// Bit patterns for call_once state machine values. Internal implementation
|
|
// detail, not for use by clients.
|
|
//
|
|
// The bit patterns are arbitrarily chosen from unlikely values, to aid in
|
|
// debugging. However, kOnceInit must be 0, so that a zero-initialized
|
|
// once_flag will be valid for immediate use.
|
|
enum {
|
|
kOnceInit = 0,
|
|
kOnceRunning = 0x65C2937B,
|
|
kOnceWaiter = 0x05A308D2,
|
|
// A very small constant is chosen for kOnceDone so that it fit in a single
|
|
// compare with immediate instruction for most common ISAs. This is verified
|
|
// for x86, POWER and ARM.
|
|
kOnceDone = 221, // Random Number
|
|
};
|
|
|
|
template <typename Callable, typename... Args>
|
|
void CallOnceImpl(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_acquire);
|
|
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)); // NOLINT
|
|
}
|
|
}
|
|
#endif // NDEBUG
|
|
static const base_internal::SpinLockWaitTransition trans[] = {
|
|
{kOnceInit, kOnceRunning, true},
|
|
{kOnceRunning, kOnceWaiter, false},
|
|
{kOnceDone, kOnceDone, true}};
|
|
|
|
// Must do this before potentially modifying control word's state.
|
|
base_internal::SchedulingHelper maybe_disable_scheduling(scheduling_mode);
|
|
// Short circuit the simplest case to avoid procedure call overhead.
|
|
uint32_t old_control = kOnceInit;
|
|
if (control->compare_exchange_strong(old_control, kOnceRunning,
|
|
std::memory_order_acquire,
|
|
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->load(std::memory_order_relaxed);
|
|
control->store(base_internal::kOnceDone, std::memory_order_release);
|
|
if (old_control == base_internal::kOnceWaiter) {
|
|
base_internal::SpinLockWake(control, true);
|
|
}
|
|
} // else *control is already kOnceDone
|
|
}
|
|
|
|
inline std::atomic<uint32_t>* ControlWord(once_flag* flag) {
|
|
return &flag->control_;
|
|
}
|
|
|
|
template <typename Callable, typename... Args>
|
|
void LowLevelCallOnce(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)...);
|
|
}
|
|
}
|
|
|
|
} // namespace base_internal
|
|
|
|
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)...);
|
|
}
|
|
}
|
|
|
|
} // namespace absl
|
|
|
|
#endif // ABSL_BASE_CALL_ONCE_H_
|