summaryrefslogtreecommitdiff
path: root/abseil-cpp/absl/flags/internal
diff options
context:
space:
mode:
Diffstat (limited to 'abseil-cpp/absl/flags/internal')
-rw-r--r--abseil-cpp/absl/flags/internal/commandlineflag.cc2
-rw-r--r--abseil-cpp/absl/flags/internal/commandlineflag.h2
-rw-r--r--abseil-cpp/absl/flags/internal/flag.cc181
-rw-r--r--abseil-cpp/absl/flags/internal/flag.h202
-rw-r--r--abseil-cpp/absl/flags/internal/flag_msvc.inc116
-rw-r--r--abseil-cpp/absl/flags/internal/parse.h21
-rw-r--r--abseil-cpp/absl/flags/internal/registry.h7
-rw-r--r--abseil-cpp/absl/flags/internal/sequence_lock.h187
-rw-r--r--abseil-cpp/absl/flags/internal/sequence_lock_test.cc169
-rw-r--r--abseil-cpp/absl/flags/internal/usage.cc319
-rw-r--r--abseil-cpp/absl/flags/internal/usage.h63
-rw-r--r--abseil-cpp/absl/flags/internal/usage_test.cc221
12 files changed, 1182 insertions, 308 deletions
diff --git a/abseil-cpp/absl/flags/internal/commandlineflag.cc b/abseil-cpp/absl/flags/internal/commandlineflag.cc
index 4482955..3c114d1 100644
--- a/abseil-cpp/absl/flags/internal/commandlineflag.cc
+++ b/abseil-cpp/absl/flags/internal/commandlineflag.cc
@@ -19,7 +19,7 @@ namespace absl {
ABSL_NAMESPACE_BEGIN
namespace flags_internal {
-FlagStateInterface::~FlagStateInterface() {}
+FlagStateInterface::~FlagStateInterface() = default;
} // namespace flags_internal
ABSL_NAMESPACE_END
diff --git a/abseil-cpp/absl/flags/internal/commandlineflag.h b/abseil-cpp/absl/flags/internal/commandlineflag.h
index cb46fe2..ebfe81b 100644
--- a/abseil-cpp/absl/flags/internal/commandlineflag.h
+++ b/abseil-cpp/absl/flags/internal/commandlineflag.h
@@ -24,7 +24,7 @@ ABSL_NAMESPACE_BEGIN
namespace flags_internal {
// An alias for flag fast type id. This value identifies the flag value type
-// simialarly to typeid(T), without relying on RTTI being available. In most
+// similarly to typeid(T), without relying on RTTI being available. In most
// cases this id is enough to uniquely identify the flag's value type. In a few
// cases we'll have to resort to using actual RTTI implementation if it is
// available.
diff --git a/abseil-cpp/absl/flags/internal/flag.cc b/abseil-cpp/absl/flags/internal/flag.cc
index 1502e7f..65d0e58 100644
--- a/abseil-cpp/absl/flags/internal/flag.cc
+++ b/abseil-cpp/absl/flags/internal/flag.cc
@@ -30,6 +30,7 @@
#include "absl/base/call_once.h"
#include "absl/base/casts.h"
#include "absl/base/config.h"
+#include "absl/base/dynamic_annotations.h"
#include "absl/base/optimization.h"
#include "absl/flags/config.h"
#include "absl/flags/internal/commandlineflag.h"
@@ -96,7 +97,8 @@ class FlagState : public flags_internal::FlagStateInterface {
counter_(counter) {}
~FlagState() override {
- if (flag_impl_.ValueStorageKind() != FlagValueStorageKind::kAlignedBuffer)
+ if (flag_impl_.ValueStorageKind() != FlagValueStorageKind::kAlignedBuffer &&
+ flag_impl_.ValueStorageKind() != FlagValueStorageKind::kSequenceLocked)
return;
flags_internal::Delete(flag_impl_.op_, value_.heap_allocated);
}
@@ -118,11 +120,9 @@ class FlagState : public flags_internal::FlagStateInterface {
union SavedValue {
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* heap_allocated;
int64_t one_word;
- flags_internal::AlignedTwoWords two_words;
} value_;
bool modified_;
bool on_command_line_;
@@ -146,12 +146,7 @@ void FlagImpl::Init() {
auto def_kind = static_cast<FlagDefaultKind>(def_kind_);
switch (ValueStorageKind()) {
- case FlagValueStorageKind::kAlignedBuffer:
- // For this storage kind the default_value_ always points to gen_func
- // during initialization.
- assert(def_kind == FlagDefaultKind::kGenFunc);
- (*default_value_.gen_func)(AlignedBufferValue());
- break;
+ case FlagValueStorageKind::kValueAndInitBit:
case FlagValueStorageKind::kOneWordAtomic: {
alignas(int64_t) std::array<char, sizeof(int64_t)> buf{};
if (def_kind == FlagDefaultKind::kGenFunc) {
@@ -160,21 +155,33 @@ void FlagImpl::Init() {
assert(def_kind != FlagDefaultKind::kDynamicValue);
std::memcpy(buf.data(), &default_value_, Sizeof(op_));
}
+ if (ValueStorageKind() == FlagValueStorageKind::kValueAndInitBit) {
+ // We presume here the memory layout of FlagValueAndInitBit struct.
+ uint8_t initialized = 1;
+ std::memcpy(buf.data() + Sizeof(op_), &initialized,
+ sizeof(initialized));
+ }
+ // Type can contain valid uninitialized bits, e.g. padding.
+ ABSL_ANNOTATE_MEMORY_IS_INITIALIZED(buf.data(), buf.size());
OneWordValue().store(absl::bit_cast<int64_t>(buf),
std::memory_order_release);
break;
}
- case FlagValueStorageKind::kTwoWordsAtomic: {
+ case FlagValueStorageKind::kSequenceLocked: {
// For this storage kind the default_value_ always points to gen_func
// during initialization.
assert(def_kind == FlagDefaultKind::kGenFunc);
- 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);
+ (*default_value_.gen_func)(AtomicBufferValue());
break;
}
+ case FlagValueStorageKind::kAlignedBuffer:
+ // For this storage kind the default_value_ always points to gen_func
+ // during initialization.
+ assert(def_kind == FlagDefaultKind::kGenFunc);
+ (*default_value_.gen_func)(AlignedBufferValue());
+ break;
}
+ seq_lock_.MarkInitialized();
}
absl::Mutex* FlagImpl::DataGuard() const {
@@ -190,7 +197,7 @@ void FlagImpl::AssertValidType(FlagFastTypeId rhs_type_id,
FlagFastTypeId lhs_type_id = flags_internal::FastTypeId(op_);
// `rhs_type_id` is the fast type id corresponding to the declaration
- // visibile at the call site. `lhs_type_id` is the fast type id
+ // visible at the call site. `lhs_type_id` is the fast type id
// corresponding to the type specified in flag definition. They must match
// for this operation to be well-defined.
if (ABSL_PREDICT_TRUE(lhs_type_id == rhs_type_id)) return;
@@ -201,7 +208,7 @@ void FlagImpl::AssertValidType(FlagFastTypeId rhs_type_id,
if (lhs_runtime_type_id == rhs_runtime_type_id) return;
-#if defined(ABSL_FLAGS_INTERNAL_HAS_RTTI)
+#ifdef ABSL_INTERNAL_HAS_RTTI
if (*lhs_runtime_type_id == *rhs_runtime_type_id) return;
#endif
@@ -229,25 +236,25 @@ std::unique_ptr<void, DynValueDeleter> FlagImpl::MakeInitValue() const {
void FlagImpl::StoreValue(const void* src) {
switch (ValueStorageKind()) {
- case FlagValueStorageKind::kAlignedBuffer:
- Copy(op_, src, AlignedBufferValue());
- break;
+ case FlagValueStorageKind::kValueAndInitBit:
case FlagValueStorageKind::kOneWordAtomic: {
- int64_t one_word_val = 0;
+ // Load the current value to avoid setting 'init' bit manually.
+ int64_t one_word_val = OneWordValue().load(std::memory_order_acquire);
std::memcpy(&one_word_val, src, Sizeof(op_));
OneWordValue().store(one_word_val, std::memory_order_release);
+ seq_lock_.IncrementModificationCount();
break;
}
- case FlagValueStorageKind::kTwoWordsAtomic: {
- AlignedTwoWords two_words_val{0, 0};
- std::memcpy(&two_words_val, src, Sizeof(op_));
- TwoWordsValue().store(two_words_val, std::memory_order_release);
+ case FlagValueStorageKind::kSequenceLocked: {
+ seq_lock_.Write(AtomicBufferValue(), src, Sizeof(op_));
break;
}
+ case FlagValueStorageKind::kAlignedBuffer:
+ Copy(op_, src, AlignedBufferValue());
+ seq_lock_.IncrementModificationCount();
+ break;
}
-
modified_ = true;
- ++counter_;
InvokeCallback();
}
@@ -266,6 +273,10 @@ FlagFastTypeId FlagImpl::TypeId() const {
return flags_internal::FastTypeId(op_);
}
+int64_t FlagImpl::ModificationCount() const {
+ return seq_lock_.ModificationCount();
+}
+
bool FlagImpl::IsSpecifiedOnCommandLine() const {
absl::MutexLock l(DataGuard());
return on_command_line_;
@@ -281,21 +292,22 @@ std::string FlagImpl::DefaultValue() const {
std::string FlagImpl::CurrentValue() const {
auto* guard = DataGuard(); // Make sure flag initialized
switch (ValueStorageKind()) {
- case FlagValueStorageKind::kAlignedBuffer: {
- absl::MutexLock l(guard);
- return flags_internal::Unparse(op_, AlignedBufferValue());
- }
+ case FlagValueStorageKind::kValueAndInitBit:
case FlagValueStorageKind::kOneWordAtomic: {
const auto one_word_val =
absl::bit_cast<std::array<char, sizeof(int64_t)>>(
OneWordValue().load(std::memory_order_acquire));
return flags_internal::Unparse(op_, one_word_val.data());
}
- case FlagValueStorageKind::kTwoWordsAtomic: {
- const auto two_words_val =
- absl::bit_cast<std::array<char, sizeof(AlignedTwoWords)>>(
- TwoWordsValue().load(std::memory_order_acquire));
- return flags_internal::Unparse(op_, two_words_val.data());
+ case FlagValueStorageKind::kSequenceLocked: {
+ std::unique_ptr<void, DynValueDeleter> cloned(flags_internal::Alloc(op_),
+ DynValueDeleter{op_});
+ ReadSequenceLockedData(cloned.get());
+ return flags_internal::Unparse(op_, cloned.get());
+ }
+ case FlagValueStorageKind::kAlignedBuffer: {
+ absl::MutexLock l(guard);
+ return flags_internal::Unparse(op_, AlignedBufferValue());
}
}
@@ -342,20 +354,26 @@ std::unique_ptr<FlagStateInterface> FlagImpl::SaveState() {
bool modified = modified_;
bool on_command_line = on_command_line_;
switch (ValueStorageKind()) {
- case FlagValueStorageKind::kAlignedBuffer: {
- return absl::make_unique<FlagState>(
- *this, flags_internal::Clone(op_, AlignedBufferValue()), modified,
- on_command_line, counter_);
- }
+ case FlagValueStorageKind::kValueAndInitBit:
case FlagValueStorageKind::kOneWordAtomic: {
return absl::make_unique<FlagState>(
*this, OneWordValue().load(std::memory_order_acquire), modified,
- on_command_line, counter_);
+ on_command_line, ModificationCount());
+ }
+ case FlagValueStorageKind::kSequenceLocked: {
+ void* cloned = flags_internal::Alloc(op_);
+ // Read is guaranteed to be successful because we hold the lock.
+ bool success =
+ seq_lock_.TryRead(cloned, AtomicBufferValue(), Sizeof(op_));
+ assert(success);
+ static_cast<void>(success);
+ return absl::make_unique<FlagState>(*this, cloned, modified,
+ on_command_line, ModificationCount());
}
- case FlagValueStorageKind::kTwoWordsAtomic: {
+ case FlagValueStorageKind::kAlignedBuffer: {
return absl::make_unique<FlagState>(
- *this, TwoWordsValue().load(std::memory_order_acquire), modified,
- on_command_line, counter_);
+ *this, flags_internal::Clone(op_, AlignedBufferValue()), modified,
+ on_command_line, ModificationCount());
}
}
return nullptr;
@@ -363,20 +381,18 @@ std::unique_ptr<FlagStateInterface> FlagImpl::SaveState() {
bool FlagImpl::RestoreState(const FlagState& flag_state) {
absl::MutexLock l(DataGuard());
-
- if (flag_state.counter_ == counter_) {
+ if (flag_state.counter_ == ModificationCount()) {
return false;
}
switch (ValueStorageKind()) {
- case FlagValueStorageKind::kAlignedBuffer:
- StoreValue(flag_state.value_.heap_allocated);
- break;
+ case FlagValueStorageKind::kValueAndInitBit:
case FlagValueStorageKind::kOneWordAtomic:
StoreValue(&flag_state.value_.one_word);
break;
- case FlagValueStorageKind::kTwoWordsAtomic:
- StoreValue(&flag_state.value_.two_words);
+ case FlagValueStorageKind::kSequenceLocked:
+ case FlagValueStorageKind::kAlignedBuffer:
+ StoreValue(flag_state.value_.heap_allocated);
break;
}
@@ -390,7 +406,7 @@ template <typename StorageT>
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_);
+ ptrdiff_t offset = flags_internal::ValueOffset(op_);
return reinterpret_cast<StorageT*>(p + offset);
}
@@ -400,14 +416,15 @@ void* FlagImpl::AlignedBufferValue() const {
return OffsetValue<void>();
}
-std::atomic<int64_t>& FlagImpl::OneWordValue() const {
- assert(ValueStorageKind() == FlagValueStorageKind::kOneWordAtomic);
- return OffsetValue<FlagOneWordValue>()->value;
+std::atomic<uint64_t>* FlagImpl::AtomicBufferValue() const {
+ assert(ValueStorageKind() == FlagValueStorageKind::kSequenceLocked);
+ return OffsetValue<std::atomic<uint64_t>>();
}
-std::atomic<AlignedTwoWords>& FlagImpl::TwoWordsValue() const {
- assert(ValueStorageKind() == FlagValueStorageKind::kTwoWordsAtomic);
- return OffsetValue<FlagTwoWordsValue>()->value;
+std::atomic<int64_t>& FlagImpl::OneWordValue() const {
+ assert(ValueStorageKind() == FlagValueStorageKind::kOneWordAtomic ||
+ ValueStorageKind() == FlagValueStorageKind::kValueAndInitBit);
+ return OffsetValue<FlagOneWordValue>()->value;
}
// Attempts to parse supplied `value` string using parsing routine in the `flag`
@@ -432,26 +449,56 @@ std::unique_ptr<void, DynValueDeleter> FlagImpl::TryParse(
void FlagImpl::Read(void* dst) const {
auto* guard = DataGuard(); // Make sure flag initialized
switch (ValueStorageKind()) {
- case FlagValueStorageKind::kAlignedBuffer: {
- absl::MutexLock l(guard);
- flags_internal::CopyConstruct(op_, AlignedBufferValue(), dst);
- break;
- }
+ case FlagValueStorageKind::kValueAndInitBit:
case FlagValueStorageKind::kOneWordAtomic: {
const int64_t one_word_val =
OneWordValue().load(std::memory_order_acquire);
std::memcpy(dst, &one_word_val, Sizeof(op_));
break;
}
- case FlagValueStorageKind::kTwoWordsAtomic: {
- const AlignedTwoWords two_words_val =
- TwoWordsValue().load(std::memory_order_acquire);
- std::memcpy(dst, &two_words_val, Sizeof(op_));
+ case FlagValueStorageKind::kSequenceLocked: {
+ ReadSequenceLockedData(dst);
+ break;
+ }
+ case FlagValueStorageKind::kAlignedBuffer: {
+ absl::MutexLock l(guard);
+ flags_internal::CopyConstruct(op_, AlignedBufferValue(), dst);
break;
}
}
}
+int64_t FlagImpl::ReadOneWord() const {
+ assert(ValueStorageKind() == FlagValueStorageKind::kOneWordAtomic ||
+ ValueStorageKind() == FlagValueStorageKind::kValueAndInitBit);
+ auto* guard = DataGuard(); // Make sure flag initialized
+ (void)guard;
+ return OneWordValue().load(std::memory_order_acquire);
+}
+
+bool FlagImpl::ReadOneBool() const {
+ assert(ValueStorageKind() == FlagValueStorageKind::kValueAndInitBit);
+ auto* guard = DataGuard(); // Make sure flag initialized
+ (void)guard;
+ return absl::bit_cast<FlagValueAndInitBit<bool>>(
+ OneWordValue().load(std::memory_order_acquire))
+ .value;
+}
+
+void FlagImpl::ReadSequenceLockedData(void* dst) const {
+ size_t size = Sizeof(op_);
+ // Attempt to read using the sequence lock.
+ if (ABSL_PREDICT_TRUE(seq_lock_.TryRead(dst, AtomicBufferValue(), size))) {
+ return;
+ }
+ // We failed due to contention. Acquire the lock to prevent contention
+ // and try again.
+ absl::ReaderMutexLock l(DataGuard());
+ bool success = seq_lock_.TryRead(dst, AtomicBufferValue(), size);
+ assert(success);
+ static_cast<void>(success);
+}
+
void FlagImpl::Write(const void* src) {
absl::MutexLock l(DataGuard());
diff --git a/abseil-cpp/absl/flags/internal/flag.h b/abseil-cpp/absl/flags/internal/flag.h
index 370d8a0..b41f9a6 100644
--- a/abseil-cpp/absl/flags/internal/flag.h
+++ b/abseil-cpp/absl/flags/internal/flag.h
@@ -29,6 +29,7 @@
#include "absl/base/attributes.h"
#include "absl/base/call_once.h"
+#include "absl/base/casts.h"
#include "absl/base/config.h"
#include "absl/base/optimization.h"
#include "absl/base/thread_annotations.h"
@@ -36,6 +37,7 @@
#include "absl/flags/config.h"
#include "absl/flags/internal/commandlineflag.h"
#include "absl/flags/internal/registry.h"
+#include "absl/flags/internal/sequence_lock.h"
#include "absl/flags/marshalling.h"
#include "absl/meta/type_traits.h"
#include "absl/strings/string_view.h"
@@ -119,7 +121,7 @@ inline void* Clone(FlagOpFn op, const void* obj) {
flags_internal::CopyConstruct(op, obj, res);
return res;
}
-// Returns true if parsing of input text is successfull.
+// Returns true if parsing of input text is successful.
inline bool Parse(FlagOpFn op, absl::string_view text, void* dst,
std::string* error) {
return op(FlagOp::kParse, &text, dst, error) != nullptr;
@@ -137,12 +139,12 @@ inline size_t Sizeof(FlagOpFn op) {
return static_cast<size_t>(reinterpret_cast<intptr_t>(
op(FlagOp::kSizeof, nullptr, nullptr, nullptr)));
}
-// Returns fast type id coresponding to the value type.
+// Returns fast type id corresponding to the value type.
inline FlagFastTypeId FastTypeId(FlagOpFn op) {
return reinterpret_cast<FlagFastTypeId>(
op(FlagOp::kFastTypeId, nullptr, nullptr, nullptr));
}
-// Returns fast type id coresponding to the value type.
+// Returns fast type id corresponding to the value type.
inline const std::type_info* RuntimeTypeId(FlagOpFn op) {
return reinterpret_cast<const std::type_info*>(
op(FlagOp::kRuntimeTypeId, nullptr, nullptr, nullptr));
@@ -161,7 +163,7 @@ inline ptrdiff_t ValueOffset(FlagOpFn op) {
// Returns an address of RTTI's typeid(T).
template <typename T>
inline const std::type_info* GenRuntimeTypeId() {
-#if defined(ABSL_FLAGS_INTERNAL_HAS_RTTI)
+#ifdef ABSL_INTERNAL_HAS_RTTI
return &typeid(T);
#else
return nullptr;
@@ -221,12 +223,12 @@ extern const char kStrippedFlagHelp[];
// first overload if possible. If help message is evaluatable on constexpr
// context We'll be able to make FixedCharArray out of it and we'll choose first
// overload. In this case the help message expression is immediately evaluated
-// and is used to construct the absl::Flag. No additionl code is generated by
+// and is used to construct the absl::Flag. No additional code is generated by
// ABSL_FLAG Otherwise SFINAE kicks in and first overload is dropped from the
// consideration, in which case the second overload will be used. The second
// overload does not attempt to evaluate the help message expression
-// immediately and instead delays the evaluation by returing the function
-// pointer (&T::NonConst) genering the help message when necessary. This is
+// immediately and instead delays the evaluation by returning the function
+// pointer (&T::NonConst) generating the help message when necessary. This is
// evaluatable in constexpr context, but the cost is an extra function being
// generated in the ABSL_FLAG code.
template <typename Gen, size_t N>
@@ -288,7 +290,7 @@ constexpr T InitDefaultValue(EmptyBraces) {
template <typename ValueT, typename GenT,
typename std::enable_if<std::is_integral<ValueT>::value, int>::type =
- (GenT{}, 0)>
+ ((void)GenT{}, 0)>
constexpr FlagDefaultArg DefaultArg(int) {
return {FlagDefaultSrc(GenT{}.value), FlagDefaultKind::kOneWord};
}
@@ -301,79 +303,55 @@ constexpr FlagDefaultArg DefaultArg(char) {
///////////////////////////////////////////////////////////////////////////////
// Flag current value auxiliary structs.
-constexpr int64_t UninitializedFlagValue() { return 0xababababababababll; }
+constexpr int64_t UninitializedFlagValue() {
+ return static_cast<int64_t>(0xababababababababll);
+}
template <typename T>
-using FlagUseOneWordStorage = std::integral_constant<
- bool, absl::type_traits_internal::is_trivially_copyable<T>::value &&
- (sizeof(T) <= 8)>;
-
-#if defined(ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD)
-// Clang does not always produce cmpxchg16b instruction when alignment of a 16
-// bytes type is not 16.
-struct alignas(16) AlignedTwoWords {
- int64_t first;
- int64_t second;
-
- bool IsInitialized() const {
- return first != flags_internal::UninitializedFlagValue();
- }
-};
+using FlagUseValueAndInitBitStorage =
+ std::integral_constant<bool, std::is_trivially_copyable<T>::value &&
+ std::is_default_constructible<T>::value &&
+ (sizeof(T) < 8)>;
template <typename T>
-using FlagUseTwoWordsStorage = std::integral_constant<
- bool, absl::type_traits_internal::is_trivially_copyable<T>::value &&
- (sizeof(T) > 8) && (sizeof(T) <= 16)>;
-#else
-// This is actually unused and only here to avoid ifdefs in other palces.
-struct AlignedTwoWords {
- constexpr AlignedTwoWords() noexcept : dummy() {}
- constexpr AlignedTwoWords(int64_t, int64_t) noexcept : dummy() {}
- char dummy;
-
- bool IsInitialized() const {
- std::abort();
- return true;
- }
-};
+using FlagUseOneWordStorage =
+ std::integral_constant<bool, std::is_trivially_copyable<T>::value &&
+ (sizeof(T) <= 8)>;
-// This trait should be type dependent, otherwise SFINAE below will fail
-template <typename T>
-using FlagUseTwoWordsStorage =
- std::integral_constant<bool, sizeof(T) != sizeof(T)>;
-#endif
-
-template <typename T>
-using FlagUseBufferStorage =
- std::integral_constant<bool, !FlagUseOneWordStorage<T>::value &&
- !FlagUseTwoWordsStorage<T>::value>;
+template <class T>
+using FlagUseSequenceLockStorage =
+ std::integral_constant<bool, std::is_trivially_copyable<T>::value &&
+ (sizeof(T) > 8)>;
enum class FlagValueStorageKind : uint8_t {
- kAlignedBuffer = 0,
+ kValueAndInitBit = 0,
kOneWordAtomic = 1,
- kTwoWordsAtomic = 2
+ kSequenceLocked = 2,
+ kAlignedBuffer = 3,
};
template <typename T>
static constexpr FlagValueStorageKind StorageKind() {
- return FlagUseBufferStorage<T>::value
- ? FlagValueStorageKind::kAlignedBuffer
- : FlagUseOneWordStorage<T>::value
- ? FlagValueStorageKind::kOneWordAtomic
- : FlagValueStorageKind::kTwoWordsAtomic;
+ return FlagUseValueAndInitBitStorage<T>::value
+ ? FlagValueStorageKind::kValueAndInitBit
+ : FlagUseOneWordStorage<T>::value
+ ? FlagValueStorageKind::kOneWordAtomic
+ : FlagUseSequenceLockStorage<T>::value
+ ? FlagValueStorageKind::kSequenceLocked
+ : FlagValueStorageKind::kAlignedBuffer;
}
struct FlagOneWordValue {
- constexpr FlagOneWordValue() : value(UninitializedFlagValue()) {}
-
+ constexpr explicit FlagOneWordValue(int64_t v) : value(v) {}
std::atomic<int64_t> value;
};
-struct FlagTwoWordsValue {
- constexpr FlagTwoWordsValue()
- : value(AlignedTwoWords{UninitializedFlagValue(), 0}) {}
-
- std::atomic<AlignedTwoWords> value;
+template <typename T>
+struct alignas(8) FlagValueAndInitBit {
+ T value;
+ // Use an int instead of a bool to guarantee that a non-zero value has
+ // a bit set.
+ uint8_t init;
};
template <typename T,
@@ -381,15 +359,22 @@ template <typename T,
struct FlagValue;
template <typename T>
-struct FlagValue<T, FlagValueStorageKind::kAlignedBuffer> {
- bool Get(T&) const { return false; }
-
- alignas(T) char value[sizeof(T)];
+struct FlagValue<T, FlagValueStorageKind::kValueAndInitBit> : FlagOneWordValue {
+ constexpr FlagValue() : FlagOneWordValue(0) {}
+ bool Get(const SequenceLock&, T& dst) const {
+ int64_t storage = value.load(std::memory_order_acquire);
+ if (ABSL_PREDICT_FALSE(storage == 0)) {
+ return false;
+ }
+ dst = absl::bit_cast<FlagValueAndInitBit<T>>(storage).value;
+ return true;
+ }
};
template <typename T>
struct FlagValue<T, FlagValueStorageKind::kOneWordAtomic> : FlagOneWordValue {
- bool Get(T& dst) const {
+ constexpr FlagValue() : FlagOneWordValue(UninitializedFlagValue()) {}
+ bool Get(const SequenceLock&, T& dst) const {
int64_t one_word_val = value.load(std::memory_order_acquire);
if (ABSL_PREDICT_FALSE(one_word_val == UninitializedFlagValue())) {
return false;
@@ -400,15 +385,23 @@ struct FlagValue<T, FlagValueStorageKind::kOneWordAtomic> : FlagOneWordValue {
};
template <typename T>
-struct FlagValue<T, FlagValueStorageKind::kTwoWordsAtomic> : FlagTwoWordsValue {
- bool Get(T& dst) const {
- AlignedTwoWords two_words_val = value.load(std::memory_order_acquire);
- if (ABSL_PREDICT_FALSE(!two_words_val.IsInitialized())) {
- return false;
- }
- std::memcpy(&dst, static_cast<const void*>(&two_words_val), sizeof(T));
- return true;
+struct FlagValue<T, FlagValueStorageKind::kSequenceLocked> {
+ bool Get(const SequenceLock& lock, T& dst) const {
+ return lock.TryRead(&dst, value_words, sizeof(T));
}
+
+ static constexpr int kNumWords =
+ flags_internal::AlignUp(sizeof(T), sizeof(uint64_t)) / sizeof(uint64_t);
+
+ alignas(T) alignas(
+ std::atomic<uint64_t>) std::atomic<uint64_t> value_words[kNumWords];
+};
+
+template <typename T>
+struct FlagValue<T, FlagValueStorageKind::kAlignedBuffer> {
+ bool Get(const SequenceLock&, T&) const { return false; }
+
+ alignas(T) char value[sizeof(T)];
};
///////////////////////////////////////////////////////////////////////////////
@@ -451,13 +444,32 @@ class FlagImpl final : public CommandLineFlag {
def_kind_(static_cast<uint8_t>(default_arg.kind)),
modified_(false),
on_command_line_(false),
- counter_(0),
callback_(nullptr),
default_value_(default_arg.source),
data_guard_{} {}
// Constant access methods
+ int64_t ReadOneWord() const ABSL_LOCKS_EXCLUDED(*DataGuard());
+ bool ReadOneBool() const ABSL_LOCKS_EXCLUDED(*DataGuard());
void Read(void* dst) const override ABSL_LOCKS_EXCLUDED(*DataGuard());
+ void Read(bool* value) const ABSL_LOCKS_EXCLUDED(*DataGuard()) {
+ *value = ReadOneBool();
+ }
+ template <typename T,
+ absl::enable_if_t<flags_internal::StorageKind<T>() ==
+ FlagValueStorageKind::kOneWordAtomic,
+ int> = 0>
+ void Read(T* value) const ABSL_LOCKS_EXCLUDED(*DataGuard()) {
+ int64_t v = ReadOneWord();
+ std::memcpy(value, static_cast<const void*>(&v), sizeof(T));
+ }
+ template <typename T,
+ typename std::enable_if<flags_internal::StorageKind<T>() ==
+ FlagValueStorageKind::kValueAndInitBit,
+ int>::type = 0>
+ void Read(T* value) const ABSL_LOCKS_EXCLUDED(*DataGuard()) {
+ *value = absl::bit_cast<FlagValueAndInitBit<T>>(ReadOneWord()).value;
+ }
// Mutating access methods
void Write(const void* src) ABSL_LOCKS_EXCLUDED(*DataGuard());
@@ -498,15 +510,17 @@ class FlagImpl final : public CommandLineFlag {
// flag.cc, we can define it in that file as well.
template <typename StorageT>
StorageT* OffsetValue() const;
- // This is an accessor for a value stored in an aligned buffer storage.
+ // This is an accessor for a value stored in an aligned buffer storage
+ // used for non-trivially-copyable data types.
// Returns a mutable pointer to the start of a buffer.
void* AlignedBufferValue() const;
+
+ // The same as above, but used for sequencelock-protected storage.
+ std::atomic<uint64_t>* AtomicBufferValue() 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;
- // This is an accessor for a value stored as two words atomic. Returns a
- // mutable reference to an atomic value.
- std::atomic<AlignedTwoWords>& TwoWordsValue() const;
// Attempts to parse supplied `value` string. If parsing is successful,
// returns new value. Otherwise returns nullptr.
@@ -516,6 +530,12 @@ class FlagImpl final : public CommandLineFlag {
// Stores the flag value based on the pointer to the source.
void StoreValue(const void* src) ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard());
+ // Copy the flag data, protected by `seq_lock_` into `dst`.
+ //
+ // REQUIRES: ValueStorageKind() == kSequenceLocked.
+ void ReadSequenceLockedData(void* dst) const
+ ABSL_LOCKS_EXCLUDED(*DataGuard());
+
FlagHelpKind HelpSourceKind() const {
return static_cast<FlagHelpKind>(help_source_kind_);
}
@@ -541,6 +561,8 @@ class FlagImpl final : public CommandLineFlag {
void CheckDefaultValueParsingRoundtrip() const override
ABSL_LOCKS_EXCLUDED(*DataGuard());
+ int64_t ModificationCount() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard());
+
// 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.
@@ -587,8 +609,9 @@ class FlagImpl final : public CommandLineFlag {
// 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());
+ // Sequence lock / mutation counter.
+ flags_internal::SequenceLock seq_lock_;
+
// Optional flag's callback and absl::Mutex to guard the invocations.
FlagCallback* callback_ ABSL_GUARDED_BY(*DataGuard());
// Either a pointer to the function generating the default value based on the
@@ -649,7 +672,9 @@ class Flag {
impl_.AssertValidType(base_internal::FastTypeId<T>(), &GenRuntimeTypeId<T>);
#endif
- if (!value_.Get(u.value)) impl_.Read(&u.value);
+ if (ABSL_PREDICT_FALSE(!value_.Get(impl_.seq_lock_, u.value))) {
+ impl_.Read(&u.value);
+ }
return std::move(u.value);
}
void Set(const T& v) {
@@ -733,8 +758,8 @@ void* FlagOps(FlagOp op, const void* v1, void* v2, void* v3) {
case FlagOp::kValueOffset: {
// Round sizeof(FlagImp) to a multiple of alignof(FlagValue<T>) to get the
// offset of the data.
- ptrdiff_t round_to = alignof(FlagValue<T>);
- ptrdiff_t offset =
+ size_t round_to = alignof(FlagValue<T>);
+ size_t offset =
(sizeof(FlagImpl) + round_to - 1) / round_to * round_to;
return reinterpret_cast<void*>(offset);
}
@@ -750,8 +775,9 @@ struct FlagRegistrarEmpty {};
template <typename T, bool do_register>
class FlagRegistrar {
public:
- explicit FlagRegistrar(Flag<T>& flag) : flag_(flag) {
- if (do_register) flags_internal::RegisterCommandLineFlag(flag_.impl_);
+ explicit FlagRegistrar(Flag<T>& flag, const char* filename) : flag_(flag) {
+ if (do_register)
+ flags_internal::RegisterCommandLineFlag(flag_.impl_, filename);
}
FlagRegistrar OnUpdate(FlagCallbackFunc cb) && {
diff --git a/abseil-cpp/absl/flags/internal/flag_msvc.inc b/abseil-cpp/absl/flags/internal/flag_msvc.inc
new file mode 100644
index 0000000..614d09f
--- /dev/null
+++ b/abseil-cpp/absl/flags/internal/flag_msvc.inc
@@ -0,0 +1,116 @@
+//
+// Copyright 2021 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
+//
+// https://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.
+
+// Do not include this file directly.
+// Include absl/flags/flag.h instead.
+
+// MSVC debug builds do not implement initialization with constexpr constructors
+// correctly. To work around this we add a level of indirection, so that the
+// class `absl::Flag` contains an `internal::Flag*` (instead of being an alias
+// to that class) and dynamically allocates an instance when necessary. We also
+// forward all calls to internal::Flag methods via trampoline methods. In this
+// setup the `absl::Flag` class does not have constructor and virtual methods,
+// all the data members are public and thus MSVC is able to initialize it at
+// link time. To deal with multiple threads accessing the flag for the first
+// time concurrently we use an atomic boolean indicating if flag object is
+// initialized. We also employ the double-checked locking pattern where the
+// second level of protection is a global Mutex, so if two threads attempt to
+// construct the flag concurrently only one wins.
+//
+// This solution is based on a recommendation here:
+// https://developercommunity.visualstudio.com/content/problem/336946/class-with-constexpr-constructor-not-using-static.html?childToView=648454#comment-648454
+
+namespace flags_internal {
+absl::Mutex* GetGlobalConstructionGuard();
+} // namespace flags_internal
+
+// Public methods of `absl::Flag<T>` are NOT part of the Abseil Flags API.
+// See https://abseil.io/docs/cpp/guides/flags
+template <typename T>
+class Flag {
+ public:
+ // No constructor and destructor to ensure this is an aggregate type.
+ // Visual Studio 2015 still requires the constructor for class to be
+ // constexpr initializable.
+#if _MSC_VER <= 1900
+ constexpr Flag(const char* name, const char* filename,
+ const flags_internal::HelpGenFunc help_gen,
+ const flags_internal::FlagDfltGenFunc default_value_gen)
+ : name_(name),
+ filename_(filename),
+ help_gen_(help_gen),
+ default_value_gen_(default_value_gen),
+ inited_(false),
+ impl_(nullptr) {}
+#endif
+
+ flags_internal::Flag<T>& GetImpl() const {
+ if (!inited_.load(std::memory_order_acquire)) {
+ absl::MutexLock l(flags_internal::GetGlobalConstructionGuard());
+
+ if (inited_.load(std::memory_order_acquire)) {
+ return *impl_;
+ }
+
+ impl_ = new flags_internal::Flag<T>(
+ name_, filename_,
+ {flags_internal::FlagHelpMsg(help_gen_),
+ flags_internal::FlagHelpKind::kGenFunc},
+ {flags_internal::FlagDefaultSrc(default_value_gen_),
+ flags_internal::FlagDefaultKind::kGenFunc});
+ inited_.store(true, std::memory_order_release);
+ }
+
+ return *impl_;
+ }
+
+ // Public methods of `absl::Flag<T>` are NOT part of the Abseil Flags API.
+ // See https://abseil.io/docs/cpp/guides/flags
+ bool IsRetired() const { return GetImpl().IsRetired(); }
+ absl::string_view Name() const { return GetImpl().Name(); }
+ std::string Help() const { return GetImpl().Help(); }
+ bool IsModified() const { return GetImpl().IsModified(); }
+ bool IsSpecifiedOnCommandLine() const {
+ return GetImpl().IsSpecifiedOnCommandLine();
+ }
+ std::string Filename() const { return GetImpl().Filename(); }
+ std::string DefaultValue() const { return GetImpl().DefaultValue(); }
+ std::string CurrentValue() const { return GetImpl().CurrentValue(); }
+ template <typename U>
+ inline bool IsOfType() const {
+ return GetImpl().template IsOfType<U>();
+ }
+ T Get() const {
+ return flags_internal::FlagImplPeer::InvokeGet<T>(GetImpl());
+ }
+ void Set(const T& v) {
+ flags_internal::FlagImplPeer::InvokeSet(GetImpl(), v);
+ }
+ void InvokeCallback() { GetImpl().InvokeCallback(); }
+
+ const CommandLineFlag& Reflect() const {
+ return flags_internal::FlagImplPeer::InvokeReflect(GetImpl());
+ }
+
+ // The data members are logically private, but they need to be public for
+ // this to be an aggregate type.
+ const char* name_;
+ const char* filename_;
+ const flags_internal::HelpGenFunc help_gen_;
+ const flags_internal::FlagDfltGenFunc default_value_gen_;
+
+ mutable std::atomic<bool> inited_;
+ mutable flags_internal::Flag<T>* impl_;
+};
diff --git a/abseil-cpp/absl/flags/internal/parse.h b/abseil-cpp/absl/flags/internal/parse.h
index de706c8..10c531b 100644
--- a/abseil-cpp/absl/flags/internal/parse.h
+++ b/abseil-cpp/absl/flags/internal/parse.h
@@ -16,11 +16,14 @@
#ifndef ABSL_FLAGS_INTERNAL_PARSE_H_
#define ABSL_FLAGS_INTERNAL_PARSE_H_
+#include <iostream>
+#include <ostream>
#include <string>
#include <vector>
#include "absl/base/config.h"
#include "absl/flags/declare.h"
+#include "absl/flags/internal/usage.h"
#include "absl/strings/string_view.h"
ABSL_DECLARE_FLAG(std::vector<std::string>, flagfile);
@@ -32,7 +35,6 @@ namespace absl {
ABSL_NAMESPACE_BEGIN
namespace flags_internal {
-enum class ArgvListAction { kRemoveParsedArgs, kKeepParsedArgs };
enum class UsageFlagsAction { kHandleUsage, kIgnoreUsage };
enum class OnUndefinedFlag {
kIgnoreUndefined,
@@ -40,10 +42,15 @@ enum class OnUndefinedFlag {
kAbortIfUndefined
};
-std::vector<char*> ParseCommandLineImpl(int argc, char* argv[],
- ArgvListAction arg_list_act,
- UsageFlagsAction usage_flag_act,
- OnUndefinedFlag on_undef_flag);
+// This is not a public interface. This interface exists to expose the ability
+// to change help output stream in case of parsing errors. This is used by
+// internal unit tests to validate expected outputs.
+// When this was written, `EXPECT_EXIT` only supported matchers on stderr,
+// but not on stdout.
+std::vector<char*> ParseCommandLineImpl(
+ int argc, char* argv[], UsageFlagsAction usage_flag_action,
+ OnUndefinedFlag undef_flag_action,
+ std::ostream& error_help_output = std::cout);
// --------------------------------------------------------------------
// Inspect original command line
@@ -52,6 +59,10 @@ std::vector<char*> ParseCommandLineImpl(int argc, char* argv[],
// command line or specified in flag file present on the original command line.
bool WasPresentOnCommandLine(absl::string_view flag_name);
+// Return existing flags similar to the parameter, in order to help in case of
+// misspellings.
+std::vector<std::string> GetMisspellingHints(absl::string_view flag);
+
} // namespace flags_internal
ABSL_NAMESPACE_END
} // namespace absl
diff --git a/abseil-cpp/absl/flags/internal/registry.h b/abseil-cpp/absl/flags/internal/registry.h
index 1df2db7..4b68c85 100644
--- a/abseil-cpp/absl/flags/internal/registry.h
+++ b/abseil-cpp/absl/flags/internal/registry.h
@@ -30,16 +30,15 @@ namespace absl {
ABSL_NAMESPACE_BEGIN
namespace flags_internal {
-// Executes specified visitor for each non-retired flag in the registry.
-// Requires the caller hold the registry lock.
-void ForEachFlagUnlocked(std::function<void(CommandLineFlag&)> visitor);
// Executes specified visitor for each non-retired flag in the registry. While
// callback are executed, the registry is locked and can't be changed.
void ForEachFlag(std::function<void(CommandLineFlag&)> visitor);
//-----------------------------------------------------------------------------
-bool RegisterCommandLineFlag(CommandLineFlag&);
+bool RegisterCommandLineFlag(CommandLineFlag&, const char* filename);
+
+void FinalizeRegistry();
//-----------------------------------------------------------------------------
// Retired registrations:
diff --git a/abseil-cpp/absl/flags/internal/sequence_lock.h b/abseil-cpp/absl/flags/internal/sequence_lock.h
new file mode 100644
index 0000000..36318ab
--- /dev/null
+++ b/abseil-cpp/absl/flags/internal/sequence_lock.h
@@ -0,0 +1,187 @@
+//
+// Copyright 2020 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
+//
+// https://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.
+
+#ifndef ABSL_FLAGS_INTERNAL_SEQUENCE_LOCK_H_
+#define ABSL_FLAGS_INTERNAL_SEQUENCE_LOCK_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <atomic>
+#include <cassert>
+#include <cstring>
+
+#include "absl/base/optimization.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace flags_internal {
+
+// Align 'x' up to the nearest 'align' bytes.
+inline constexpr size_t AlignUp(size_t x, size_t align) {
+ return align * ((x + align - 1) / align);
+}
+
+// A SequenceLock implements lock-free reads. A sequence counter is incremented
+// before and after each write, and readers access the counter before and after
+// accessing the protected data. If the counter is verified to not change during
+// the access, and the sequence counter value was even, then the reader knows
+// that the read was race-free and valid. Otherwise, the reader must fall back
+// to a Mutex-based code path.
+//
+// This particular SequenceLock starts in an "uninitialized" state in which
+// TryRead() returns false. It must be enabled by calling MarkInitialized().
+// This serves as a marker that the associated flag value has not yet been
+// initialized and a slow path needs to be taken.
+//
+// The memory reads and writes protected by this lock must use the provided
+// `TryRead()` and `Write()` functions. These functions behave similarly to
+// `memcpy()`, with one oddity: the protected data must be an array of
+// `std::atomic<uint64>`. This is to comply with the C++ standard, which
+// considers data races on non-atomic objects to be undefined behavior. See "Can
+// Seqlocks Get Along With Programming Language Memory Models?"[1] by Hans J.
+// Boehm for more details.
+//
+// [1] https://www.hpl.hp.com/techreports/2012/HPL-2012-68.pdf
+class SequenceLock {
+ public:
+ constexpr SequenceLock() : lock_(kUninitialized) {}
+
+ // Mark that this lock is ready for use.
+ void MarkInitialized() {
+ assert(lock_.load(std::memory_order_relaxed) == kUninitialized);
+ lock_.store(0, std::memory_order_release);
+ }
+
+ // Copy "size" bytes of data from "src" to "dst", protected as a read-side
+ // critical section of the sequence lock.
+ //
+ // Unlike traditional sequence lock implementations which loop until getting a
+ // clean read, this implementation returns false in the case of concurrent
+ // calls to `Write`. In such a case, the caller should fall back to a
+ // locking-based slow path.
+ //
+ // Returns false if the sequence lock was not yet marked as initialized.
+ //
+ // NOTE: If this returns false, "dst" may be overwritten with undefined
+ // (potentially uninitialized) data.
+ bool TryRead(void* dst, const std::atomic<uint64_t>* src, size_t size) const {
+ // Acquire barrier ensures that no loads done by f() are reordered
+ // above the first load of the sequence counter.
+ int64_t seq_before = lock_.load(std::memory_order_acquire);
+ if (ABSL_PREDICT_FALSE(seq_before & 1) == 1) return false;
+ RelaxedCopyFromAtomic(dst, src, size);
+ // Another acquire fence ensures that the load of 'lock_' below is
+ // strictly ordered after the RelaxedCopyToAtomic call above.
+ std::atomic_thread_fence(std::memory_order_acquire);
+ int64_t seq_after = lock_.load(std::memory_order_relaxed);
+ return ABSL_PREDICT_TRUE(seq_before == seq_after);
+ }
+
+ // Copy "size" bytes from "src" to "dst" as a write-side critical section
+ // of the sequence lock. Any concurrent readers will be forced to retry
+ // until they get a read that does not conflict with this write.
+ //
+ // This call must be externally synchronized against other calls to Write,
+ // but may proceed concurrently with reads.
+ void Write(std::atomic<uint64_t>* dst, const void* src, size_t size) {
+ // We can use relaxed instructions to increment the counter since we
+ // are extenally synchronized. The std::atomic_thread_fence below
+ // ensures that the counter updates don't get interleaved with the
+ // copy to the data.
+ int64_t orig_seq = lock_.load(std::memory_order_relaxed);
+ assert((orig_seq & 1) == 0); // Must be initially unlocked.
+ lock_.store(orig_seq + 1, std::memory_order_relaxed);
+
+ // We put a release fence between update to lock_ and writes to shared data.
+ // Thus all stores to shared data are effectively release operations and
+ // update to lock_ above cannot be re-ordered past any of them. Note that
+ // this barrier is not for the fetch_add above. A release barrier for the
+ // fetch_add would be before it, not after.
+ std::atomic_thread_fence(std::memory_order_release);
+ RelaxedCopyToAtomic(dst, src, size);
+ // "Release" semantics ensure that none of the writes done by
+ // RelaxedCopyToAtomic() can be reordered after the following modification.
+ lock_.store(orig_seq + 2, std::memory_order_release);
+ }
+
+ // Return the number of times that Write() has been called.
+ //
+ // REQUIRES: This must be externally synchronized against concurrent calls to
+ // `Write()` or `IncrementModificationCount()`.
+ // REQUIRES: `MarkInitialized()` must have been previously called.
+ int64_t ModificationCount() const {
+ int64_t val = lock_.load(std::memory_order_relaxed);
+ assert(val != kUninitialized && (val & 1) == 0);
+ return val / 2;
+ }
+
+ // REQUIRES: This must be externally synchronized against concurrent calls to
+ // `Write()` or `ModificationCount()`.
+ // REQUIRES: `MarkInitialized()` must have been previously called.
+ void IncrementModificationCount() {
+ int64_t val = lock_.load(std::memory_order_relaxed);
+ assert(val != kUninitialized);
+ lock_.store(val + 2, std::memory_order_relaxed);
+ }
+
+ private:
+ // Perform the equivalent of "memcpy(dst, src, size)", but using relaxed
+ // atomics.
+ static void RelaxedCopyFromAtomic(void* dst, const std::atomic<uint64_t>* src,
+ size_t size) {
+ char* dst_byte = static_cast<char*>(dst);
+ while (size >= sizeof(uint64_t)) {
+ uint64_t word = src->load(std::memory_order_relaxed);
+ std::memcpy(dst_byte, &word, sizeof(word));
+ dst_byte += sizeof(word);
+ src++;
+ size -= sizeof(word);
+ }
+ if (size > 0) {
+ uint64_t word = src->load(std::memory_order_relaxed);
+ std::memcpy(dst_byte, &word, size);
+ }
+ }
+
+ // Perform the equivalent of "memcpy(dst, src, size)", but using relaxed
+ // atomics.
+ static void RelaxedCopyToAtomic(std::atomic<uint64_t>* dst, const void* src,
+ size_t size) {
+ const char* src_byte = static_cast<const char*>(src);
+ while (size >= sizeof(uint64_t)) {
+ uint64_t word;
+ std::memcpy(&word, src_byte, sizeof(word));
+ dst->store(word, std::memory_order_relaxed);
+ src_byte += sizeof(word);
+ dst++;
+ size -= sizeof(word);
+ }
+ if (size > 0) {
+ uint64_t word = 0;
+ std::memcpy(&word, src_byte, size);
+ dst->store(word, std::memory_order_relaxed);
+ }
+ }
+
+ static constexpr int64_t kUninitialized = -1;
+ std::atomic<int64_t> lock_;
+};
+
+} // namespace flags_internal
+ABSL_NAMESPACE_END
+} // namespace absl
+
+#endif // ABSL_FLAGS_INTERNAL_SEQUENCE_LOCK_H_
diff --git a/abseil-cpp/absl/flags/internal/sequence_lock_test.cc b/abseil-cpp/absl/flags/internal/sequence_lock_test.cc
new file mode 100644
index 0000000..c3ec372
--- /dev/null
+++ b/abseil-cpp/absl/flags/internal/sequence_lock_test.cc
@@ -0,0 +1,169 @@
+// Copyright 2020 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
+//
+// https://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.
+#include "absl/flags/internal/sequence_lock.h"
+
+#include <algorithm>
+#include <atomic>
+#include <thread> // NOLINT(build/c++11)
+#include <tuple>
+#include <vector>
+
+#include "gtest/gtest.h"
+#include "absl/base/internal/sysinfo.h"
+#include "absl/container/fixed_array.h"
+#include "absl/time/clock.h"
+
+namespace {
+
+namespace flags = absl::flags_internal;
+
+class ConcurrentSequenceLockTest
+ : public testing::TestWithParam<std::tuple<int, int>> {
+ public:
+ ConcurrentSequenceLockTest()
+ : buf_bytes_(std::get<0>(GetParam())),
+ num_threads_(std::get<1>(GetParam())) {}
+
+ protected:
+ const int buf_bytes_;
+ const int num_threads_;
+};
+
+TEST_P(ConcurrentSequenceLockTest, ReadAndWrite) {
+ const int buf_words =
+ flags::AlignUp(buf_bytes_, sizeof(uint64_t)) / sizeof(uint64_t);
+
+ // The buffer that will be protected by the SequenceLock.
+ absl::FixedArray<std::atomic<uint64_t>> protected_buf(buf_words);
+ for (auto& v : protected_buf) v = -1;
+
+ flags::SequenceLock seq_lock;
+ std::atomic<bool> stop{false};
+ std::atomic<int64_t> bad_reads{0};
+ std::atomic<int64_t> good_reads{0};
+ std::atomic<int64_t> unsuccessful_reads{0};
+
+ // Start a bunch of threads which read 'protected_buf' under the sequence
+ // lock. The main thread will concurrently update 'protected_buf'. The updates
+ // always consist of an array of identical integers. The reader ensures that
+ // any data it reads matches that pattern (i.e. the reads are not "torn").
+ std::vector<std::thread> threads;
+ for (int i = 0; i < num_threads_; i++) {
+ threads.emplace_back([&]() {
+ absl::FixedArray<char> local_buf(buf_bytes_);
+ while (!stop.load(std::memory_order_relaxed)) {
+ if (seq_lock.TryRead(local_buf.data(), protected_buf.data(),
+ buf_bytes_)) {
+ bool good = true;
+ for (const auto& v : local_buf) {
+ if (v != local_buf[0]) good = false;
+ }
+ if (good) {
+ good_reads.fetch_add(1, std::memory_order_relaxed);
+ } else {
+ bad_reads.fetch_add(1, std::memory_order_relaxed);
+ }
+ } else {
+ unsuccessful_reads.fetch_add(1, std::memory_order_relaxed);
+ }
+ }
+ });
+ }
+ while (unsuccessful_reads.load(std::memory_order_relaxed) < num_threads_) {
+ absl::SleepFor(absl::Milliseconds(1));
+ }
+ seq_lock.MarkInitialized();
+
+ // Run a maximum of 5 seconds. On Windows, the scheduler behavior seems
+ // somewhat unfair and without an explicit timeout for this loop, the tests
+ // can run a long time.
+ absl::Time deadline = absl::Now() + absl::Seconds(5);
+ for (int i = 0; i < 100 && absl::Now() < deadline; i++) {
+ absl::FixedArray<char> writer_buf(buf_bytes_);
+ for (auto& v : writer_buf) v = i;
+ seq_lock.Write(protected_buf.data(), writer_buf.data(), buf_bytes_);
+ absl::SleepFor(absl::Microseconds(10));
+ }
+ stop.store(true, std::memory_order_relaxed);
+ for (auto& t : threads) t.join();
+ ASSERT_GE(good_reads, 0);
+ ASSERT_EQ(bad_reads, 0);
+}
+
+// Simple helper for generating a range of thread counts.
+// Generates [low, low*scale, low*scale^2, ...high)
+// (even if high is between low*scale^k and low*scale^(k+1)).
+std::vector<int> MultiplicativeRange(int low, int high, int scale) {
+ std::vector<int> result;
+ for (int current = low; current < high; current *= scale) {
+ result.push_back(current);
+ }
+ result.push_back(high);
+ return result;
+}
+
+#ifndef ABSL_HAVE_THREAD_SANITIZER
+const int kMaxThreads = absl::base_internal::NumCPUs();
+#else
+// With TSAN, a lot of threads contending for atomic access on the sequence
+// lock make this test run too slowly.
+const int kMaxThreads = std::min(absl::base_internal::NumCPUs(), 4);
+#endif
+
+// Return all of the interesting buffer sizes worth testing:
+// powers of two and adjacent values.
+std::vector<int> InterestingBufferSizes() {
+ std::vector<int> ret;
+ for (int v : MultiplicativeRange(1, 128, 2)) {
+ ret.push_back(v);
+ if (v > 1) {
+ ret.push_back(v - 1);
+ }
+ ret.push_back(v + 1);
+ }
+ return ret;
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ TestManyByteSizes, ConcurrentSequenceLockTest,
+ testing::Combine(
+ // Buffer size (bytes).
+ testing::ValuesIn(InterestingBufferSizes()),
+ // Number of reader threads.
+ testing::ValuesIn(MultiplicativeRange(1, kMaxThreads, 2))));
+
+// Simple single-threaded test, parameterized by the size of the buffer to be
+// protected.
+class SequenceLockTest : public testing::TestWithParam<int> {};
+
+TEST_P(SequenceLockTest, SingleThreaded) {
+ const int size = GetParam();
+ absl::FixedArray<std::atomic<uint64_t>> protected_buf(
+ flags::AlignUp(size, sizeof(uint64_t)) / sizeof(uint64_t));
+
+ flags::SequenceLock seq_lock;
+ seq_lock.MarkInitialized();
+
+ std::vector<char> src_buf(size, 'x');
+ seq_lock.Write(protected_buf.data(), src_buf.data(), size);
+
+ std::vector<char> dst_buf(size, '0');
+ ASSERT_TRUE(seq_lock.TryRead(dst_buf.data(), protected_buf.data(), size));
+ ASSERT_EQ(src_buf, dst_buf);
+}
+INSTANTIATE_TEST_SUITE_P(TestManyByteSizes, SequenceLockTest,
+ // Buffer size (bytes).
+ testing::Range(1, 128));
+
+} // namespace
diff --git a/abseil-cpp/absl/flags/internal/usage.cc b/abseil-cpp/absl/flags/internal/usage.cc
index 0805df3..13852e1 100644
--- a/abseil-cpp/absl/flags/internal/usage.cc
+++ b/abseil-cpp/absl/flags/internal/usage.cc
@@ -17,7 +17,10 @@
#include <stdint.h>
+#include <algorithm>
+#include <cstdlib>
#include <functional>
+#include <iterator>
#include <map>
#include <ostream>
#include <string>
@@ -33,30 +36,31 @@
#include "absl/flags/internal/program_name.h"
#include "absl/flags/internal/registry.h"
#include "absl/flags/usage_config.h"
+#include "absl/strings/match.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"
-ABSL_FLAG(bool, help, false,
- "show help on important flags for this binary [tip: all flags can "
- "have two dashes]");
-ABSL_FLAG(bool, helpfull, false, "show help on all flags");
-ABSL_FLAG(bool, helpshort, false,
- "show help on only the main module for this program");
-ABSL_FLAG(bool, helppackage, false,
- "show help on all modules in the main package");
-ABSL_FLAG(bool, version, false, "show version and build info and exit");
-ABSL_FLAG(bool, only_check_args, false, "exit after checking all flags");
-ABSL_FLAG(std::string, helpon, "",
- "show help on the modules named by this flag value");
-ABSL_FLAG(std::string, helpmatch, "",
- "show help on modules whose name contains the specified substr");
+// Dummy global variables to prevent anyone else defining these.
+bool FLAGS_help = false;
+bool FLAGS_helpfull = false;
+bool FLAGS_helpshort = false;
+bool FLAGS_helppackage = false;
+bool FLAGS_version = false;
+bool FLAGS_only_check_args = false;
+bool FLAGS_helpon = false;
+bool FLAGS_helpmatch = false;
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace flags_internal {
namespace {
+using PerFlagFilter = std::function<bool(const absl::CommandLineFlag&)>;
+
+// Maximum length size in a human readable format.
+constexpr size_t kHrfMaxLineLength = 80;
+
// This class is used to emit an XML element with `tag` and `text`.
// It adds opening and closing tags and escapes special characters in the text.
// For example:
@@ -88,8 +92,16 @@ class XMLElement {
case '>':
out << "&gt;";
break;
+ case '\n':
+ case '\v':
+ case '\f':
+ case '\t':
+ out << " ";
+ break;
default:
- out << c;
+ if (IsValidXmlCharacter(static_cast<unsigned char>(c))) {
+ out << c;
+ }
break;
}
}
@@ -98,6 +110,7 @@ class XMLElement {
}
private:
+ static bool IsValidXmlCharacter(unsigned char c) { return c >= 0x20; }
absl::string_view tag_;
absl::string_view txt_;
};
@@ -109,9 +122,12 @@ class FlagHelpPrettyPrinter {
public:
// Pretty printer holds on to the std::ostream& reference to direct an output
// to that stream.
- FlagHelpPrettyPrinter(int max_line_len, std::ostream& out)
+ FlagHelpPrettyPrinter(size_t max_line_len, size_t min_line_len,
+ size_t wrapped_line_indent, std::ostream& out)
: out_(out),
max_line_len_(max_line_len),
+ min_line_len_(min_line_len),
+ wrapped_line_indent_(wrapped_line_indent),
line_len_(0),
first_line_(true) {}
@@ -124,7 +140,7 @@ class FlagHelpPrettyPrinter {
for (auto line : absl::StrSplit(str, absl::ByAnyChar("\n\r"))) {
if (!tokens.empty()) {
// Keep line separators in the input string.
- tokens.push_back("\n");
+ tokens.emplace_back("\n");
}
for (auto token :
absl::StrSplit(line, absl::ByAnyChar(" \t"), absl::SkipEmpty())) {
@@ -164,13 +180,12 @@ class FlagHelpPrettyPrinter {
void StartLine() {
if (first_line_) {
- out_ << " ";
- line_len_ = 4;
+ line_len_ = min_line_len_;
first_line_ = false;
} else {
- out_ << " ";
- line_len_ = 6;
+ line_len_ = min_line_len_ + wrapped_line_indent_;
}
+ out_ << std::string(line_len_, ' ');
}
void EndLine() {
out_ << '\n';
@@ -179,13 +194,15 @@ class FlagHelpPrettyPrinter {
private:
std::ostream& out_;
- const int max_line_len_;
- int line_len_;
+ const size_t max_line_len_;
+ const size_t min_line_len_;
+ const size_t wrapped_line_indent_;
+ size_t line_len_;
bool first_line_;
};
void FlagHelpHumanReadable(const CommandLineFlag& flag, std::ostream& out) {
- FlagHelpPrettyPrinter printer(80, out); // Max line length is 80.
+ FlagHelpPrettyPrinter printer(kHrfMaxLineLength, 4, 2, out);
// Flag name.
printer.Write(absl::StrCat("--", flag.Name()));
@@ -221,7 +238,7 @@ void FlagHelpHumanReadable(const CommandLineFlag& flag, std::ostream& out) {
// If a flag's help message has been stripped (e.g. by adding '#define
// STRIP_FLAG_HELP 1' then this flag will not be displayed by '--help'
// and its variants.
-void FlagsHelpImpl(std::ostream& out, flags_internal::FlagKindFilter filter_cb,
+void FlagsHelpImpl(std::ostream& out, PerFlagFilter filter_cb,
HelpFormat format, absl::string_view program_usage_message) {
if (format == HelpFormat::kHumanReadable) {
out << flags_internal::ShortProgramInvocationName() << ": "
@@ -240,7 +257,7 @@ void FlagsHelpImpl(std::ostream& out, flags_internal::FlagKindFilter filter_cb,
<< XMLElement("usage", program_usage_message) << '\n';
}
- // Map of package name to
+ // Ordered map of package name to
// map of file name to
// vector of flags in the file.
// This map is used to output matching flags grouped by package and file
@@ -256,10 +273,10 @@ void FlagsHelpImpl(std::ostream& out, flags_internal::FlagKindFilter filter_cb,
// If the flag has been stripped, pretend that it doesn't exist.
if (flag.Help() == flags_internal::kStrippedFlagHelp) return;
- std::string flag_filename = flag.Filename();
-
// Make sure flag satisfies the filter
- if (!filter_cb || !filter_cb(flag_filename)) return;
+ if (!filter_cb(flag)) return;
+
+ std::string flag_filename = flag.Filename();
matching_flags[std::string(flags_internal::Package(flag_filename))]
[flag_filename]
@@ -268,20 +285,26 @@ void FlagsHelpImpl(std::ostream& out, flags_internal::FlagKindFilter filter_cb,
absl::string_view package_separator; // controls blank lines between packages
absl::string_view file_separator; // controls blank lines between files
- for (const auto& package : matching_flags) {
+ for (auto& package : matching_flags) {
if (format == HelpFormat::kHumanReadable) {
out << package_separator;
package_separator = "\n\n";
}
file_separator = "";
- for (const auto& flags_in_file : package.second) {
+ for (auto& flags_in_file : package.second) {
if (format == HelpFormat::kHumanReadable) {
out << file_separator << " Flags from " << flags_in_file.first
<< ":\n";
file_separator = "\n";
}
+ std::sort(std::begin(flags_in_file.second),
+ std::end(flags_in_file.second),
+ [](const CommandLineFlag* lhs, const CommandLineFlag* rhs) {
+ return lhs->Name() < rhs->Name();
+ });
+
for (const auto* flag : flags_in_file.second) {
flags_internal::FlagHelp(out, *flag, format);
}
@@ -289,15 +312,34 @@ void FlagsHelpImpl(std::ostream& out, flags_internal::FlagKindFilter filter_cb,
}
if (format == HelpFormat::kHumanReadable) {
+ FlagHelpPrettyPrinter printer(kHrfMaxLineLength, 0, 0, out);
+
if (filter_cb && matching_flags.empty()) {
- out << " No modules matched: use -helpfull\n";
+ printer.Write("No flags matched.\n", true);
}
+ printer.EndLine();
+ printer.Write(
+ "Try --helpfull to get a list of all flags or --help=substring "
+ "shows help for flags which include specified substring in either "
+ "in the name, or description or path.\n",
+ true);
} else {
// The end of the document.
out << "</AllFlags>\n";
}
}
+void FlagsHelpImpl(std::ostream& out,
+ flags_internal::FlagKindFilter filename_filter_cb,
+ HelpFormat format, absl::string_view program_usage_message) {
+ FlagsHelpImpl(
+ out,
+ [&](const absl::CommandLineFlag& flag) {
+ return filename_filter_cb && filename_filter_cb(flag.Filename());
+ },
+ format, program_usage_message);
+}
+
} // namespace
// --------------------------------------------------------------------
@@ -309,12 +351,12 @@ void FlagHelp(std::ostream& out, const CommandLineFlag& flag,
}
// --------------------------------------------------------------------
-// Produces the help messages for all flags matching the filter.
+// Produces the help messages for all flags matching the filename filter.
// If filter is empty produces help messages for all flags.
void FlagsHelp(std::ostream& out, absl::string_view filter, HelpFormat format,
absl::string_view program_usage_message) {
flags_internal::FlagKindFilter filter_cb = [&](absl::string_view filename) {
- return filter.empty() || filename.find(filter) != absl::string_view::npos;
+ return filter.empty() || absl::StrContains(filename, filter);
};
flags_internal::FlagsHelpImpl(out, filter_cb, format, program_usage_message);
}
@@ -322,70 +364,187 @@ void FlagsHelp(std::ostream& out, absl::string_view filter, HelpFormat format,
// --------------------------------------------------------------------
// Checks all the 'usage' command line flags to see if any have been set.
// If so, handles them appropriately.
-int HandleUsageFlags(std::ostream& out,
- absl::string_view program_usage_message) {
- if (absl::GetFlag(FLAGS_helpshort)) {
- flags_internal::FlagsHelpImpl(
- out, flags_internal::GetUsageConfig().contains_helpshort_flags,
- HelpFormat::kHumanReadable, program_usage_message);
- return 1;
+HelpMode HandleUsageFlags(std::ostream& out,
+ absl::string_view program_usage_message) {
+ switch (GetFlagsHelpMode()) {
+ case HelpMode::kNone:
+ break;
+ case HelpMode::kImportant:
+ flags_internal::FlagsHelpImpl(
+ out, flags_internal::GetUsageConfig().contains_help_flags,
+ GetFlagsHelpFormat(), program_usage_message);
+ break;
+
+ case HelpMode::kShort:
+ flags_internal::FlagsHelpImpl(
+ out, flags_internal::GetUsageConfig().contains_helpshort_flags,
+ GetFlagsHelpFormat(), program_usage_message);
+ break;
+
+ case HelpMode::kFull:
+ flags_internal::FlagsHelp(out, "", GetFlagsHelpFormat(),
+ program_usage_message);
+ break;
+
+ case HelpMode::kPackage:
+ flags_internal::FlagsHelpImpl(
+ out, flags_internal::GetUsageConfig().contains_helppackage_flags,
+ GetFlagsHelpFormat(), program_usage_message);
+ break;
+
+ case HelpMode::kMatch: {
+ std::string substr = GetFlagsHelpMatchSubstr();
+ if (substr.empty()) {
+ // show all options
+ flags_internal::FlagsHelp(out, substr, GetFlagsHelpFormat(),
+ program_usage_message);
+ } else {
+ auto filter_cb = [&substr](const absl::CommandLineFlag& flag) {
+ if (absl::StrContains(flag.Name(), substr)) return true;
+ if (absl::StrContains(flag.Filename(), substr)) return true;
+ if (absl::StrContains(flag.Help(), substr)) return true;
+
+ return false;
+ };
+ flags_internal::FlagsHelpImpl(
+ out, filter_cb, HelpFormat::kHumanReadable, program_usage_message);
+ }
+ break;
+ }
+ case HelpMode::kVersion:
+ if (flags_internal::GetUsageConfig().version_string)
+ out << flags_internal::GetUsageConfig().version_string();
+ // Unlike help, we may be asking for version in a script, so return 0
+ break;
+
+ case HelpMode::kOnlyCheckArgs:
+ break;
}
- if (absl::GetFlag(FLAGS_helpfull)) {
- // show all options
- flags_internal::FlagsHelp(out, "", HelpFormat::kHumanReadable,
- program_usage_message);
- return 1;
- }
+ return GetFlagsHelpMode();
+}
- if (!absl::GetFlag(FLAGS_helpon).empty()) {
- flags_internal::FlagsHelp(
- out, absl::StrCat("/", absl::GetFlag(FLAGS_helpon), "."),
- HelpFormat::kHumanReadable, program_usage_message);
- return 1;
- }
+// --------------------------------------------------------------------
+// Globals representing usage reporting flags
- if (!absl::GetFlag(FLAGS_helpmatch).empty()) {
- flags_internal::FlagsHelp(out, absl::GetFlag(FLAGS_helpmatch),
- HelpFormat::kHumanReadable,
- program_usage_message);
- return 1;
- }
+namespace {
- if (absl::GetFlag(FLAGS_help)) {
- flags_internal::FlagsHelpImpl(
- out, flags_internal::GetUsageConfig().contains_help_flags,
- HelpFormat::kHumanReadable, program_usage_message);
+ABSL_CONST_INIT absl::Mutex help_attributes_guard(absl::kConstInit);
+ABSL_CONST_INIT std::string* match_substr
+ ABSL_GUARDED_BY(help_attributes_guard) = nullptr;
+ABSL_CONST_INIT HelpMode help_mode ABSL_GUARDED_BY(help_attributes_guard) =
+ HelpMode::kNone;
+ABSL_CONST_INIT HelpFormat help_format ABSL_GUARDED_BY(help_attributes_guard) =
+ HelpFormat::kHumanReadable;
- out << "\nTry --helpfull to get a list of all flags.\n";
+} // namespace
- return 1;
- }
+std::string GetFlagsHelpMatchSubstr() {
+ absl::MutexLock l(&help_attributes_guard);
+ if (match_substr == nullptr) return "";
+ return *match_substr;
+}
- if (absl::GetFlag(FLAGS_helppackage)) {
- flags_internal::FlagsHelpImpl(
- out, flags_internal::GetUsageConfig().contains_helppackage_flags,
- HelpFormat::kHumanReadable, program_usage_message);
+void SetFlagsHelpMatchSubstr(absl::string_view substr) {
+ absl::MutexLock l(&help_attributes_guard);
+ if (match_substr == nullptr) match_substr = new std::string;
+ match_substr->assign(substr.data(), substr.size());
+}
- out << "\nTry --helpfull to get a list of all flags.\n";
+HelpMode GetFlagsHelpMode() {
+ absl::MutexLock l(&help_attributes_guard);
+ return help_mode;
+}
- return 1;
+void SetFlagsHelpMode(HelpMode mode) {
+ absl::MutexLock l(&help_attributes_guard);
+ help_mode = mode;
+}
+
+HelpFormat GetFlagsHelpFormat() {
+ absl::MutexLock l(&help_attributes_guard);
+ return help_format;
+}
+
+void SetFlagsHelpFormat(HelpFormat format) {
+ absl::MutexLock l(&help_attributes_guard);
+ help_format = format;
+}
+
+// Deduces usage flags from the input argument in a form --name=value or
+// --name. argument is already split into name and value before we call this
+// function.
+bool DeduceUsageFlags(absl::string_view name, absl::string_view value) {
+ if (absl::ConsumePrefix(&name, "help")) {
+ if (name.empty()) {
+ if (value.empty()) {
+ SetFlagsHelpMode(HelpMode::kImportant);
+ } else {
+ SetFlagsHelpMode(HelpMode::kMatch);
+ SetFlagsHelpMatchSubstr(value);
+ }
+ return true;
+ }
+
+ if (name == "match") {
+ SetFlagsHelpMode(HelpMode::kMatch);
+ SetFlagsHelpMatchSubstr(value);
+ return true;
+ }
+
+ if (name == "on") {
+ SetFlagsHelpMode(HelpMode::kMatch);
+ SetFlagsHelpMatchSubstr(absl::StrCat("/", value, "."));
+ return true;
+ }
+
+ if (name == "full") {
+ SetFlagsHelpMode(HelpMode::kFull);
+ return true;
+ }
+
+ if (name == "short") {
+ SetFlagsHelpMode(HelpMode::kShort);
+ return true;
+ }
+
+ if (name == "package") {
+ SetFlagsHelpMode(HelpMode::kPackage);
+ return true;
+ }
+
+ return false;
}
- if (absl::GetFlag(FLAGS_version)) {
- if (flags_internal::GetUsageConfig().version_string)
- out << flags_internal::GetUsageConfig().version_string();
- // Unlike help, we may be asking for version in a script, so return 0
- return 0;
+ if (name == "version") {
+ SetFlagsHelpMode(HelpMode::kVersion);
+ return true;
}
- if (absl::GetFlag(FLAGS_only_check_args)) {
- return 0;
+ if (name == "only_check_args") {
+ SetFlagsHelpMode(HelpMode::kOnlyCheckArgs);
+ return true;
}
- return -1;
+ return false;
}
+// --------------------------------------------------------------------
+
+void MaybeExit(HelpMode mode) {
+ switch (mode) {
+ case flags_internal::HelpMode::kNone:
+ return;
+ case flags_internal::HelpMode::kOnlyCheckArgs:
+ case flags_internal::HelpMode::kVersion:
+ std::exit(0);
+ default: // For all the other modes we exit with 1
+ std::exit(1);
+ }
+}
+
+// --------------------------------------------------------------------
+
} // namespace flags_internal
ABSL_NAMESPACE_END
} // namespace absl
diff --git a/abseil-cpp/absl/flags/internal/usage.h b/abseil-cpp/absl/flags/internal/usage.h
index 0c62dc4..a96cbf3 100644
--- a/abseil-cpp/absl/flags/internal/usage.h
+++ b/abseil-cpp/absl/flags/internal/usage.h
@@ -17,11 +17,11 @@
#define ABSL_FLAGS_INTERNAL_USAGE_H_
#include <iosfwd>
+#include <ostream>
#include <string>
#include "absl/base/config.h"
#include "absl/flags/commandlineflag.h"
-#include "absl/flags/declare.h"
#include "absl/strings/string_view.h"
// --------------------------------------------------------------------
@@ -36,7 +36,20 @@ enum class HelpFormat {
kHumanReadable,
};
-// Outputs the help message describing specific flag.
+// The kind of usage help requested.
+enum class HelpMode {
+ kNone,
+ kImportant,
+ kShort,
+ kFull,
+ kPackage,
+ kMatch,
+ kVersion,
+ kOnlyCheckArgs
+};
+
+// Streams the help message describing `flag` to `out`.
+// The default value for `flag` is included in the output.
void FlagHelp(std::ostream& out, const CommandLineFlag& flag,
HelpFormat format = HelpFormat::kHumanReadable);
@@ -56,26 +69,38 @@ void FlagsHelp(std::ostream& out, absl::string_view filter,
// If any of the 'usage' related command line flags (listed on the bottom of
// this file) has been set this routine produces corresponding help message in
-// the specified output stream and returns:
-// 0 - if "version" or "only_check_flags" flags were set and handled.
-// 1 - if some other 'usage' related flag was set and handled.
-// -1 - if no usage flags were set on a commmand line.
-// Non negative return values are expected to be used as an exit code for a
-// binary.
-int HandleUsageFlags(std::ostream& out,
- absl::string_view program_usage_message);
+// the specified output stream and returns HelpMode that was handled. Otherwise
+// it returns HelpMode::kNone.
+HelpMode HandleUsageFlags(std::ostream& out,
+ absl::string_view program_usage_message);
+
+// --------------------------------------------------------------------
+// Encapsulates the logic of exiting the binary depending on handled help mode.
+
+void MaybeExit(HelpMode mode);
+
+// --------------------------------------------------------------------
+// Globals representing usage reporting flags
+
+// Returns substring to filter help output (--help=substr argument)
+std::string GetFlagsHelpMatchSubstr();
+// Returns the requested help mode.
+HelpMode GetFlagsHelpMode();
+// Returns the requested help format.
+HelpFormat GetFlagsHelpFormat();
+
+// These are corresponding setters to the attributes above.
+void SetFlagsHelpMatchSubstr(absl::string_view);
+void SetFlagsHelpMode(HelpMode);
+void SetFlagsHelpFormat(HelpFormat);
+
+// Deduces usage flags from the input argument in a form --name=value or
+// --name. argument is already split into name and value before we call this
+// function.
+bool DeduceUsageFlags(absl::string_view name, absl::string_view value);
} // namespace flags_internal
ABSL_NAMESPACE_END
} // namespace absl
-ABSL_DECLARE_FLAG(bool, help);
-ABSL_DECLARE_FLAG(bool, helpfull);
-ABSL_DECLARE_FLAG(bool, helpshort);
-ABSL_DECLARE_FLAG(bool, helppackage);
-ABSL_DECLARE_FLAG(bool, version);
-ABSL_DECLARE_FLAG(bool, only_check_args);
-ABSL_DECLARE_FLAG(std::string, helpon);
-ABSL_DECLARE_FLAG(std::string, helpmatch);
-
#endif // ABSL_FLAGS_INTERNAL_USAGE_H_
diff --git a/abseil-cpp/absl/flags/internal/usage_test.cc b/abseil-cpp/absl/flags/internal/usage_test.cc
index 6e583fb..6847386 100644
--- a/abseil-cpp/absl/flags/internal/usage_test.cc
+++ b/abseil-cpp/absl/flags/internal/usage_test.cc
@@ -20,10 +20,10 @@
#include <sstream>
#include <string>
+#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/flags/flag.h"
#include "absl/flags/internal/parse.h"
-#include "absl/flags/internal/path_util.h"
#include "absl/flags/internal/program_name.h"
#include "absl/flags/reflection.h"
#include "absl/flags/usage.h"
@@ -39,15 +39,20 @@ ABSL_FLAG(double, usage_reporting_test_flag_03, 1.03,
"usage_reporting_test_flag_03 help message");
ABSL_FLAG(int64_t, usage_reporting_test_flag_04, 1000000000000004L,
"usage_reporting_test_flag_04 help message");
+ABSL_FLAG(std::string, usage_reporting_test_flag_07, "\r\n\f\v\a\b\t ",
+ "usage_reporting_test_flag_07 help \r\n\f\v\a\b\t ");
static const char kTestUsageMessage[] = "Custom usage message";
struct UDT {
UDT() = default;
UDT(const UDT&) = default;
+ UDT& operator=(const UDT&) = default;
};
-bool AbslParseFlag(absl::string_view, UDT*, std::string*) { return true; }
-std::string AbslUnparseFlag(const UDT&) { return "UDT{}"; }
+static bool AbslParseFlag(absl::string_view, UDT*, std::string*) {
+ return true;
+}
+static std::string AbslUnparseFlag(const UDT&) { return "UDT{}"; }
ABSL_FLAG(UDT, usage_reporting_test_flag_05, {},
"usage_reporting_test_flag_05 help message");
@@ -87,6 +92,11 @@ class UsageReportingTest : public testing::Test {
default_config.normalize_filename = &NormalizeFileName;
absl::SetFlagsUsageConfig(default_config);
}
+ ~UsageReportingTest() override {
+ flags::SetFlagsHelpMode(flags::HelpMode::kNone);
+ flags::SetFlagsHelpMatchSubstr("");
+ flags::SetFlagsHelpFormat(flags::HelpFormat::kHumanReadable);
+ }
private:
absl::FlagSaver flag_saver_;
@@ -97,14 +107,19 @@ class UsageReportingTest : public testing::Test {
using UsageReportingDeathTest = UsageReportingTest;
TEST_F(UsageReportingDeathTest, TestSetProgramUsageMessage) {
+#if !defined(GTEST_HAS_ABSL) || !GTEST_HAS_ABSL
+ // Check for kTestUsageMessage set in main() below.
EXPECT_EQ(absl::ProgramUsageMessage(), kTestUsageMessage);
+#else
+ // Check for part of the usage message set by GoogleTest.
+ EXPECT_THAT(absl::ProgramUsageMessage(),
+ ::testing::HasSubstr(
+ "This program contains tests written using Google Test"));
+#endif
-#ifndef _WIN32
- // TODO(rogeeff): figure out why this does not work on Windows.
EXPECT_DEATH_IF_SUPPORTED(
absl::SetProgramUsageMessage("custom usage message"),
- ".*SetProgramUsageMessage\\(\\) called twice.*");
-#endif
+ ::testing::HasSubstr("SetProgramUsageMessage() called twice"));
}
// --------------------------------------------------------------------
@@ -190,7 +205,15 @@ TEST_F(UsageReportingTest, TestFlagsHelpHRF) {
Some more help.
Even more long long long long long long long long long long long long help
- message.); default: "";
+ message.); default: "";)"
+
+ "\n --usage_reporting_test_flag_07 (usage_reporting_test_flag_07 "
+ "help\n\n \f\v\a\b ); default: \"\r\n\f\v\a\b\t \";\n"
+
+ R"(
+Try --helpfull to get a list of all flags or --help=substring shows help for
+flags which include specified substring in either in the name, or description or
+path.
)";
std::stringstream test_buf_01;
@@ -214,7 +237,11 @@ TEST_F(UsageReportingTest, TestFlagsHelpHRF) {
EXPECT_EQ(test_buf_04.str(),
R"(usage_test: Custom usage message
- No modules matched: use -helpfull
+No flags matched.
+
+Try --helpfull to get a list of all flags or --help=substring shows help for
+flags which include specified substring in either in the name, or description or
+path.
)");
std::stringstream test_buf_05;
@@ -226,30 +253,29 @@ TEST_F(UsageReportingTest, TestFlagsHelpHRF) {
absl::StartsWith(test_out_str, "usage_test: Custom usage message"));
EXPECT_TRUE(absl::StrContains(
test_out_str, "Flags from absl/flags/internal/usage_test.cc:"));
- EXPECT_TRUE(absl::StrContains(test_out_str,
- "Flags from absl/flags/internal/usage.cc:"));
EXPECT_TRUE(
absl::StrContains(test_out_str, "-usage_reporting_test_flag_01 "));
- EXPECT_TRUE(absl::StrContains(test_out_str, "-help (show help"))
- << test_out_str;
}
// --------------------------------------------------------------------
TEST_F(UsageReportingTest, TestNoUsageFlags) {
std::stringstream test_buf;
- EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), -1);
+ EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage),
+ flags::HelpMode::kNone);
}
// --------------------------------------------------------------------
TEST_F(UsageReportingTest, TestUsageFlag_helpshort) {
- absl::SetFlag(&FLAGS_helpshort, true);
+ flags::SetFlagsHelpMode(flags::HelpMode::kShort);
std::stringstream test_buf;
- EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 1);
- EXPECT_EQ(test_buf.str(),
- R"(usage_test: Custom usage message
+ EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage),
+ flags::HelpMode::kShort);
+ EXPECT_EQ(
+ test_buf.str(),
+ R"(usage_test: Custom usage message
Flags from absl/flags/internal/usage_test.cc:
--usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message);
@@ -266,21 +292,96 @@ TEST_F(UsageReportingTest, TestUsageFlag_helpshort) {
Some more help.
Even more long long long long long long long long long long long long help
- message.); default: "";
+ message.); default: "";)"
+
+ "\n --usage_reporting_test_flag_07 (usage_reporting_test_flag_07 "
+ "help\n\n \f\v\a\b ); default: \"\r\n\f\v\a\b\t \";\n"
+
+ R"(
+Try --helpfull to get a list of all flags or --help=substring shows help for
+flags which include specified substring in either in the name, or description or
+path.
)");
}
// --------------------------------------------------------------------
-TEST_F(UsageReportingTest, TestUsageFlag_help) {
- absl::SetFlag(&FLAGS_help, true);
+TEST_F(UsageReportingTest, TestUsageFlag_help_simple) {
+ flags::SetFlagsHelpMode(flags::HelpMode::kImportant);
std::stringstream test_buf;
- EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 1);
+ EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage),
+ flags::HelpMode::kImportant);
+ EXPECT_EQ(
+ test_buf.str(),
+ R"(usage_test: Custom usage message
+
+ Flags from absl/flags/internal/usage_test.cc:
+ --usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message);
+ default: 101;
+ --usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message);
+ default: false;
+ --usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message);
+ default: 1.03;
+ --usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message);
+ default: 1000000000000004;
+ --usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message);
+ default: UDT{};
+ --usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message.
+
+ Some more help.
+ Even more long long long long long long long long long long long long help
+ message.); default: "";)"
+
+ "\n --usage_reporting_test_flag_07 (usage_reporting_test_flag_07 "
+ "help\n\n \f\v\a\b ); default: \"\r\n\f\v\a\b\t \";\n"
+
+ R"(
+Try --helpfull to get a list of all flags or --help=substring shows help for
+flags which include specified substring in either in the name, or description or
+path.
+)");
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(UsageReportingTest, TestUsageFlag_help_one_flag) {
+ flags::SetFlagsHelpMode(flags::HelpMode::kMatch);
+ flags::SetFlagsHelpMatchSubstr("usage_reporting_test_flag_06");
+
+ std::stringstream test_buf;
+ EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage),
+ flags::HelpMode::kMatch);
EXPECT_EQ(test_buf.str(),
R"(usage_test: Custom usage message
Flags from absl/flags/internal/usage_test.cc:
+ --usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message.
+
+ Some more help.
+ Even more long long long long long long long long long long long long help
+ message.); default: "";
+
+Try --helpfull to get a list of all flags or --help=substring shows help for
+flags which include specified substring in either in the name, or description or
+path.
+)");
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(UsageReportingTest, TestUsageFlag_help_multiple_flag) {
+ flags::SetFlagsHelpMode(flags::HelpMode::kMatch);
+ flags::SetFlagsHelpMatchSubstr("test_flag");
+
+ std::stringstream test_buf;
+ EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage),
+ flags::HelpMode::kMatch);
+ EXPECT_EQ(
+ test_buf.str(),
+ R"(usage_test: Custom usage message
+
+ Flags from absl/flags/internal/usage_test.cc:
--usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message);
default: 101;
--usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message);
@@ -295,21 +396,29 @@ TEST_F(UsageReportingTest, TestUsageFlag_help) {
Some more help.
Even more long long long long long long long long long long long long help
- message.); default: "";
+ message.); default: "";)"
-Try --helpfull to get a list of all flags.
+ "\n --usage_reporting_test_flag_07 (usage_reporting_test_flag_07 "
+ "help\n\n \f\v\a\b ); default: \"\r\n\f\v\a\b\t \";\n"
+
+ R"(
+Try --helpfull to get a list of all flags or --help=substring shows help for
+flags which include specified substring in either in the name, or description or
+path.
)");
}
// --------------------------------------------------------------------
TEST_F(UsageReportingTest, TestUsageFlag_helppackage) {
- absl::SetFlag(&FLAGS_helppackage, true);
+ flags::SetFlagsHelpMode(flags::HelpMode::kPackage);
std::stringstream test_buf;
- EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 1);
- EXPECT_EQ(test_buf.str(),
- R"(usage_test: Custom usage message
+ EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage),
+ flags::HelpMode::kPackage);
+ EXPECT_EQ(
+ test_buf.str(),
+ R"(usage_test: Custom usage message
Flags from absl/flags/internal/usage_test.cc:
--usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message);
@@ -326,19 +435,26 @@ TEST_F(UsageReportingTest, TestUsageFlag_helppackage) {
Some more help.
Even more long long long long long long long long long long long long help
- message.); default: "";
+ message.); default: "";)"
-Try --helpfull to get a list of all flags.
+ "\n --usage_reporting_test_flag_07 (usage_reporting_test_flag_07 "
+ "help\n\n \f\v\a\b ); default: \"\r\n\f\v\a\b\t \";\n"
+
+ R"(
+Try --helpfull to get a list of all flags or --help=substring shows help for
+flags which include specified substring in either in the name, or description or
+path.
)");
}
// --------------------------------------------------------------------
TEST_F(UsageReportingTest, TestUsageFlag_version) {
- absl::SetFlag(&FLAGS_version, true);
+ flags::SetFlagsHelpMode(flags::HelpMode::kVersion);
std::stringstream test_buf;
- EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 0);
+ EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage),
+ flags::HelpMode::kVersion);
#ifndef NDEBUG
EXPECT_EQ(test_buf.str(), "usage_test\nDebug build (NDEBUG not #defined)\n");
#else
@@ -349,32 +465,41 @@ TEST_F(UsageReportingTest, TestUsageFlag_version) {
// --------------------------------------------------------------------
TEST_F(UsageReportingTest, TestUsageFlag_only_check_args) {
- absl::SetFlag(&FLAGS_only_check_args, true);
+ flags::SetFlagsHelpMode(flags::HelpMode::kOnlyCheckArgs);
std::stringstream test_buf;
- EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 0);
+ EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage),
+ flags::HelpMode::kOnlyCheckArgs);
EXPECT_EQ(test_buf.str(), "");
}
// --------------------------------------------------------------------
TEST_F(UsageReportingTest, TestUsageFlag_helpon) {
- absl::SetFlag(&FLAGS_helpon, "bla-bla");
+ flags::SetFlagsHelpMode(flags::HelpMode::kMatch);
+ flags::SetFlagsHelpMatchSubstr("/bla-bla.");
std::stringstream test_buf_01;
- EXPECT_EQ(flags::HandleUsageFlags(test_buf_01, kTestUsageMessage), 1);
+ EXPECT_EQ(flags::HandleUsageFlags(test_buf_01, kTestUsageMessage),
+ flags::HelpMode::kMatch);
EXPECT_EQ(test_buf_01.str(),
R"(usage_test: Custom usage message
- No modules matched: use -helpfull
+No flags matched.
+
+Try --helpfull to get a list of all flags or --help=substring shows help for
+flags which include specified substring in either in the name, or description or
+path.
)");
- absl::SetFlag(&FLAGS_helpon, "usage_test");
+ flags::SetFlagsHelpMatchSubstr("/usage_test.");
std::stringstream test_buf_02;
- EXPECT_EQ(flags::HandleUsageFlags(test_buf_02, kTestUsageMessage), 1);
- EXPECT_EQ(test_buf_02.str(),
- R"(usage_test: Custom usage message
+ EXPECT_EQ(flags::HandleUsageFlags(test_buf_02, kTestUsageMessage),
+ flags::HelpMode::kMatch);
+ EXPECT_EQ(
+ test_buf_02.str(),
+ R"(usage_test: Custom usage message
Flags from absl/flags/internal/usage_test.cc:
--usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message);
@@ -391,7 +516,15 @@ TEST_F(UsageReportingTest, TestUsageFlag_helpon) {
Some more help.
Even more long long long long long long long long long long long long help
- message.); default: "";
+ message.); default: "";)"
+
+ "\n --usage_reporting_test_flag_07 (usage_reporting_test_flag_07 "
+ "help\n\n \f\v\a\b ); default: \"\r\n\f\v\a\b\t \";\n"
+
+ R"(
+Try --helpfull to get a list of all flags or --help=substring shows help for
+flags which include specified substring in either in the name, or description or
+path.
)");
}
@@ -402,8 +535,10 @@ TEST_F(UsageReportingTest, TestUsageFlag_helpon) {
int main(int argc, char* argv[]) {
(void)absl::GetFlag(FLAGS_undefok); // Force linking of parse.cc
flags::SetProgramInvocationName("usage_test");
+#if !defined(GTEST_HAS_ABSL) || !GTEST_HAS_ABSL
+ // GoogleTest calls absl::SetProgramUsageMessage() already.
absl::SetProgramUsageMessage(kTestUsageMessage);
+#endif
::testing::InitGoogleTest(&argc, argv);
-
return RUN_ALL_TESTS();
}