diff options
Diffstat (limited to 'absl/flags/internal')
-rw-r--r-- | absl/flags/internal/flag.cc | 73 | ||||
-rw-r--r-- | absl/flags/internal/flag.h | 103 |
2 files changed, 91 insertions, 85 deletions
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 |