diff options
-rw-r--r-- | CONTRIBUTING.md | 9 | ||||
-rw-r--r-- | absl/base/BUILD.bazel | 1 | ||||
-rw-r--r-- | absl/base/CMakeLists.txt | 1 | ||||
-rw-r--r-- | absl/base/log_severity_test.cc | 5 | ||||
-rw-r--r-- | absl/flags/BUILD.bazel | 2 | ||||
-rw-r--r-- | absl/flags/flag.h | 12 | ||||
-rw-r--r-- | absl/flags/flag_test.cc | 6 | ||||
-rw-r--r-- | absl/flags/internal/flag.cc | 47 | ||||
-rw-r--r-- | absl/flags/internal/flag.h | 372 |
9 files changed, 245 insertions, 210 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f4cb4a29..9dadae93 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -123,10 +123,13 @@ will be expected to conform to the style outlined ## Running Tests -Use "bazel test <>" functionality to run the unit tests. +If you have [Bazel](https://bazel.build/) installed, use `bazel test +--test_tag_filters="-benchmark" ...` to run the unit tests. -Prerequisites for building and running tests are listed in -[README.md](README.md) +If you are running the Linux operating system and have +[Docker](https://www.docker.com/) installed, you can also run the `linux_*.sh` +scripts under the `ci/`(https://github.com/abseil/abseil-cpp/tree/master/ci) +directory to test Abseil under a variety of conditions. ## Abseil Committers diff --git a/absl/base/BUILD.bazel b/absl/base/BUILD.bazel index 9814e420..c6948a27 100644 --- a/absl/base/BUILD.bazel +++ b/absl/base/BUILD.bazel @@ -675,6 +675,7 @@ cc_test( linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":log_severity", + "//absl/flags:flag_internal", "//absl/flags:marshalling", "//absl/strings", "@com_google_googletest//:gtest_main", diff --git a/absl/base/CMakeLists.txt b/absl/base/CMakeLists.txt index 3ca985a9..c21571be 100644 --- a/absl/base/CMakeLists.txt +++ b/absl/base/CMakeLists.txt @@ -610,6 +610,7 @@ absl_cc_test( SRCS "log_severity_test.cc" DEPS + absl::flags_internal absl::flags_marshalling absl::log_severity absl::strings diff --git a/absl/base/log_severity_test.cc b/absl/base/log_severity_test.cc index 1e3aafa5..2302aa12 100644 --- a/absl/base/log_severity_test.cc +++ b/absl/base/log_severity_test.cc @@ -24,6 +24,7 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "absl/flags/internal/flag.h" #include "absl/flags/marshalling.h" #include "absl/strings/str_cat.h" @@ -51,6 +52,10 @@ TEST(StreamTest, Works) { Eq("absl::LogSeverity(4)")); } +static_assert( + absl::flags_internal::IsAtomicFlagTypeTrait<absl::LogSeverity>::value, + "Flags of type absl::LogSeverity ought to be lock-free."); + using ParseFlagFromOutOfRangeIntegerTest = TestWithParam<int64_t>; INSTANTIATE_TEST_SUITE_P( Instantiation, ParseFlagFromOutOfRangeIntegerTest, diff --git a/absl/flags/BUILD.bazel b/absl/flags/BUILD.bazel index cbdbae52..03833d44 100644 --- a/absl/flags/BUILD.bazel +++ b/absl/flags/BUILD.bazel @@ -36,7 +36,7 @@ cc_library( ], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, - visibility = ["//visibility:private"], + visibility = ["//absl/base:__subpackages__"], deps = [ ":config", ":handle", diff --git a/absl/flags/flag.h b/absl/flags/flag.h index fcfdd58c..782dee2e 100644 --- a/absl/flags/flag.h +++ b/absl/flags/flag.h @@ -120,11 +120,11 @@ class Flag { return impl_; } - impl_ = new flags_internal::Flag<T>( - name_, filename_, marshalling_op_, - {flags_internal::FlagHelpSrc(help_gen_), - flags_internal::FlagHelpSrcKind::kGenFunc}, - default_value_gen_); + impl_ = + new flags_internal::Flag<T>(name_, filename_, marshalling_op_, + {flags_internal::FlagHelpMsg(help_gen_), + flags_internal::FlagHelpKind::kGenFunc}, + default_value_gen_); inited_.store(true, std::memory_order_release); } @@ -152,7 +152,7 @@ class Flag { T Get() const { return GetImpl()->Get(); } bool AtomicGet(T* v) const { return GetImpl()->AtomicGet(v); } void Set(const T& v) { GetImpl()->Set(v); } - void SetCallback(const flags_internal::FlagCallback mutation_callback) { + void SetCallback(const flags_internal::FlagCallbackFunc mutation_callback) { GetImpl()->SetCallback(mutation_callback); } void InvokeCallback() { GetImpl()->InvokeCallback(); } diff --git a/absl/flags/flag_test.cc b/absl/flags/flag_test.cc index 7b50fd66..6722329c 100644 --- a/absl/flags/flag_test.cc +++ b/absl/flags/flag_test.cc @@ -51,8 +51,8 @@ void TestCallback() {} template <typename T> bool TestConstructionFor() { - constexpr flags::HelpInitArg help_arg{flags::FlagHelpSrc("literal help"), - flags::FlagHelpSrcKind::kLiteral}; + constexpr flags::FlagHelpArg help_arg{flags::FlagHelpMsg("literal help"), + flags::FlagHelpKind::kLiteral}; constexpr flags::Flag<T> f1("f1", "file", &flags::FlagMarshallingOps<T>, help_arg, &TestMakeDflt<T>); EXPECT_EQ(f1.Name(), "f1"); @@ -61,7 +61,7 @@ bool TestConstructionFor() { ABSL_CONST_INIT static flags::Flag<T> f2( "f2", "file", &flags::FlagMarshallingOps<T>, - {flags::FlagHelpSrc(&TestHelpMsg), flags::FlagHelpSrcKind::kGenFunc}, + {flags::FlagHelpMsg(&TestHelpMsg), flags::FlagHelpKind::kGenFunc}, &TestMakeDflt<T>); flags::FlagRegistrar<T, false>(&f2).OnUpdate(TestCallback); diff --git a/absl/flags/internal/flag.cc b/absl/flags/internal/flag.cc index 2ef8e3f6..3df8d82c 100644 --- a/absl/flags/internal/flag.cc +++ b/absl/flags/internal/flag.cc @@ -91,11 +91,11 @@ void FlagImpl::Init() { absl::MutexLock lock(reinterpret_cast<absl::Mutex*>(&data_guard_)); - if (cur_ != nullptr) { + if (value_.dynamic != nullptr) { inited_.store(true, std::memory_order_release); } else { // Need to initialize cur field. - cur_ = MakeInitValue().release(); + value_.dynamic = MakeInitValue().release(); StoreAtomic(); inited_.store(true, std::memory_order_release); } @@ -117,7 +117,7 @@ void FlagImpl::Destroy() { absl::MutexLock l(DataGuard()); // Values are heap allocated for Abseil Flags. - if (cur_) Delete(op_, cur_); + if (value_.dynamic) Delete(op_, value_.dynamic); // Release the dynamically allocated default value if any. if (def_kind_ == FlagDefaultSrcKind::kDynamicValue) { @@ -125,7 +125,7 @@ void FlagImpl::Destroy() { } // If this flag has an assigned callback, release callback data. - if (callback_data_) delete callback_data_; + if (callback_) delete callback_; } absl::MutexLock l(&flag_mutex_lifetime_guard); @@ -150,8 +150,8 @@ std::string FlagImpl::Filename() const { } std::string FlagImpl::Help() const { - return help_source_kind_ == FlagHelpSrcKind::kLiteral ? help_.literal - : help_.gen_func(); + return help_source_kind_ == FlagHelpKind::kLiteral ? help_.literal + : help_.gen_func(); } bool FlagImpl::IsModified() const { @@ -174,27 +174,26 @@ std::string FlagImpl::DefaultValue() const { std::string FlagImpl::CurrentValue() const { absl::MutexLock l(DataGuard()); - return Unparse(marshalling_op_, cur_); + return Unparse(marshalling_op_, value_.dynamic); } -void FlagImpl::SetCallback( - const flags_internal::FlagCallback mutation_callback) { +void FlagImpl::SetCallback(const FlagCallbackFunc mutation_callback) { absl::MutexLock l(DataGuard()); - if (callback_data_ == nullptr) { - callback_data_ = new CallbackData; + if (callback_ == nullptr) { + callback_ = new FlagCallback; } - callback_data_->func = mutation_callback; + callback_->func = mutation_callback; InvokeCallback(); } void FlagImpl::InvokeCallback() const { - if (!callback_data_) return; + if (!callback_) return; // Make a copy of the C-style function pointer that we are about to invoke // before we release the lock guarding it. - FlagCallback cb = callback_data_->func; + FlagCallbackFunc cb = callback_->func; // If the flag has a mutation callback this function invokes it. While the // callback is being invoked the primary flag's mutex is unlocked and it is @@ -208,7 +207,7 @@ void FlagImpl::InvokeCallback() const { // completed. Requires that *primary_lock be held in exclusive mode; it may be // released and reacquired by the implementation. MutexRelock relock(DataGuard()); - absl::MutexLock lock(&callback_data_->guard); + absl::MutexLock lock(&callback_->guard); cb(); } @@ -267,7 +266,7 @@ void FlagImpl::Read(void* dst, const flags_internal::FlagOpFn dst_op) const { absl::StrCat("Flag '", Name(), "' is defined as one type and declared as another")); } - CopyConstruct(op_, cur_, dst); + CopyConstruct(op_, value_.dynamic, dst); } void FlagImpl::StoreAtomic() { @@ -275,14 +274,14 @@ void FlagImpl::StoreAtomic() { if (data_size <= sizeof(int64_t)) { int64_t t = 0; - std::memcpy(&t, cur_, data_size); - atomics_.small_atomic.store(t, std::memory_order_release); + std::memcpy(&t, value_.dynamic, data_size); + value_.atomics.small_atomic.store(t, std::memory_order_release); } #if defined(ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD) else if (data_size <= sizeof(FlagsInternalTwoWordsType)) { FlagsInternalTwoWordsType t{0, 0}; - std::memcpy(&t, cur_, data_size); - atomics_.big_atomic.store(t, std::memory_order_release); + std::memcpy(&t, value_.dynamic, data_size); + value_.atomics.big_atomic.store(t, std::memory_order_release); } #endif } @@ -313,7 +312,7 @@ void FlagImpl::Write(const void* src, const flags_internal::FlagOpFn src_op) { modified_ = true; counter_++; - Copy(op_, src, cur_); + Copy(op_, src, value_.dynamic); StoreAtomic(); InvokeCallback(); @@ -334,7 +333,7 @@ bool FlagImpl::SetFromString(absl::string_view value, FlagSettingMode set_mode, switch (set_mode) { case SET_FLAGS_VALUE: { // set or modify the flag's value - if (!TryParse(&cur_, value, err)) return false; + if (!TryParse(&value_.dynamic, value, err)) return false; modified_ = true; counter_++; StoreAtomic(); @@ -348,7 +347,7 @@ bool FlagImpl::SetFromString(absl::string_view value, FlagSettingMode set_mode, case SET_FLAG_IF_DEFAULT: { // set the flag's value, but only if it hasn't been set by someone else if (!modified_) { - if (!TryParse(&cur_, value, err)) return false; + if (!TryParse(&value_.dynamic, value, err)) return false; modified_ = true; counter_++; StoreAtomic(); @@ -381,7 +380,7 @@ bool FlagImpl::SetFromString(absl::string_view value, FlagSettingMode set_mode, if (!modified_) { // Need to set both default value *and* current, in this case - Copy(op_, default_src_.dynamic_value, cur_); + Copy(op_, default_src_.dynamic_value, value_.dynamic); StoreAtomic(); InvokeCallback(); } diff --git a/absl/flags/internal/flag.h b/absl/flags/internal/flag.h index 4341f113..cc07dce1 100644 --- a/absl/flags/internal/flag.h +++ b/absl/flags/internal/flag.h @@ -22,6 +22,7 @@ #include <cstring> #include <memory> #include <string> +#include <type_traits> #include "absl/base/config.h" #include "absl/base/thread_annotations.h" @@ -37,65 +38,12 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace flags_internal { -// The minimum atomic size we believe to generate lock free code, i.e. all -// trivially copyable types not bigger this size generate lock free code. -static constexpr int kMinLockFreeAtomicSize = 8; - -// The same as kMinLockFreeAtomicSize but maximum atomic size. As double words -// might use two registers, we want to dispatch the logic for them. -#if defined(ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD) -static constexpr int kMaxLockFreeAtomicSize = 16; -#else -static constexpr int kMaxLockFreeAtomicSize = 8; -#endif - -// We can use atomic in cases when it fits in the register, trivially copyable -// in order to make memcpy operations. -template <typename T> -struct IsAtomicFlagTypeTrait { - static constexpr bool value = - (sizeof(T) <= kMaxLockFreeAtomicSize && - type_traits_internal::is_trivially_copyable<T>::value); -}; - -// Clang does not always produce cmpxchg16b instruction when alignment of a 16 -// bytes type is not 16. -struct alignas(16) FlagsInternalTwoWordsType { - int64_t first; - int64_t second; -}; - -constexpr bool operator==(const FlagsInternalTwoWordsType& that, - const FlagsInternalTwoWordsType& other) { - return that.first == other.first && that.second == other.second; -} -constexpr bool operator!=(const FlagsInternalTwoWordsType& that, - const FlagsInternalTwoWordsType& other) { - return !(that == other); -} - -constexpr int64_t SmallAtomicInit() { return 0xababababababababll; } - -template <typename T, typename S = void> -struct BestAtomicType { - using type = int64_t; - static constexpr int64_t AtomicInit() { return SmallAtomicInit(); } -}; - -template <typename T> -struct BestAtomicType< - T, typename std::enable_if<(kMinLockFreeAtomicSize < sizeof(T) && - sizeof(T) <= kMaxLockFreeAtomicSize), - void>::type> { - using type = FlagsInternalTwoWordsType; - static constexpr FlagsInternalTwoWordsType AtomicInit() { - return {SmallAtomicInit(), SmallAtomicInit()}; - } -}; - template <typename T> class Flag; +/////////////////////////////////////////////////////////////////////////////// +// Persistent state of the flag data. + template <typename T> class FlagState : public flags_internal::FlagStateInterface { public: @@ -123,24 +71,27 @@ class FlagState : public flags_internal::FlagStateInterface { int64_t counter_; }; +/////////////////////////////////////////////////////////////////////////////// +// Flag help auxiliary structs. + // This is help argument for absl::Flag encapsulating the string literal pointer // or pointer to function generating it as well as enum descriminating two // cases. using HelpGenFunc = std::string (*)(); -union FlagHelpSrc { - constexpr explicit FlagHelpSrc(const char* help_msg) : literal(help_msg) {} - constexpr explicit FlagHelpSrc(HelpGenFunc help_gen) : gen_func(help_gen) {} +union FlagHelpMsg { + constexpr explicit FlagHelpMsg(const char* help_msg) : literal(help_msg) {} + constexpr explicit FlagHelpMsg(HelpGenFunc help_gen) : gen_func(help_gen) {} const char* literal; HelpGenFunc gen_func; }; -enum class FlagHelpSrcKind : int8_t { kLiteral, kGenFunc }; +enum class FlagHelpKind : int8_t { kLiteral, kGenFunc }; -struct HelpInitArg { - FlagHelpSrc source; - FlagHelpSrcKind kind; +struct FlagHelpArg { + FlagHelpMsg source; + FlagHelpKind kind; }; extern const char kStrippedFlagHelp[]; @@ -172,17 +123,18 @@ constexpr const char* HelpConstexprWrap(char* p) { return p; } // evaluatable in constexpr context, but the cost is an extra function being // generated in the ABSL_FLAG code. template <typename T, int = (T::Const(), 1)> -constexpr flags_internal::HelpInitArg HelpArg(int) { - return {flags_internal::FlagHelpSrc(T::Const()), - flags_internal::FlagHelpSrcKind::kLiteral}; +constexpr FlagHelpArg HelpArg(int) { + return {FlagHelpMsg(T::Const()), FlagHelpKind::kLiteral}; } template <typename T> -constexpr flags_internal::HelpInitArg HelpArg(char) { - return {flags_internal::FlagHelpSrc(&T::NonConst), - flags_internal::FlagHelpSrcKind::kGenFunc}; +constexpr FlagHelpArg HelpArg(char) { + return {FlagHelpMsg(&T::NonConst), FlagHelpKind::kGenFunc}; } +/////////////////////////////////////////////////////////////////////////////// +// Flag default value auxiliary structs. + // Signature for the function generating the initial flag value (usually // based on default value supplied in flag's definition) using FlagDfltGenFunc = void* (*)(); @@ -197,32 +149,138 @@ union FlagDefaultSrc { enum class FlagDefaultSrcKind : int8_t { kDynamicValue, kGenFunc }; +/////////////////////////////////////////////////////////////////////////////// +// Flag current value auxiliary structs. + +// The minimum atomic size we believe to generate lock free code, i.e. all +// trivially copyable types not bigger this size generate lock free code. +static constexpr int kMinLockFreeAtomicSize = 8; + +// The same as kMinLockFreeAtomicSize but maximum atomic size. As double words +// might use two registers, we want to dispatch the logic for them. +#if defined(ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD) +static constexpr int kMaxLockFreeAtomicSize = 16; +#else +static constexpr int kMaxLockFreeAtomicSize = 8; +#endif + +// We can use atomic in cases when it fits in the register, trivially copyable +// in order to make memcpy operations. +template <typename T> +struct IsAtomicFlagTypeTrait { + static constexpr bool value = + (sizeof(T) <= kMaxLockFreeAtomicSize && + type_traits_internal::is_trivially_copyable<T>::value); +}; + +// Clang does not always produce cmpxchg16b instruction when alignment of a 16 +// bytes type is not 16. +struct alignas(16) FlagsInternalTwoWordsType { + int64_t first; + int64_t second; +}; + +constexpr bool operator==(const FlagsInternalTwoWordsType& that, + const FlagsInternalTwoWordsType& other) { + return that.first == other.first && that.second == other.second; +} +constexpr bool operator!=(const FlagsInternalTwoWordsType& that, + const FlagsInternalTwoWordsType& other) { + return !(that == other); +} + +constexpr int64_t SmallAtomicInit() { return 0xababababababababll; } + +template <typename T, typename S = void> +struct BestAtomicType { + using type = int64_t; + static constexpr int64_t AtomicInit() { return SmallAtomicInit(); } +}; + +template <typename T> +struct BestAtomicType< + T, typename std::enable_if<(kMinLockFreeAtomicSize < sizeof(T) && + sizeof(T) <= kMaxLockFreeAtomicSize), + void>::type> { + using type = FlagsInternalTwoWordsType; + static constexpr FlagsInternalTwoWordsType AtomicInit() { + return {SmallAtomicInit(), SmallAtomicInit()}; + } +}; + +struct FlagValue { + // Heap allocated value. + void* dynamic = nullptr; + // For some types, a copy of the current value is kept in an atomically + // accessible field. + union Atomics { + // Using small atomic for small types. + std::atomic<int64_t> small_atomic; + template <typename T, + typename K = typename std::enable_if< + (sizeof(T) <= kMinLockFreeAtomicSize), void>::type> + int64_t load() const { + return small_atomic.load(std::memory_order_acquire); + } + +#if defined(ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD) + // Using big atomics for big types. + std::atomic<FlagsInternalTwoWordsType> big_atomic; + template <typename T, typename K = typename std::enable_if< + (kMinLockFreeAtomicSize < sizeof(T) && + sizeof(T) <= kMaxLockFreeAtomicSize), + void>::type> + FlagsInternalTwoWordsType load() const { + return big_atomic.load(std::memory_order_acquire); + } + constexpr Atomics() + : big_atomic{FlagsInternalTwoWordsType{SmallAtomicInit(), + SmallAtomicInit()}} {} +#else + constexpr Atomics() : small_atomic{SmallAtomicInit()} {} +#endif + }; + Atomics atomics{}; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Flag callback auxiliary structs. + // Signature for the mutation callback used by watched Flags // The callback is noexcept. // TODO(rogeeff): add noexcept after C++17 support is added. -using FlagCallback = void (*)(); +using FlagCallbackFunc = void (*)(); + +struct FlagCallback { + FlagCallbackFunc func; + absl::Mutex guard; // Guard for concurrent callback invocations. +}; + +/////////////////////////////////////////////////////////////////////////////// +// Flag implementation, which does not depend on flag value type. +// The class encapsulates the Flag's data and access to it. struct DynValueDeleter { - void operator()(void* ptr) const { Delete(op, ptr); } + explicit DynValueDeleter(FlagOpFn op_arg = nullptr) : op(op_arg) {} + void operator()(void* ptr) const { + if (op != nullptr) Delete(op, ptr); + } const FlagOpFn op; }; -// The class encapsulates the Flag's data and safe access to it. class FlagImpl { public: - constexpr FlagImpl(const char* name, const char* filename, - const flags_internal::FlagOpFn op, - const flags_internal::FlagMarshallingOpFn marshalling_op, - const HelpInitArg help, - const flags_internal::FlagDfltGenFunc default_value_gen) + constexpr FlagImpl(const char* name, const char* filename, FlagOpFn op, + FlagMarshallingOpFn marshalling_op, FlagHelpArg help, + FlagDfltGenFunc default_value_gen) : name_(name), filename_(filename), op_(op), marshalling_op_(marshalling_op), help_(help.source), help_source_kind_(help.kind), - def_kind_(flags_internal::FlagDefaultSrcKind::kGenFunc), + def_kind_(FlagDefaultSrcKind::kGenFunc), default_src_(default_value_gen), data_guard_{} {} @@ -237,7 +295,7 @@ class FlagImpl { bool IsSpecifiedOnCommandLine() const ABSL_LOCKS_EXCLUDED(*DataGuard()); std::string DefaultValue() const ABSL_LOCKS_EXCLUDED(*DataGuard()); std::string CurrentValue() const ABSL_LOCKS_EXCLUDED(*DataGuard()); - void Read(void* dst, const flags_internal::FlagOpFn dst_op) const + void Read(void* dst, const FlagOpFn dst_op) const ABSL_LOCKS_EXCLUDED(*DataGuard()); // Attempts to parse supplied `value` std::string. If parsing is successful, then // it replaces `dst` with the new value. @@ -247,32 +305,30 @@ class FlagImpl { #ifndef NDEBUG template <typename T> void Get(T* dst) const { - Read(dst, &flags_internal::FlagOps<T>); + Read(dst, &FlagOps<T>); } #else template <typename T, typename std::enable_if< - !flags_internal::IsAtomicFlagTypeTrait<T>::value, - int>::type = 0> + !IsAtomicFlagTypeTrait<T>::value, int>::type = 0> void Get(T* dst) const { - Read(dst, &flags_internal::FlagOps<T>); + Read(dst, &FlagOps<T>); } // Overload for `GetFlag()` for types that support lock-free reads. - template <typename T, - typename std::enable_if< - flags_internal::IsAtomicFlagTypeTrait<T>::value, int>::type = 0> + template <typename T, typename std::enable_if<IsAtomicFlagTypeTrait<T>::value, + int>::type = 0> void Get(T* dst) const { - using U = flags_internal::BestAtomicType<T>; - const typename U::type r = atomics_.template load<T>(); + using U = BestAtomicType<T>; + const typename U::type r = value_.atomics.template load<T>(); if (r != U::AtomicInit()) { std::memcpy(static_cast<void*>(dst), &r, sizeof(T)); } else { - Read(dst, &flags_internal::FlagOps<T>); + Read(dst, &FlagOps<T>); } } #endif // Mutating access methods - void Write(const void* src, const flags_internal::FlagOpFn src_op) + void Write(const void* src, const FlagOpFn src_op) ABSL_LOCKS_EXCLUDED(*DataGuard()); bool SetFromString(absl::string_view value, FlagSettingMode set_mode, ValueSource source, std::string* err) @@ -282,18 +338,18 @@ class FlagImpl { void StoreAtomic() ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard()); // Interfaces to operate on callbacks. - void SetCallback(const flags_internal::FlagCallback mutation_callback) + void SetCallback(const FlagCallbackFunc mutation_callback) ABSL_LOCKS_EXCLUDED(*DataGuard()); void InvokeCallback() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard()); // Interfaces to save/restore mutable flag data template <typename T> - std::unique_ptr<flags_internal::FlagStateInterface> SaveState( - Flag<T>* flag) const ABSL_LOCKS_EXCLUDED(*DataGuard()) { + std::unique_ptr<FlagStateInterface> SaveState(Flag<T>* flag) const + ABSL_LOCKS_EXCLUDED(*DataGuard()) { T&& cur_value = flag->Get(); absl::MutexLock l(DataGuard()); - return absl::make_unique<flags_internal::FlagState<T>>( + return absl::make_unique<FlagState<T>>( flag, std::move(cur_value), modified_, on_command_line_, counter_); } bool RestoreState(const void* value, bool modified, bool on_command_line, @@ -306,35 +362,47 @@ class FlagImpl { ABSL_LOCKS_EXCLUDED(*DataGuard()); private: - // Lazy initialization of the Flag's data. - void Init(); - // Ensures that the lazily initialized data is initialized, - // and returns pointer to the mutex guarding flags data. + // Ensures that `data_guard_` is initialized and returns it. absl::Mutex* DataGuard() const ABSL_LOCK_RETURNED((absl::Mutex*)&data_guard_); // Returns heap allocated value of type T initialized with default value. std::unique_ptr<void, DynValueDeleter> MakeInitValue() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard()); + // Lazy initialization of the Flag's data. + void Init(); - // Immutable Flag's data. - // Constant configuration for a particular flag. - const char* const name_; // Flags name passed to ABSL_FLAG as second arg. - const char* const filename_; // The file name where ABSL_FLAG resides. - const FlagOpFn op_; // Type-specific handler. - const FlagMarshallingOpFn marshalling_op_; // Marshalling ops handler. - const FlagHelpSrc help_; // Help message literal or function to generate it. + // Immutable flag's state. + + // Flags name passed to ABSL_FLAG as second arg. + const char* const name_; + // The file name where ABSL_FLAG resides. + const char* const filename_; + // Type-specific handler. + const FlagOpFn op_; + // Marshalling ops handler. + const FlagMarshallingOpFn marshalling_op_; + // Help message literal or function to generate it. + const FlagHelpMsg help_; // Indicates if help message was supplied as literal or generator func. - const FlagHelpSrcKind help_source_kind_; + const FlagHelpKind help_source_kind_; // Indicates that the Flag state is initialized. std::atomic<bool> inited_{false}; - // Mutable Flag state (guarded by data_guard_). - // Additional bool to protect against multiple concurrent constructions - // of `data_guard_`. + + // Mutable flag's state (guarded by `data_guard_`). + + // Protects against multiple concurrent constructions of `data_guard_`. bool is_data_guard_inited_ = false; - // Has flag value been modified? + // Has this flag's value been modified? bool modified_ ABSL_GUARDED_BY(*DataGuard()) = false; - // Specified on command line. + // Has this flag been specified on command line. bool on_command_line_ ABSL_GUARDED_BY(*DataGuard()) = false; + + // Mutation counter + int64_t counter_ ABSL_GUARDED_BY(*DataGuard()) = 0; + + // Optional flag's callback and absl::Mutex to guard the invocations. + FlagCallback* callback_ ABSL_GUARDED_BY(*DataGuard()) = nullptr; + // If def_kind_ == kDynamicValue, default_src_ holds a dynamically allocated // value. FlagDefaultSrcKind def_kind_ ABSL_GUARDED_BY(*DataGuard()); @@ -343,46 +411,10 @@ class FlagImpl { // value via SetCommandLineOptionWithMode. def_kind_ is used to distinguish // these two cases. FlagDefaultSrc default_src_ ABSL_GUARDED_BY(*DataGuard()); - // Lazily initialized pointer to current value - void* cur_ ABSL_GUARDED_BY(*DataGuard()) = nullptr; - // Mutation counter - int64_t counter_ ABSL_GUARDED_BY(*DataGuard()) = 0; - // For some types, a copy of the current value is kept in an atomically - // accessible field. - union Atomics { - // Using small atomic for small types. - std::atomic<int64_t> small_atomic; - template <typename T, - typename K = typename std::enable_if< - (sizeof(T) <= kMinLockFreeAtomicSize), void>::type> - int64_t load() const { - return small_atomic.load(std::memory_order_acquire); - } -#if defined(ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD) - // Using big atomics for big types. - std::atomic<FlagsInternalTwoWordsType> big_atomic; - template <typename T, typename K = typename std::enable_if< - (kMinLockFreeAtomicSize < sizeof(T) && - sizeof(T) <= kMaxLockFreeAtomicSize), - void>::type> - FlagsInternalTwoWordsType load() const { - return big_atomic.load(std::memory_order_acquire); - } - constexpr Atomics() - : big_atomic{FlagsInternalTwoWordsType{SmallAtomicInit(), - SmallAtomicInit()}} {} -#else - constexpr Atomics() : small_atomic{SmallAtomicInit()} {} -#endif - }; - Atomics atomics_{}; + // Current Flag Value + FlagValue value_; - struct CallbackData { - FlagCallback func; - absl::Mutex guard; // Guard for concurrent callback invocations. - }; - CallbackData* callback_data_ ABSL_GUARDED_BY(*DataGuard()) = nullptr; // This is reserved space for an absl::Mutex to guard flag data. It will be // initialized in FlagImpl::Init via placement new. // We can't use "absl::Mutex data_guard_", since this class is not literal. @@ -393,15 +425,18 @@ class FlagImpl { alignas(absl::Mutex) mutable char data_guard_[sizeof(absl::Mutex)]; }; -// This is "unspecified" implementation of absl::Flag<T> type. +/////////////////////////////////////////////////////////////////////////////// +// The "unspecified" implementation of Flag object parameterized by the +// flag's value type. + template <typename T> class Flag final : public flags_internal::CommandLineFlag { public: constexpr Flag(const char* name, const char* filename, - const flags_internal::FlagMarshallingOpFn marshalling_op, - const flags_internal::HelpInitArg help, - const flags_internal::FlagDfltGenFunc default_value_gen) - : impl_(name, filename, &flags_internal::FlagOps<T>, marshalling_op, help, + const FlagMarshallingOpFn marshalling_op, + const FlagHelpArg help, + const FlagDfltGenFunc default_value_gen) + : impl_(name, filename, &FlagOps<T>, marshalling_op, help, default_value_gen) {} T Get() const { @@ -417,9 +452,9 @@ class Flag final : public flags_internal::CommandLineFlag { return std::move(u.value); } - void Set(const T& v) { impl_.Write(&v, &flags_internal::FlagOps<T>); } + void Set(const T& v) { impl_.Write(&v, &FlagOps<T>); } - void SetCallback(const flags_internal::FlagCallback mutation_callback) { + void SetCallback(const FlagCallbackFunc mutation_callback) { impl_.SetCallback(mutation_callback); } @@ -434,7 +469,6 @@ class Flag final : public flags_internal::CommandLineFlag { } std::string DefaultValue() const override { return impl_.DefaultValue(); } std::string CurrentValue() const override { return impl_.CurrentValue(); } - bool ValidateInputValue(absl::string_view value) const override { return impl_.ValidateInputValue(value); } @@ -442,24 +476,20 @@ class Flag final : public flags_internal::CommandLineFlag { // Interfaces to save and restore flags to/from persistent state. // Returns current flag state or nullptr if flag does not support // saving and restoring a state. - std::unique_ptr<flags_internal::FlagStateInterface> SaveState() override { + std::unique_ptr<FlagStateInterface> SaveState() override { return impl_.SaveState(this); } // Restores the flag state to the supplied state object. If there is // nothing to restore returns false. Otherwise returns true. - bool RestoreState(const flags_internal::FlagState<T>& flag_state) { + bool RestoreState(const FlagState<T>& flag_state) { return impl_.RestoreState(&flag_state.cur_value_, flag_state.modified_, flag_state.on_command_line_, flag_state.counter_); } - - bool SetFromString(absl::string_view value, - flags_internal::FlagSettingMode set_mode, - flags_internal::ValueSource source, - std::string* error) override { + bool SetFromString(absl::string_view value, FlagSettingMode set_mode, + ValueSource source, std::string* error) override { return impl_.SetFromString(value, set_mode, source, error); } - void CheckDefaultValueParsingRoundtrip() const override { impl_.CheckDefaultValueParsingRoundtrip(); } @@ -469,14 +499,10 @@ class Flag final : public flags_internal::CommandLineFlag { void Destroy() override { impl_.Destroy(); } - void Read(void* dst) const override { - impl_.Read(dst, &flags_internal::FlagOps<T>); - } - flags_internal::FlagOpFn TypeId() const override { - return &flags_internal::FlagOps<T>; - } + void Read(void* dst) const override { impl_.Read(dst, &FlagOps<T>); } + FlagOpFn TypeId() const override { return &FlagOps<T>; } - // Flag's data + // Flag's implementation with value type abstracted out. FlagImpl impl_; }; @@ -499,7 +525,7 @@ class FlagRegistrar { if (do_register) flags_internal::RegisterCommandLineFlag(flag_); } - FlagRegistrar& OnUpdate(flags_internal::FlagCallback cb) && { + FlagRegistrar& OnUpdate(FlagCallbackFunc cb) && { flag_->SetCallback(cb); return *this; } |