diff options
author | Abseil Team <absl-team@google.com> | 2020-04-03T20·24-0700 |
---|---|---|
committer | Andy Getz <durandal@google.com> | 2020-04-04T21·08-0400 |
commit | d43b7997c0ca0f3312a51d1057fb73cfe934703b (patch) | |
tree | ea979e2e5d205e9bd38757a2793720b15f80fb83 /absl/flags | |
parent | 62f05b1f57ad660e9c09e02ce7d591dcc4d0ca08 (diff) |
Export of internal Abseil changes
-- 7a9e8d95f795be037aa2dce4e44809ad0166aaec by Samuel Benzaquen <sbenza@google.com>: Make end() iterator be nullptr. This makes the creation of and comparison with end() smaller and faster. `find()!=end()` becomes leaner. PiperOrigin-RevId: 304681605 -- 8f3024979446b391b79b1b60ada7d00a504d6aa6 by Derek Mauro <dmauro@google.com>: Fix Bazel's distdir detection and prefer double brackets (bash recommendation) PiperOrigin-RevId: 304615725 -- f1d709cb4b2b3743d548b814dd19602fb057a5e6 by Abseil Team <absl-team@google.com>: Internal change PiperOrigin-RevId: 304570545 -- 2bbfa5bda52057e1938a96c286ad33ff64e535e0 by Gennadiy Rozental <rogeeff@google.com>: Implement general storage case as aligned buffer. Aside from eliminating dynamic memory allocation for flag storage, we also saving 11 bytes per int flag, 15 bytes per double and string flag. PiperOrigin-RevId: 304511965 -- 9e1aed2a95d7d060f8b906fe8c67fc3ba537b521 by Derek Mauro <dmauro@google.com>: Use reserve to make a bad_alloc less likely in endian_test This happened once and shouldn't have happened, so it was probably just a flake, but might as well make this change. PiperOrigin-RevId: 304505572 -- c2faf22ba2d4d66753390e6959494214895581f0 by Gennadiy Rozental <rogeeff@google.com>: Use anonymous bit fields to enforce separation between const and mutable bit fields. We also move init_control field (which is now safe) to save 8 bytes per flag (based on size_tester output) PiperOrigin-RevId: 304505215 -- 7ec51250a84bb03e826b3caad64431e91748186a by Krzysztof Kosiński <krzysio@google.com>: Change the buffer size in AppendNumberUnit to constexpr. PiperOrigin-RevId: 304492779 -- a6c8db1be4f421ea7b7c02f7a01b4f48bad61883 by Gennadiy Rozental <rogeeff@google.com>: Add test cases for two word storage. Some additional tests were added for other storage kinds as well. These came about after I started to look into a coverage output and noticed that some cases (like reading flag values via reflection) were not covered by this test at all. It does not make sense to just add tests for two word values, so I've covered other storage kinds as well. PiperOrigin-RevId: 304432511 -- 2644ecc32e1215cd6451efcb2f1054fd77e7c812 by Abseil Team <absl-team@google.com>: Internal change PiperOrigin-RevId: 304254681 -- 4949a6b20c2bb4b9b2c811f439ccb893abc08df5 by Abseil Team <absl-team@google.com>: Internal change PiperOrigin-RevId: 304250274 GitOrigin-RevId: 7a9e8d95f795be037aa2dce4e44809ad0166aaec Change-Id: I01623de87355bec5cf87cc5932a1ca44cade9aae
Diffstat (limited to 'absl/flags')
-rw-r--r-- | absl/flags/BUILD.bazel | 2 | ||||
-rw-r--r-- | absl/flags/CMakeLists.txt | 1 | ||||
-rw-r--r-- | absl/flags/flag.h | 6 | ||||
-rw-r--r-- | absl/flags/flag_test.cc | 173 | ||||
-rw-r--r-- | absl/flags/internal/flag.cc | 73 | ||||
-rw-r--r-- | absl/flags/internal/flag.h | 103 |
6 files changed, 248 insertions, 110 deletions
diff --git a/absl/flags/BUILD.bazel b/absl/flags/BUILD.bazel index 4b51d9d45207..685e395419eb 100644 --- a/absl/flags/BUILD.bazel +++ b/absl/flags/BUILD.bazel @@ -326,7 +326,9 @@ cc_test( ":handle", ":registry", "//absl/base:core_headers", + "//absl/base:malloc_internal", "//absl/strings", + "//absl/time", "@com_google_googletest//:gtest_main", ], ) diff --git a/absl/flags/CMakeLists.txt b/absl/flags/CMakeLists.txt index 2204b0ff2dd8..ec82ee1e36bb 100644 --- a/absl/flags/CMakeLists.txt +++ b/absl/flags/CMakeLists.txt @@ -304,6 +304,7 @@ absl_cc_test( absl::flags_internal absl::flags_registry absl::strings + absl::time gtest_main ) diff --git a/absl/flags/flag.h b/absl/flags/flag.h index 4cc8ae373299..150615929b0f 100644 --- a/absl/flags/flag.h +++ b/absl/flags/flag.h @@ -314,9 +314,9 @@ ABSL_NAMESPACE_END static std::string NonConst() { return ABSL_FLAG_IMPL_FLAGHELP(txt); } \ } -#define ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value) \ - static void* AbslFlagsInitFlag##name() { \ - return absl::flags_internal::MakeFromDefaultValue<Type>(default_value); \ +#define ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value) \ + static void AbslFlagsInitFlag##name(void* dst) { \ + absl::flags_internal::MakeFromDefaultValue<Type>(dst, default_value); \ } // ABSL_FLAG_IMPL diff --git a/absl/flags/flag_test.cc b/absl/flags/flag_test.cc index 377e3b2b89ad..dbe94a2c3b62 100644 --- a/absl/flags/flag_test.cc +++ b/absl/flags/flag_test.cc @@ -19,6 +19,7 @@ #include <cmath> #include <string> +#include <thread> // NOLINT #include <vector> #include "gtest/gtest.h" @@ -34,6 +35,7 @@ #include "absl/strings/str_cat.h" #include "absl/strings/str_split.h" #include "absl/strings/string_view.h" +#include "absl/time/time.h" ABSL_DECLARE_FLAG(int64_t, mistyped_int_flag); ABSL_DECLARE_FLAG(std::vector<std::string>, mistyped_string_flag); @@ -44,8 +46,8 @@ namespace flags = absl::flags_internal; std::string TestHelpMsg() { return "dynamic help"; } template <typename T> -void* TestMakeDflt() { - return new T{}; +void TestMakeDflt(void* dst) { + new (dst) T{}; } void TestCallback() {} @@ -74,6 +76,7 @@ class FlagTest : public testing::Test { #endif return std::string(fname); } + flags::FlagSaver flag_saver_; }; struct S1 { @@ -107,15 +110,15 @@ TEST_F(FlagTest, Traits) { flags::FlagValueStorageKind::kTwoWordsAtomic); #else EXPECT_EQ(flags::StorageKind<S1>(), - flags::FlagValueStorageKind::kHeapAllocated); + flags::FlagValueStorageKind::kAlignedBuffer); EXPECT_EQ(flags::StorageKind<S2>(), - flags::FlagValueStorageKind::kHeapAllocated); + flags::FlagValueStorageKind::kAlignedBuffer); #endif EXPECT_EQ(flags::StorageKind<std::string>(), - flags::FlagValueStorageKind::kHeapAllocated); + flags::FlagValueStorageKind::kAlignedBuffer); EXPECT_EQ(flags::StorageKind<std::vector<std::string>>(), - flags::FlagValueStorageKind::kHeapAllocated); + flags::FlagValueStorageKind::kAlignedBuffer); } // -------------------------------------------------------------------- @@ -190,6 +193,7 @@ ABSL_DECLARE_FLAG(uint64_t, test_flag_08); ABSL_DECLARE_FLAG(double, test_flag_09); ABSL_DECLARE_FLAG(float, test_flag_10); ABSL_DECLARE_FLAG(std::string, test_flag_11); +ABSL_DECLARE_FLAG(absl::Duration, test_flag_12); namespace { @@ -208,6 +212,7 @@ TEST_F(FlagTest, TestFlagDeclaration) { EXPECT_EQ(FLAGS_test_flag_09.Name(), "test_flag_09"); EXPECT_EQ(FLAGS_test_flag_10.Name(), "test_flag_10"); EXPECT_EQ(FLAGS_test_flag_11.Name(), "test_flag_11"); + EXPECT_EQ(FLAGS_test_flag_12.Name(), "test_flag_12"); } #endif // !ABSL_FLAGS_STRIP_NAMES @@ -226,6 +231,7 @@ ABSL_FLAG(uint64_t, test_flag_08, 9876543, "test flag 08"); ABSL_FLAG(double, test_flag_09, -9.876e-50, "test flag 09"); ABSL_FLAG(float, test_flag_10, 1.234e12f, "test flag 10"); ABSL_FLAG(std::string, test_flag_11, "", "test flag 11"); +ABSL_FLAG(absl::Duration, test_flag_12, absl::Minutes(10), "test flag 12"); namespace { @@ -287,6 +293,11 @@ TEST_F(FlagTest, TestFlagDefinition) { EXPECT_EQ(FLAGS_test_flag_11.Help(), "test flag 11"); EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_11.Filename(), expected_file_name)) << FLAGS_test_flag_11.Filename(); + + EXPECT_EQ(FLAGS_test_flag_12.Name(), "test_flag_12"); + EXPECT_EQ(FLAGS_test_flag_12.Help(), "test flag 12"); + EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_12.Filename(), expected_file_name)) + << FLAGS_test_flag_12.Filename(); } #endif // !ABSL_FLAGS_STRIP_NAMES @@ -304,6 +315,20 @@ TEST_F(FlagTest, TestDefault) { EXPECT_EQ(FLAGS_test_flag_09.DefaultValue(), "-9.876e-50"); EXPECT_EQ(FLAGS_test_flag_10.DefaultValue(), "1.234e+12"); EXPECT_EQ(FLAGS_test_flag_11.DefaultValue(), ""); + EXPECT_EQ(FLAGS_test_flag_12.DefaultValue(), "10m"); + + EXPECT_EQ(FLAGS_test_flag_01.CurrentValue(), "true"); + EXPECT_EQ(FLAGS_test_flag_02.CurrentValue(), "1234"); + EXPECT_EQ(FLAGS_test_flag_03.CurrentValue(), "-34"); + EXPECT_EQ(FLAGS_test_flag_04.CurrentValue(), "189"); + EXPECT_EQ(FLAGS_test_flag_05.CurrentValue(), "10765"); + EXPECT_EQ(FLAGS_test_flag_06.CurrentValue(), "40000"); + EXPECT_EQ(FLAGS_test_flag_07.CurrentValue(), "-1234567"); + EXPECT_EQ(FLAGS_test_flag_08.CurrentValue(), "9876543"); + EXPECT_EQ(FLAGS_test_flag_09.CurrentValue(), "-9.876e-50"); + EXPECT_EQ(FLAGS_test_flag_10.CurrentValue(), "1.234e+12"); + EXPECT_EQ(FLAGS_test_flag_11.CurrentValue(), ""); + EXPECT_EQ(FLAGS_test_flag_12.CurrentValue(), "10m"); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_01), true); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_02), 1234); @@ -316,6 +341,7 @@ TEST_F(FlagTest, TestDefault) { EXPECT_NEAR(absl::GetFlag(FLAGS_test_flag_09), -9.876e-50, 1e-55); EXPECT_NEAR(absl::GetFlag(FLAGS_test_flag_10), 1.234e12f, 1e5f); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_11), ""); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_12), absl::Minutes(10)); } // -------------------------------------------------------------------- @@ -408,6 +434,38 @@ TEST_F(FlagTest, TestGetSet) { absl::SetFlag(&FLAGS_test_flag_11, "asdf"); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_11), "asdf"); + + absl::SetFlag(&FLAGS_test_flag_12, absl::Seconds(110)); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_12), absl::Seconds(110)); +} + +// -------------------------------------------------------------------- + +TEST_F(FlagTest, TestGetViaReflection) { + auto* handle = flags::FindCommandLineFlag("test_flag_01"); + EXPECT_EQ(*handle->Get<bool>(), true); + handle = flags::FindCommandLineFlag("test_flag_02"); + EXPECT_EQ(*handle->Get<int>(), 1234); + handle = flags::FindCommandLineFlag("test_flag_03"); + EXPECT_EQ(*handle->Get<int16_t>(), -34); + handle = flags::FindCommandLineFlag("test_flag_04"); + EXPECT_EQ(*handle->Get<uint16_t>(), 189); + handle = flags::FindCommandLineFlag("test_flag_05"); + EXPECT_EQ(*handle->Get<int32_t>(), 10765); + handle = flags::FindCommandLineFlag("test_flag_06"); + EXPECT_EQ(*handle->Get<uint32_t>(), 40000); + handle = flags::FindCommandLineFlag("test_flag_07"); + EXPECT_EQ(*handle->Get<int64_t>(), -1234567); + handle = flags::FindCommandLineFlag("test_flag_08"); + EXPECT_EQ(*handle->Get<uint64_t>(), 9876543); + handle = flags::FindCommandLineFlag("test_flag_09"); + EXPECT_NEAR(*handle->Get<double>(), -9.876e-50, 1e-55); + handle = flags::FindCommandLineFlag("test_flag_10"); + EXPECT_NEAR(*handle->Get<float>(), 1.234e12f, 1e5f); + handle = flags::FindCommandLineFlag("test_flag_11"); + EXPECT_EQ(*handle->Get<std::string>(), ""); + handle = flags::FindCommandLineFlag("test_flag_12"); + EXPECT_EQ(*handle->Get<absl::Duration>(), absl::Minutes(10)); } // -------------------------------------------------------------------- @@ -416,28 +474,32 @@ int GetDflt1() { return 1; } } // namespace -ABSL_FLAG(int, test_flag_12, GetDflt1(), "test flag 12"); -ABSL_FLAG(std::string, test_flag_13, absl::StrCat("AAA", "BBB"), - "test flag 13"); +ABSL_FLAG(int, test_int_flag_with_non_const_default, GetDflt1(), + "test int flag non const default"); +ABSL_FLAG(std::string, test_string_flag_with_non_const_default, + absl::StrCat("AAA", "BBB"), "test string flag non const default"); namespace { TEST_F(FlagTest, TestNonConstexprDefault) { - EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_12), 1); - EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_13), "AAABBB"); + EXPECT_EQ(absl::GetFlag(FLAGS_test_int_flag_with_non_const_default), 1); + EXPECT_EQ(absl::GetFlag(FLAGS_test_string_flag_with_non_const_default), + "AAABBB"); } // -------------------------------------------------------------------- } // namespace -ABSL_FLAG(bool, test_flag_14, true, absl::StrCat("test ", "flag ", "14")); +ABSL_FLAG(bool, test_flag_with_non_const_help, true, + absl::StrCat("test ", "flag ", "non const help")); namespace { #if !ABSL_FLAGS_STRIP_HELP TEST_F(FlagTest, TestNonConstexprHelp) { - EXPECT_EQ(FLAGS_test_flag_14.Help(), "test flag 14"); + EXPECT_EQ(FLAGS_test_flag_with_non_const_help.Help(), + "test flag non const help"); } #endif //! ABSL_FLAGS_STRIP_HELP @@ -503,14 +565,14 @@ std::string AbslUnparseFlag(const CustomUDT& f) { } // namespace -ABSL_FLAG(CustomUDT, test_flag_15, CustomUDT(), "test flag 15"); +ABSL_FLAG(CustomUDT, test_flag_custom_udt, CustomUDT(), "test flag custom UDT"); namespace { TEST_F(FlagTest, TestCustomUDT) { - EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_15), CustomUDT(1, 1)); - absl::SetFlag(&FLAGS_test_flag_15, CustomUDT(2, 3)); - EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_15), CustomUDT(2, 3)); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_custom_udt), CustomUDT(1, 1)); + absl::SetFlag(&FLAGS_test_flag_custom_udt, CustomUDT(2, 3)); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_custom_udt), CustomUDT(2, 3)); } // MSVC produces link error on the type mismatch. @@ -570,16 +632,17 @@ std::string AbslUnparseFlag(const ConversionTestVal& val) { // Flag default values can be specified with a value that converts to the flag // value type implicitly. -ABSL_FLAG(ConversionTestVal, test_flag_16, - ConversionTestVal::ViaImplicitConv::kTen, "test flag 16"); +ABSL_FLAG(ConversionTestVal, test_flag_implicit_conv, + ConversionTestVal::ViaImplicitConv::kTen, + "test flag init via implicit conversion"); namespace { TEST_F(FlagTest, CanSetViaImplicitConversion) { - EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_16).a, 10); - absl::SetFlag(&FLAGS_test_flag_16, + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_implicit_conv).a, 10); + absl::SetFlag(&FLAGS_test_flag_implicit_conv, ConversionTestVal::ViaImplicitConv::kEleven); - EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_16).a, 11); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_implicit_conv).a, 11); } // -------------------------------------------------------------------- @@ -646,3 +709,69 @@ TEST_F(FlagTest, TestRetiredFlagRegistration) { } } // namespace + +// -------------------------------------------------------------------- + +namespace { + +// User-defined type with small alignment, but size exceeding 16. +struct SmallAlignUDT { + SmallAlignUDT() : c('A'), s(12) {} + char c; + int16_t s; + char bytes[14]; +}; + +bool AbslParseFlag(absl::string_view, SmallAlignUDT*, std::string*) { + return true; +} +std::string AbslUnparseFlag(const SmallAlignUDT&) { return ""; } + +// User-defined type with small size, but not trivially copyable. +struct NonTriviallyCopyableUDT { + NonTriviallyCopyableUDT() : c('A') {} + NonTriviallyCopyableUDT(const NonTriviallyCopyableUDT& rhs) : c(rhs.c) {} + NonTriviallyCopyableUDT& operator=(const NonTriviallyCopyableUDT& rhs) { + c = rhs.c; + return *this; + } + + char c; +}; + +bool AbslParseFlag(absl::string_view, NonTriviallyCopyableUDT*, std::string*) { + return true; +} +std::string AbslUnparseFlag(const NonTriviallyCopyableUDT&) { return ""; } + +} // namespace + +ABSL_FLAG(SmallAlignUDT, test_flag_sa_udt, {}, "help"); +ABSL_FLAG(NonTriviallyCopyableUDT, test_flag_ntc_udt, {}, "help"); + +namespace { + +TEST_F(FlagTest, TestSmallAlignUDT) { + SmallAlignUDT value = absl::GetFlag(FLAGS_test_flag_sa_udt); + EXPECT_EQ(value.c, 'A'); + EXPECT_EQ(value.s, 12); + + value.c = 'B'; + value.s = 45; + absl::SetFlag(&FLAGS_test_flag_sa_udt, value); + value = absl::GetFlag(FLAGS_test_flag_sa_udt); + EXPECT_EQ(value.c, 'B'); + EXPECT_EQ(value.s, 45); +} + +TEST_F(FlagTest, TestNonTriviallyCopyableUDT) { + NonTriviallyCopyableUDT value = absl::GetFlag(FLAGS_test_flag_ntc_udt); + EXPECT_EQ(value.c, 'A'); + + value.c = 'B'; + absl::SetFlag(&FLAGS_test_flag_ntc_udt, value); + value = absl::GetFlag(FLAGS_test_flag_ntc_udt); + EXPECT_EQ(value.c, 'B'); +} + +} // namespace diff --git a/absl/flags/internal/flag.cc b/absl/flags/internal/flag.cc index f3c424ad83d8..089567f7ae50 100644 --- a/absl/flags/internal/flag.cc +++ b/absl/flags/internal/flag.cc @@ -92,9 +92,9 @@ class FlagState : public flags_internal::FlagStateInterface { counter_(counter) {} ~FlagState() override { - if (flag_impl_->ValueStorageKind() != FlagValueStorageKind::kHeapAllocated) + if (flag_impl_->ValueStorageKind() != FlagValueStorageKind::kAlignedBuffer) return; - flags_internal::Delete(flag_impl_->op_, value_.dynamic); + flags_internal::Delete(flag_impl_->op_, value_.heap_allocated); } private: @@ -112,11 +112,11 @@ class FlagState : public flags_internal::FlagStateInterface { // Flag and saved flag data. FlagImpl* flag_impl_; union SavedValue { - explicit SavedValue(void* v) : dynamic(v) {} + explicit SavedValue(void* v) : heap_allocated(v) {} explicit SavedValue(int64_t v) : one_word(v) {} explicit SavedValue(flags_internal::AlignedTwoWords v) : two_words(v) {} - void* dynamic; + void* heap_allocated; int64_t one_word; flags_internal::AlignedTwoWords two_words; } value_; @@ -128,25 +128,33 @@ class FlagState : public flags_internal::FlagStateInterface { /////////////////////////////////////////////////////////////////////////////// // Flag implementation, which does not depend on flag value type. +DynValueDeleter::DynValueDeleter(FlagOpFn op_arg) : op(op_arg) {} + +void DynValueDeleter::operator()(void* ptr) const { + if (op == nullptr) return; + + Delete(op, ptr); +} + void FlagImpl::Init() { new (&data_guard_) absl::Mutex; // At this point the default_value_ always points to gen_func. - std::unique_ptr<void, DynValueDeleter> init_value( - (*default_value_.gen_func)(), DynValueDeleter{op_}); switch (ValueStorageKind()) { - case FlagValueStorageKind::kHeapAllocated: - HeapAllocatedValue() = init_value.release(); + case FlagValueStorageKind::kAlignedBuffer: + (*default_value_.gen_func)(AlignedBufferValue()); break; case FlagValueStorageKind::kOneWordAtomic: { - int64_t atomic_value; - std::memcpy(&atomic_value, init_value.get(), Sizeof(op_)); - OneWordValue().store(atomic_value, std::memory_order_release); + alignas(int64_t) std::array<char, sizeof(int64_t)> buf{}; + (*default_value_.gen_func)(buf.data()); + auto value = absl::bit_cast<int64_t>(buf); + OneWordValue().store(value, std::memory_order_release); break; } case FlagValueStorageKind::kTwoWordsAtomic: { - AlignedTwoWords atomic_value{0, 0}; - std::memcpy(&atomic_value, init_value.get(), Sizeof(op_)); + alignas(AlignedTwoWords) std::array<char, sizeof(AlignedTwoWords)> buf{}; + (*default_value_.gen_func)(buf.data()); + auto atomic_value = absl::bit_cast<AlignedTwoWords>(buf); TwoWordsValue().store(atomic_value, std::memory_order_release); break; } @@ -191,15 +199,16 @@ std::unique_ptr<void, DynValueDeleter> FlagImpl::MakeInitValue() const { if (DefaultKind() == FlagDefaultKind::kDynamicValue) { res = flags_internal::Clone(op_, default_value_.dynamic_value); } else { - res = (*default_value_.gen_func)(); + res = flags_internal::Alloc(op_); + (*default_value_.gen_func)(res); } return {res, DynValueDeleter{op_}}; } void FlagImpl::StoreValue(const void* src) { switch (ValueStorageKind()) { - case FlagValueStorageKind::kHeapAllocated: - Copy(op_, src, HeapAllocatedValue()); + case FlagValueStorageKind::kAlignedBuffer: + Copy(op_, src, AlignedBufferValue()); break; case FlagValueStorageKind::kOneWordAtomic: { int64_t one_word_val = 0; @@ -257,9 +266,9 @@ std::string FlagImpl::DefaultValue() const { std::string FlagImpl::CurrentValue() const { auto* guard = DataGuard(); // Make sure flag initialized switch (ValueStorageKind()) { - case FlagValueStorageKind::kHeapAllocated: { + case FlagValueStorageKind::kAlignedBuffer: { absl::MutexLock l(guard); - return flags_internal::Unparse(op_, HeapAllocatedValue()); + return flags_internal::Unparse(op_, AlignedBufferValue()); } case FlagValueStorageKind::kOneWordAtomic: { const auto one_word_val = @@ -318,9 +327,9 @@ std::unique_ptr<FlagStateInterface> FlagImpl::SaveState() { bool modified = modified_; bool on_command_line = on_command_line_; switch (ValueStorageKind()) { - case FlagValueStorageKind::kHeapAllocated: { + case FlagValueStorageKind::kAlignedBuffer: { return absl::make_unique<FlagState>( - this, flags_internal::Clone(op_, HeapAllocatedValue()), modified, + this, flags_internal::Clone(op_, AlignedBufferValue()), modified, on_command_line, counter_); } case FlagValueStorageKind::kOneWordAtomic: { @@ -345,8 +354,8 @@ bool FlagImpl::RestoreState(const FlagState& flag_state) { } switch (ValueStorageKind()) { - case FlagValueStorageKind::kHeapAllocated: - StoreValue(flag_state.value_.dynamic); + case FlagValueStorageKind::kAlignedBuffer: + StoreValue(flag_state.value_.heap_allocated); break; case FlagValueStorageKind::kOneWordAtomic: StoreValue(&flag_state.value_.one_word); @@ -363,25 +372,27 @@ bool FlagImpl::RestoreState(const FlagState& flag_state) { } template <typename StorageT> -typename StorageT::value_type& FlagImpl::OffsetValue() const { +StorageT* FlagImpl::OffsetValue() const { char* p = reinterpret_cast<char*>(const_cast<FlagImpl*>(this)); // The offset is deduced via Flag value type specific op_. size_t offset = flags_internal::ValueOffset(op_); - return reinterpret_cast<StorageT*>(p + offset)->value; + return reinterpret_cast<StorageT*>(p + offset); } -void*& FlagImpl::HeapAllocatedValue() const { - assert(ValueStorageKind() == FlagValueStorageKind::kHeapAllocated); - return OffsetValue<FlagHeapAllocatedValue>(); +void* FlagImpl::AlignedBufferValue() const { + assert(ValueStorageKind() == FlagValueStorageKind::kAlignedBuffer); + return OffsetValue<void>(); } + std::atomic<int64_t>& FlagImpl::OneWordValue() const { assert(ValueStorageKind() == FlagValueStorageKind::kOneWordAtomic); - return OffsetValue<FlagOneWordValue>(); + return OffsetValue<FlagOneWordValue>()->value; } + std::atomic<AlignedTwoWords>& FlagImpl::TwoWordsValue() const { assert(ValueStorageKind() == FlagValueStorageKind::kTwoWordsAtomic); - return OffsetValue<FlagTwoWordsValue>(); + return OffsetValue<FlagTwoWordsValue>()->value; } // Attempts to parse supplied `value` string using parsing routine in the `flag` @@ -406,9 +417,9 @@ std::unique_ptr<void, DynValueDeleter> FlagImpl::TryParse( void FlagImpl::Read(void* dst) const { auto* guard = DataGuard(); // Make sure flag initialized switch (ValueStorageKind()) { - case FlagValueStorageKind::kHeapAllocated: { + case FlagValueStorageKind::kAlignedBuffer: { absl::MutexLock l(guard); - flags_internal::CopyConstruct(op_, HeapAllocatedValue(), dst); + flags_internal::CopyConstruct(op_, AlignedBufferValue(), dst); break; } case FlagValueStorageKind::kOneWordAtomic: { diff --git a/absl/flags/internal/flag.h b/absl/flags/internal/flag.h index ae42dedcd633..2ae3dce3d13d 100644 --- a/absl/flags/internal/flag.h +++ b/absl/flags/internal/flag.h @@ -46,8 +46,8 @@ namespace flags_internal { // by function specific to that type with a signature matching FlagOpFn. enum class FlagOp { + kAlloc, kDelete, - kClone, kCopy, kCopyConstruct, kSizeof, @@ -63,13 +63,13 @@ using FlagOpFn = void* (*)(FlagOp, const void*, void*, void*); template <typename T> void* FlagOps(FlagOp op, const void* v1, void* v2, void* v3); -// Deletes memory interpreting obj as flag value type pointer. -inline void Delete(FlagOpFn op, const void* obj) { - op(FlagOp::kDelete, obj, nullptr, nullptr); +// Allocate aligned memory for a flag value. +inline void* Alloc(FlagOpFn op) { + return op(FlagOp::kAlloc, nullptr, nullptr, nullptr); } -// Makes a copy of flag value pointed by obj. -inline void* Clone(FlagOpFn op, const void* obj) { - return op(FlagOp::kClone, obj, nullptr, nullptr); +// Deletes memory interpreting obj as flag value type pointer. +inline void Delete(FlagOpFn op, void* obj) { + op(FlagOp::kDelete, nullptr, obj, nullptr); } // Copies src to dst interpreting as flag value type pointers. inline void Copy(FlagOpFn op, const void* src, void* dst) { @@ -80,6 +80,12 @@ inline void Copy(FlagOpFn op, const void* src, void* dst) { inline void CopyConstruct(FlagOpFn op, const void* src, void* dst) { op(FlagOp::kCopyConstruct, src, dst, nullptr); } +// Makes a copy of flag value pointed by obj. +inline void* Clone(FlagOpFn op, const void* obj) { + void* res = flags_internal::Alloc(op); + flags_internal::CopyConstruct(op, obj, res); + return res; +} // Returns true if parsing of input text is successfull. inline bool Parse(FlagOpFn op, absl::string_view text, void* dst, std::string* error) { @@ -195,7 +201,7 @@ constexpr FlagHelpArg HelpArg(char) { // Signature for the function generating the initial flag value (usually // based on default value supplied in flag's definition) -using FlagDfltGenFunc = void* (*)(); +using FlagDfltGenFunc = void (*)(void*); union FlagDefaultSrc { constexpr explicit FlagDefaultSrc(FlagDfltGenFunc gen_func_arg) @@ -253,46 +259,36 @@ using FlagUseTwoWordsStorage = #endif template <typename T> -using FlagUseHeapStorage = +using FlagUseBufferStorage = std::integral_constant<bool, !FlagUseOneWordStorage<T>::value && !FlagUseTwoWordsStorage<T>::value>; enum class FlagValueStorageKind : uint8_t { - kHeapAllocated = 0, + kAlignedBuffer = 0, kOneWordAtomic = 1, kTwoWordsAtomic = 2 }; template <typename T> static constexpr FlagValueStorageKind StorageKind() { - return FlagUseHeapStorage<T>::value - ? FlagValueStorageKind::kHeapAllocated + return FlagUseBufferStorage<T>::value + ? FlagValueStorageKind::kAlignedBuffer : FlagUseOneWordStorage<T>::value ? FlagValueStorageKind::kOneWordAtomic - : FlagUseTwoWordsStorage<T>::value - ? FlagValueStorageKind::kTwoWordsAtomic - : FlagValueStorageKind::kHeapAllocated; + : FlagValueStorageKind::kTwoWordsAtomic; } -struct FlagHeapAllocatedValue { - using value_type = void*; - - value_type value; -}; - struct FlagOneWordValue { - using value_type = std::atomic<int64_t>; constexpr FlagOneWordValue() : value(UninitializedFlagValue()) {} - value_type value; + std::atomic<int64_t> value; }; struct FlagTwoWordsValue { - using value_type = std::atomic<AlignedTwoWords>; constexpr FlagTwoWordsValue() : value(AlignedTwoWords{UninitializedFlagValue(), 0}) {} - value_type value; + std::atomic<AlignedTwoWords> value; }; template <typename T, @@ -300,9 +296,10 @@ template <typename T, struct FlagValue; template <typename T> -struct FlagValue<T, FlagValueStorageKind::kHeapAllocated> - : FlagHeapAllocatedValue { +struct FlagValue<T, FlagValueStorageKind::kAlignedBuffer> { bool Get(T*) const { return false; } + + alignas(T) char value[sizeof(T)]; }; template <typename T> @@ -347,10 +344,8 @@ struct FlagCallback { // The class encapsulates the Flag's data and access to it. struct DynValueDeleter { - explicit DynValueDeleter(FlagOpFn op_arg = nullptr) : op(op_arg) {} - void operator()(void* ptr) const { - if (op != nullptr) flags_internal::Delete(op, ptr); - } + explicit DynValueDeleter(FlagOpFn op_arg = nullptr); + void operator()(void* ptr) const; FlagOpFn op; }; @@ -416,10 +411,10 @@ class FlagImpl final : public flags_internal::CommandLineFlag { // it is only used inside the three routines below, which are defined in // flag.cc, we can define it in that file as well. template <typename StorageT> - typename StorageT::value_type& OffsetValue() const; - // This is an accessor for a value stored in heap allocated storage. - // Returns a mutable reference to a pointer to allow vlaue mutation. - void*& HeapAllocatedValue() const; + StorageT* OffsetValue() const; + // This is an accessor for a value stored in an aligned buffer storage. + // Returns a mutable pointer to the start of a buffer. + void* AlignedBufferValue() const; // This is an accessor for a value stored as one word atomic. Returns a // mutable reference to an atomic value. std::atomic<int64_t>& OneWordValue() const; @@ -492,17 +487,8 @@ class FlagImpl final : public flags_internal::CommandLineFlag { // Kind of storage this flag is using for the flag's value. const uint8_t value_storage_kind_ : 2; - // ------------------------------------------------------------------------ - // The bytes containing the const bitfields must not be shared with bytes - // containing the mutable bitfields. - // ------------------------------------------------------------------------ - - // Unique tag for absl::call_once call to initialize this flag. - // - // The placement of this variable between the immutable and mutable bitfields - // is important as prevents them from occupying the same byte. If you remove - // this variable, make sure to maintain this property. - absl::once_flag init_control_; + uint8_t : 0; // The bytes containing the const bitfields must not be + // shared with bytes containing the mutable bitfields. // Mutable flag's state (guarded by `data_guard_`). @@ -514,6 +500,9 @@ class FlagImpl final : public flags_internal::CommandLineFlag { // Has this flag been specified on command line. bool on_command_line_ : 1 ABSL_GUARDED_BY(*DataGuard()); + // Unique tag for absl::call_once call to initialize this flag. + absl::once_flag init_control_; + // Mutation counter int64_t counter_ ABSL_GUARDED_BY(*DataGuard()); // Optional flag's callback and absl::Mutex to guard the invocations. @@ -600,11 +589,17 @@ class Flag { template <typename T> void* FlagOps(FlagOp op, const void* v1, void* v2, void* v3) { switch (op) { - case FlagOp::kDelete: - delete static_cast<const T*>(v1); + case FlagOp::kAlloc: { + std::allocator<T> alloc; + return std::allocator_traits<std::allocator<T>>::allocate(alloc, 1); + } + case FlagOp::kDelete: { + T* p = static_cast<T*>(v2); + p->~T(); + std::allocator<T> alloc; + std::allocator_traits<std::allocator<T>>::deallocate(alloc, p, 1); return nullptr; - case FlagOp::kClone: - return new T(*static_cast<const T*>(v1)); + } case FlagOp::kCopy: *static_cast<T*>(v2) = *static_cast<const T*>(v1); return nullptr; @@ -675,13 +670,13 @@ class FlagRegistrar { struct EmptyBraces {}; template <typename T> -T* MakeFromDefaultValue(T t) { - return new T(std::move(t)); +void MakeFromDefaultValue(void* dst, T t) { + new (dst) T(std::move(t)); } template <typename T> -T* MakeFromDefaultValue(EmptyBraces) { - return new T{}; +void MakeFromDefaultValue(void* dst, EmptyBraces) { + new (dst) T{}; } } // namespace flags_internal |