Export of internal Abseil changes

--
b770d03c2f1042d399c3f9576e881691cbe962c4 by Abseil Team <absl-team@google.com>:

Avoid applying the workaround for MSVC's static initialization problems when using clang-cl.

PiperOrigin-RevId: 275870089

--
2b8fc02ec49aeb5ad56cef20259cdd7f6ee0c917 by Abseil Team <absl-team@google.com>:

Document return values.

PiperOrigin-RevId: 275839042

--
baa99064f9a28b188661df7fba989fcd558818af by Abseil Team <absl-team@google.com>:

Support "auto" and other uncommon builtin types in absl::debugging_internal::Demangle.

PiperOrigin-RevId: 275556195

--
f9d5c6a3a0d374dbf105d6e2e9e0c8fa949ed187 by Gennadiy Rozental <rogeeff@google.com>:

Internal rework.

PiperOrigin-RevId: 275550005

--
2679a77db5b26349e8c8b2059621af55d2fca139 by Mark Barolak <mbar@google.com>:

Remove a comment reference to the no longer extant ::string implementation.

PiperOrigin-RevId: 275531987

--
7b427a7613c44a98c6f13da43b2bff2837ca6b19 by Derek Mauro <dmauro@google.com>:

Upgrade to Bazel 1.0.0 and CMake 3.15.4

PiperOrigin-RevId: 275500823

--
81f7d20905debf9d1e300bd2e9899f88d27f632a by Derek Mauro <dmauro@google.com>:

Fix -Wimplicit-int-float-conversion warning in latest clang

PiperOrigin-RevId: 275492439
GitOrigin-RevId: b770d03c2f1042d399c3f9576e881691cbe962c4
Change-Id: I9b39dad524489f0d62c912d02e8ac43761c81e55
This commit is contained in:
Abseil Team 2019-10-21 10:21:03 -07:00 committed by Derek Mauro
parent e4c8d0eb8e
commit 2796d500ae
23 changed files with 639 additions and 635 deletions

View file

@ -93,6 +93,8 @@ static const AbbrevPair kOperatorList[] = {
};
// List of builtin types from Itanium C++ ABI.
//
// Invariant: only one- or two-character type abbreviations here.
static const AbbrevPair kBuiltinTypeList[] = {
{"v", "void", 0},
{"w", "wchar_t", 0},
@ -115,6 +117,16 @@ static const AbbrevPair kBuiltinTypeList[] = {
{"e", "long double", 0},
{"g", "__float128", 0},
{"z", "ellipsis", 0},
{"De", "decimal128", 0}, // IEEE 754r decimal floating point (128 bits)
{"Dd", "decimal64", 0}, // IEEE 754r decimal floating point (64 bits)
{"Dc", "decltype(auto)", 0},
{"Da", "auto", 0},
{"Dn", "std::nullptr_t", 0}, // i.e., decltype(nullptr)
{"Df", "decimal32", 0}, // IEEE 754r decimal floating point (32 bits)
{"Di", "char32_t", 0},
{"Ds", "char16_t", 0},
{"Dh", "float16", 0}, // IEEE 754r half-precision float (16 bits)
{nullptr, nullptr, 0},
};
@ -1168,12 +1180,6 @@ static bool ParseType(State *state) {
}
state->parse_state = copy;
// nullptr_t, i.e. decltype(nullptr).
if (ParseTwoCharToken(state, "Dn")) {
return true;
}
state->parse_state = copy;
if (ParseOneCharToken(state, 'U') && ParseSourceName(state) &&
ParseType(state)) {
return true;
@ -1214,16 +1220,26 @@ static bool ParseCVQualifiers(State *state) {
return num_cv_qualifiers > 0;
}
// <builtin-type> ::= v, etc.
// <builtin-type> ::= v, etc. # single-character builtin types
// ::= u <source-name>
// ::= Dd, etc. # two-character builtin types
//
// Not supported:
// ::= DF <number> _ # _FloatN (N bits)
//
static bool ParseBuiltinType(State *state) {
ComplexityGuard guard(state);
if (guard.IsTooComplex()) return false;
const AbbrevPair *p;
for (p = kBuiltinTypeList; p->abbrev != nullptr; ++p) {
if (RemainingInput(state)[0] == p->abbrev[0]) {
// Guaranteed only 1- or 2-character strings in kBuiltinTypeList.
if (p->abbrev[1] == '\0') {
if (ParseOneCharToken(state, p->abbrev[0])) {
MaybeAppend(state, p->real_name);
return true;
}
} else if (p->abbrev[2] == '\0' && ParseTwoCharToken(state, p->abbrev)) {
MaybeAppend(state, p->real_name);
++state->parse_state.mangled_idx;
return true;
}
}

View file

@ -36,10 +36,10 @@ namespace absl {
// GetStackFrames()
//
// Records program counter values for up to `max_depth` frames, skipping the
// most recent `skip_count` stack frames, and stores their corresponding values
// and sizes in `results` and `sizes` buffers. (Note that the frame generated
// for the `absl::GetStackFrames()` routine itself is also skipped.)
// routine itself.
// most recent `skip_count` stack frames, stores their corresponding values
// and sizes in `results` and `sizes` buffers, and returns the number of frames
// stored. (Note that the frame generated for the `absl::GetStackFrames()`
// routine itself is also skipped.)
//
// Example:
//
@ -54,8 +54,8 @@ namespace absl {
// The current stack frame would consist of three function calls: `bar()`,
// `foo()`, and then `main()`; however, since the `GetStackFrames()` call sets
// `skip_count` to `1`, it will skip the frame for `bar()`, the most recently
// invoked function call. It will therefore return two program counters and will
// produce values that map to the following function calls:
// invoked function call. It will therefore return 2 and fill `result` with
// program counters within the following functions:
//
// result[0] foo()
// result[1] main()
@ -82,9 +82,10 @@ extern int GetStackFrames(void** result, int* sizes, int max_depth,
//
// Records program counter values obtained from a signal handler. Records
// program counter values for up to `max_depth` frames, skipping the most recent
// `skip_count` stack frames, and stores their corresponding values and sizes in
// `results` and `sizes` buffers. (Note that the frame generated for the
// `absl::GetStackFramesWithContext()` routine itself is also skipped.)
// `skip_count` stack frames, stores their corresponding values and sizes in
// `results` and `sizes` buffers, and returns the number of frames stored. (Note
// that the frame generated for the `absl::GetStackFramesWithContext()` routine
// itself is also skipped.)
//
// The `uc` parameter, if non-null, should be a pointer to a `ucontext_t` value
// passed to a signal handler registered via the `sa_sigaction` field of a
@ -105,8 +106,9 @@ extern int GetStackFramesWithContext(void** result, int* sizes, int max_depth,
// GetStackTrace()
//
// Records program counter values for up to `max_depth` frames, skipping the
// most recent `skip_count` stack frames, and stores their corresponding values
// in `results`. Note that this function is similar to `absl::GetStackFrames()`
// most recent `skip_count` stack frames, stores their corresponding values
// in `results`, and returns the number of frames
// stored. Note that this function is similar to `absl::GetStackFrames()`
// except that it returns the stack trace only, and not stack frame sizes.
//
// Example:
@ -131,9 +133,9 @@ extern int GetStackTrace(void** result, int max_depth, int skip_count);
//
// Records program counter values obtained from a signal handler. Records
// program counter values for up to `max_depth` frames, skipping the most recent
// `skip_count` stack frames, and stores their corresponding values in
// `results`. (Note that the frame generated for the
// `absl::GetStackFramesWithContext()` routine itself is also skipped.)
// `skip_count` stack frames, stores their corresponding values in `results`,
// and returns the number of frames stored. (Note that the frame generated for
// the `absl::GetStackFramesWithContext()` routine itself is also skipped.)
//
// The `uc` parameter, if non-null, should be a pointer to a `ucontext_t` value
// passed to a signal handler registered via the `sa_sigaction` field of a

View file

@ -40,6 +40,7 @@ cc_library(
deps = [
":handle",
":registry",
"//absl/base:core_headers",
"//absl/memory",
"//absl/strings",
"//absl/synchronization",
@ -135,9 +136,6 @@ cc_library(
":config",
":marshalling",
"//absl/base:core_headers",
"//absl/base:raw_logging_internal",
"//absl/strings",
"//absl/synchronization",
"//absl/types:optional",
],
)

View file

@ -39,7 +39,7 @@ class Flag;
// Flag
//
// Forward declaration of the `absl::Flag` type for use in defining the macro.
#if defined(_MSC_VER)
#if defined(_MSC_VER) && !defined(__clang__)
template <typename T>
class Flag;
#else

View file

@ -43,7 +43,7 @@ ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(ABSL_FLAGS_ATOMIC_GET)
// This global nutex protects on-demand construction of flag objects in MSVC
// builds.
#if defined(_MSC_VER)
#if defined(_MSC_VER) && !defined(__clang__)
namespace flags_internal {

View file

@ -63,7 +63,7 @@ namespace absl {
// ABSL_FLAG(int, count, 0, "Count of items to process");
//
// No public methods of `absl::Flag<T>` are part of the Abseil Flags API.
#if !defined(_MSC_VER)
#if !defined(_MSC_VER) || defined(__clang__)
template <typename T>
using Flag = flags_internal::Flag<T>;
#else
@ -119,7 +119,6 @@ class Flag {
absl::string_view Name() const { return GetImpl()->Name(); }
std::string Help() const { return GetImpl()->Help(); }
bool IsModified() const { return GetImpl()->IsModified(); }
void SetModified(bool is_modified) { GetImpl()->SetModified(is_modified); }
bool IsSpecifiedOnCommandLine() const {
return GetImpl()->IsSpecifiedOnCommandLine();
}
@ -127,9 +126,6 @@ class Flag {
std::string Filename() const { return GetImpl()->Filename(); }
std::string DefaultValue() const { return GetImpl()->DefaultValue(); }
std::string CurrentValue() const { return GetImpl()->CurrentValue(); }
bool InvokeValidator(const void* value) const {
return GetImpl()->InvokeValidator(value);
}
template <typename T1>
inline bool IsOfType() const {
return GetImpl()->template IsOfType<T1>();
@ -272,7 +268,7 @@ void SetFlag(absl::Flag<T>* flag, const V& v) {
#if ABSL_FLAGS_STRIP_NAMES
#define ABSL_FLAG_IMPL_FLAGNAME(txt) ""
#define ABSL_FLAG_IMPL_FILENAME() ""
#if !defined(_MSC_VER)
#if !defined(_MSC_VER) || defined(__clang__)
#define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \
absl::flags_internal::FlagRegistrar<T, false>(&flag)
#else
@ -282,7 +278,7 @@ void SetFlag(absl::Flag<T>* flag, const V& v) {
#else
#define ABSL_FLAG_IMPL_FLAGNAME(txt) txt
#define ABSL_FLAG_IMPL_FILENAME() __FILE__
#if !defined(_MSC_VER)
#if !defined(_MSC_VER) || defined(__clang__)
#define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \
absl::flags_internal::FlagRegistrar<T, true>(&flag)
#else

View file

@ -42,14 +42,13 @@ void TestCallback() {}
template <typename T>
bool TestConstructionFor() {
constexpr flags::Flag<T> f1("f1", &TestHelpMsg, "file",
&absl::flags_internal::FlagMarshallingOps<T>,
&TestMakeDflt<T>);
&flags::FlagMarshallingOps<T>, &TestMakeDflt<T>);
EXPECT_EQ(f1.Name(), "f1");
EXPECT_EQ(f1.Help(), "help");
EXPECT_EQ(f1.Filename(), "file");
ABSL_CONST_INIT static flags::Flag<T> f2(
"f2", &TestHelpMsg, "file", &absl::flags_internal::FlagMarshallingOps<T>,
ABSL_CONST_INIT static flags::Flag<T> f2("f2", &TestHelpMsg, "file",
&flags::FlagMarshallingOps<T>,
&TestMakeDflt<T>);
flags::FlagRegistrar<T, false>(&f2).OnUpdate(TestCallback);

View file

@ -15,14 +15,7 @@
#include "absl/flags/internal/commandlineflag.h"
#include <cassert>
#include "absl/base/internal/raw_logging.h"
#include "absl/base/optimization.h"
#include "absl/flags/config.h"
#include "absl/flags/usage_config.h"
#include "absl/strings/str_cat.h"
#include "absl/synchronization/mutex.h"
namespace absl {
namespace flags_internal {
@ -35,80 +28,6 @@ namespace flags_internal {
// This is used by this file, and also in commandlineflags_reporting.cc
const char kStrippedFlagHelp[] = "\001\002\003\004 (unknown) \004\003\002\001";
namespace {
// Currently we only validate flag values for user-defined flag types.
bool ShouldValidateFlagValue(const CommandLineFlag& flag) {
#define DONT_VALIDATE(T) \
if (flag.IsOfType<T>()) return false;
ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(DONT_VALIDATE)
DONT_VALIDATE(std::string)
DONT_VALIDATE(std::vector<std::string>)
#undef DONT_VALIDATE
return true;
}
} // namespace
absl::Mutex* InitFlag(CommandLineFlag* flag) {
ABSL_CONST_INIT static absl::Mutex init_lock(absl::kConstInit);
absl::Mutex* mu;
{
absl::MutexLock lock(&init_lock);
if (flag->locks_ == nullptr) { // Must initialize Mutexes for this flag.
flag->locks_ = new flags_internal::CommandLineFlagLocks;
}
mu = &flag->locks_->primary_mu;
}
{
absl::MutexLock lock(mu);
if (!flag->IsRetired() && flag->def_ == nullptr) {
// Need to initialize def and cur fields.
flag->def_ = (*flag->make_init_value_)();
flag->cur_ = Clone(flag->op_, flag->def_);
UpdateCopy(flag);
flag->inited_.store(true, std::memory_order_release);
flag->InvokeCallback();
}
}
flag->inited_.store(true, std::memory_order_release);
return mu;
}
// Ensure that the lazily initialized fields of *flag have been initialized,
// and return &flag->locks_->primary_mu.
absl::Mutex* CommandLineFlag::InitFlagIfNecessary() const
ABSL_LOCK_RETURNED(locks_->primary_mu) {
if (!inited_.load(std::memory_order_acquire)) {
return InitFlag(const_cast<CommandLineFlag*>(this));
}
// All fields initialized; locks_ is therefore safe to read.
return &locks_->primary_mu;
}
bool CommandLineFlag::IsModified() const {
absl::MutexLock l(InitFlagIfNecessary());
return modified_;
}
void CommandLineFlag::SetModified(bool is_modified) {
absl::MutexLock l(InitFlagIfNecessary());
modified_ = is_modified;
}
bool CommandLineFlag::IsSpecifiedOnCommandLine() const {
absl::MutexLock l(InitFlagIfNecessary());
return on_command_line_;
}
absl::string_view CommandLineFlag::Typename() const {
// We do not store/report type in Abseil Flags, so that user do not rely on in
// at runtime
@ -137,223 +56,6 @@ std::string CommandLineFlag::Filename() const {
return flags_internal::GetUsageConfig().normalize_filename(filename_);
}
std::string CommandLineFlag::DefaultValue() const {
absl::MutexLock l(InitFlagIfNecessary());
return Unparse(marshalling_op_, def_);
}
std::string CommandLineFlag::CurrentValue() const {
absl::MutexLock l(InitFlagIfNecessary());
return Unparse(marshalling_op_, cur_);
}
int64_t CommandLineFlag::MutationCounter() const {
absl::MutexLock l(InitFlagIfNecessary());
return counter_;
}
// Attempts to parse supplied `value` string using parsing routine in the `flag`
// argument. If parsing is successful, it will try to validate that the parsed
// value is valid for the specified 'flag'. Finally this function stores the
// parsed value in 'dst' assuming it is a pointer to the flag's value type. In
// case if any error is encountered in either step, the error message is stored
// in 'err'
bool TryParseLocked(CommandLineFlag* flag, void* dst, absl::string_view value,
std::string* err)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(flag->locks_->primary_mu) {
void* tentative_value = Clone(flag->op_, flag->def_);
std::string parse_err;
if (!Parse(flag->marshalling_op_, value, tentative_value, &parse_err)) {
auto type_name = flag->Typename();
absl::string_view err_sep = parse_err.empty() ? "" : "; ";
absl::string_view typename_sep = type_name.empty() ? "" : " ";
*err = absl::StrCat("Illegal value '", value, "' specified for",
typename_sep, type_name, " flag '", flag->Name(), "'",
err_sep, parse_err);
Delete(flag->op_, tentative_value);
return false;
}
if (!flag->InvokeValidator(tentative_value)) {
*err = absl::StrCat("Failed validation of new value '",
Unparse(flag->marshalling_op_, tentative_value),
"' for flag '", flag->Name(), "'");
Delete(flag->op_, tentative_value);
return false;
}
flag->counter_++;
Copy(flag->op_, tentative_value, dst);
Delete(flag->op_, tentative_value);
return true;
}
// Sets the value of the flag based on specified string `value`. If the flag
// was successfully set to new value, it returns true. Otherwise, sets `err`
// to indicate the error, leaves the flag unchanged, and returns false. There
// are three ways to set the flag's value:
// * Update the current flag value
// * Update the flag's default value
// * Update the current flag value if it was never set before
// The mode is selected based on 'set_mode' parameter.
bool CommandLineFlag::SetFromString(absl::string_view value,
FlagSettingMode set_mode,
ValueSource source, std::string* err) {
if (IsRetired()) return false;
absl::MutexLock l(InitFlagIfNecessary());
// Direct-access flags can be modified without going through the
// flag API. Detect such changes and update the flag->modified_ bit.
if (!IsAbseilFlag()) {
if (!modified_ && ChangedDirectly(this, cur_, def_)) {
modified_ = true;
}
}
switch (set_mode) {
case SET_FLAGS_VALUE: {
// set or modify the flag's value
if (!TryParseLocked(this, cur_, value, err)) return false;
modified_ = true;
UpdateCopy(this);
InvokeCallback();
if (source == kCommandLine) {
on_command_line_ = true;
}
break;
}
case SET_FLAG_IF_DEFAULT: {
// set the flag's value, but only if it hasn't been set by someone else
if (!modified_) {
if (!TryParseLocked(this, cur_, value, err)) return false;
modified_ = true;
UpdateCopy(this);
InvokeCallback();
} else {
// TODO(rogeeff): review and fix this semantic. Currently we do not fail
// in this case if flag is modified. This is misleading since the flag's
// value is not updated even though we return true.
// *err = absl::StrCat(Name(), " is already set to ",
// CurrentValue(), "\n");
// return false;
return true;
}
break;
}
case SET_FLAGS_DEFAULT: {
// modify the flag's default-value
if (!TryParseLocked(this, def_, value, err)) return false;
if (!modified_) {
// Need to set both defvalue *and* current, in this case
Copy(op_, def_, cur_);
UpdateCopy(this);
InvokeCallback();
}
break;
}
default: {
// unknown set_mode
assert(false);
return false;
}
}
return true;
}
void CommandLineFlag::CheckDefaultValueParsingRoundtrip() const {
std::string v = DefaultValue();
absl::MutexLock lock(InitFlagIfNecessary());
void* dst = Clone(op_, def_);
std::string error;
if (!flags_internal::Parse(marshalling_op_, v, dst, &error)) {
ABSL_INTERNAL_LOG(
FATAL,
absl::StrCat("Flag ", Name(), " (from ", Filename(),
"): std::string form of default value '", v,
"' could not be parsed; error=", error));
}
// We do not compare dst to def since parsing/unparsing may make
// small changes, e.g., precision loss for floating point types.
Delete(op_, dst);
}
bool CommandLineFlag::ValidateDefaultValue() const {
absl::MutexLock lock(InitFlagIfNecessary());
return InvokeValidator(def_);
}
bool CommandLineFlag::ValidateInputValue(absl::string_view value) const {
absl::MutexLock l(InitFlagIfNecessary()); // protect default value access
void* obj = Clone(op_, def_);
std::string ignored_error;
const bool result =
flags_internal::Parse(marshalling_op_, value, obj, &ignored_error) &&
InvokeValidator(obj);
Delete(op_, obj);
return result;
}
void CommandLineFlag::Read(void* dst,
const flags_internal::FlagOpFn dst_op) const {
absl::ReaderMutexLock l(InitFlagIfNecessary());
// `dst_op` is the unmarshaling operation corresponding to the declaration
// visibile at the call site. `op` is the Flag's defined unmarshalling
// operation. They must match for this operation to be well-defined.
if (ABSL_PREDICT_FALSE(dst_op != op_)) {
ABSL_INTERNAL_LOG(
ERROR,
absl::StrCat("Flag '", Name(),
"' is defined as one type and declared as another"));
}
CopyConstruct(op_, cur_, dst);
}
void CommandLineFlag::Write(const void* src,
const flags_internal::FlagOpFn src_op) {
absl::MutexLock l(InitFlagIfNecessary());
// `src_op` is the marshalling operation corresponding to the declaration
// visible at the call site. `op` is the Flag's defined marshalling operation.
// They must match for this operation to be well-defined.
if (ABSL_PREDICT_FALSE(src_op != op_)) {
ABSL_INTERNAL_LOG(
ERROR,
absl::StrCat("Flag '", Name(),
"' is defined as one type and declared as another"));
}
if (ShouldValidateFlagValue(*this)) {
void* obj = Clone(op_, src);
std::string ignored_error;
std::string src_as_str = Unparse(marshalling_op_, src);
if (!Parse(marshalling_op_, src_as_str, obj, &ignored_error) ||
!InvokeValidator(obj)) {
ABSL_INTERNAL_LOG(ERROR, absl::StrCat("Attempt to set flag '", Name(),
"' to invalid value ", src_as_str));
}
Delete(op_, obj);
}
modified_ = true;
counter_++;
Copy(op_, src, cur_);
UpdateCopy(this);
InvokeCallback();
}
std::string HelpText::GetHelpText() const {
if (help_function_) return help_function_();
if (help_message_) return help_message_;
@ -361,40 +63,5 @@ std::string HelpText::GetHelpText() const {
return {};
}
// Update any copy of the flag value that is stored in an atomic word.
// In addition if flag has a mutation callback this function invokes it.
void UpdateCopy(CommandLineFlag* flag) {
#define STORE_ATOMIC(T) \
else if (flag->IsOfType<T>()) { \
flag->StoreAtomic(); \
}
if (false) {
}
ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(STORE_ATOMIC)
#undef STORE_ATOMIC
}
// Return true iff flag value was changed via direct-access.
bool ChangedDirectly(CommandLineFlag* flag, const void* a, const void* b) {
if (!flag->IsAbseilFlag()) {
// Need to compare values for direct-access flags.
#define CHANGED_FOR_TYPE(T) \
if (flag->IsOfType<T>()) { \
return *reinterpret_cast<const T*>(a) != *reinterpret_cast<const T*>(b); \
}
CHANGED_FOR_TYPE(bool);
CHANGED_FOR_TYPE(int32_t);
CHANGED_FOR_TYPE(int64_t);
CHANGED_FOR_TYPE(uint64_t);
CHANGED_FOR_TYPE(double);
CHANGED_FOR_TYPE(std::string);
#undef CHANGED_FOR_TYPE
}
return false;
}
} // namespace flags_internal
} // namespace absl

View file

@ -16,12 +16,10 @@
#ifndef ABSL_FLAGS_INTERNAL_COMMANDLINEFLAG_H_
#define ABSL_FLAGS_INTERNAL_COMMANDLINEFLAG_H_
#include <atomic>
#include <memory>
#include "absl/base/macros.h"
#include "absl/flags/marshalling.h"
#include "absl/synchronization/mutex.h"
#include "absl/types/optional.h"
namespace absl {
@ -151,14 +149,6 @@ inline size_t Sizeof(FlagOpFn op) {
op(flags_internal::kSizeof, nullptr, nullptr)));
}
// The following struct contains the locks in a CommandLineFlag struct.
// They are in a separate struct that is lazily allocated to avoid problems
// with static initialization and to avoid multiple allocations.
struct CommandLineFlagLocks {
absl::Mutex primary_mu; // protects several fields in CommandLineFlag
absl::Mutex callback_mu; // used to serialize callbacks
};
// Holds either a pointer to help text or a function which produces it. This is
// needed for supporting both static initialization of Flags while supporting
// the legacy registration framework. We can't use absl::variant<const char*,
@ -200,25 +190,9 @@ class FlagStateInterface {
// Holds all information for a flag.
class CommandLineFlag {
public:
constexpr CommandLineFlag(
const char* name, HelpText help_text, const char* filename,
const flags_internal::FlagOpFn op,
const flags_internal::FlagMarshallingOpFn marshalling_op,
const flags_internal::InitialValGenFunc initial_value_gen, void* def,
void* cur)
: name_(name),
help_(help_text),
filename_(filename),
op_(op),
marshalling_op_(marshalling_op),
make_init_value_(initial_value_gen),
inited_(false),
modified_(false),
on_command_line_(false),
def_(def),
cur_(cur),
counter_(0),
locks_(nullptr) {}
constexpr CommandLineFlag(const char* name, HelpText help_text,
const char* filename)
: name_(name), help_(help_text), filename_(filename) {}
// Virtual destructor
virtual void Destroy() const = 0;
@ -227,60 +201,73 @@ class CommandLineFlag {
CommandLineFlag(const CommandLineFlag&) = delete;
CommandLineFlag& operator=(const CommandLineFlag&) = delete;
// Access methods.
// Returns true iff this object corresponds to retired flag
virtual bool IsRetired() const { return false; }
// Returns true iff this is a handle to an Abseil Flag.
virtual bool IsAbseilFlag() const { return true; }
// Non-polymorphic access methods.
absl::string_view Name() const { return name_; }
std::string Help() const { return help_.GetHelpText(); }
bool IsModified() const;
void SetModified(bool is_modified);
bool IsSpecifiedOnCommandLine() const;
absl::string_view Typename() const;
std::string Filename() const;
std::string DefaultValue() const;
std::string CurrentValue() const;
// Interface to store the value in atomic if one used. This is short term
// interface. To be reworked once cur_ is moved.
virtual void StoreAtomic() {}
// Interfaces to operate on validators.
virtual bool InvokeValidator(const void* /*value*/) const { return true; }
// Invoke the flag validators for old flags.
// TODO(rogeeff): implement proper validators for Abseil Flags
bool ValidateDefaultValue() const;
bool ValidateInputValue(absl::string_view value) const;
// Return true iff flag has type T.
template <typename T>
inline bool IsOfType() const {
return op_ == &flags_internal::FlagOps<T>;
return TypeId() == &flags_internal::FlagOps<T>;
}
// Attempts to retrieve the flag value. Returns value on success,
// absl::nullopt otherwise.
template <typename T>
absl::optional<T> Get() const {
if (IsRetired() || flags_internal::FlagOps<T> != op_) return absl::nullopt;
T res;
Read(&res, flags_internal::FlagOps<T>);
return res;
if (IsRetired() || !IsOfType<T>()) {
return absl::nullopt;
}
// Implementation notes:
//
// We are wrapping a union around the value of `T` to serve three purposes:
//
// 1. `U.value` has correct size and alignment for a value of type `T`
// 2. The `U.value` constructor is not invoked since U's constructor does
// not
// do it explicitly.
// 3. The `U.value` destructor is invoked since U's destructor does it
// explicitly. This makes `U` a kind of RAII wrapper around non default
// constructible value of T, which is destructed when we leave the
// scope. We do need to destroy U.value, which is constructed by
// CommandLineFlag::Read even though we left it in a moved-from state
// after std::move.
//
// All of this serves to avoid requiring `T` being default constructible.
union U {
T value;
U() {}
~U() { value.~T(); }
};
U u;
Read(&u.value);
return std::move(u.value);
}
// Polymorphic access methods
// Returns true iff this object corresponds to retired flag
virtual bool IsRetired() const { return false; }
// Returns true iff this is a handle to an Abseil Flag.
virtual bool IsAbseilFlag() const { return true; }
// Returns id of the flag's value type.
virtual flags_internal::FlagOpFn TypeId() const = 0;
virtual bool IsModified() const = 0;
virtual bool IsSpecifiedOnCommandLine() const = 0;
virtual std::string DefaultValue() const = 0;
virtual std::string CurrentValue() const = 0;
// Interfaces to operate on validators.
virtual bool ValidateInputValue(absl::string_view value) const = 0;
// Interface to save flag to some persistent state. Returns current flag state
// or nullptr if flag does not support saving and restoring a state.
virtual std::unique_ptr<FlagStateInterface> SaveState() = 0;
// Interfaces to overate on callbacks.
virtual void InvokeCallback() {}
// Sets the value of the flag based on specified std::string `value`. If the flag
// was successfully set to new value, it returns true. Otherwise, sets `error`
// to indicate the error, leaves the flag unchanged, and returns false. There
@ -289,75 +276,29 @@ class CommandLineFlag {
// * Update the flag's default value
// * Update the current flag value if it was never set before
// The mode is selected based on `set_mode` parameter.
bool SetFromString(absl::string_view value,
virtual bool SetFromString(absl::string_view value,
flags_internal::FlagSettingMode set_mode,
flags_internal::ValueSource source, std::string* error);
flags_internal::ValueSource source,
std::string* error) = 0;
void CheckDefaultValueParsingRoundtrip() const;
// Checks that flags default value can be converted to std::string and back to the
// flag's value type.
virtual void CheckDefaultValueParsingRoundtrip() const = 0;
// Constant configuration for a particular flag.
protected:
~CommandLineFlag() = default;
// Thread safe access to mutation counter.
int64_t MutationCounter() const;
const char* const name_; // Flags name passed to ABSL_FLAG as second arg.
const HelpText help_; // The function generating help message.
const char* const filename_; // The file name where ABSL_FLAG resides.
const char* const name_;
const HelpText help_;
const char* const filename_;
const FlagOpFn op_; // Type-specific handler
const FlagMarshallingOpFn marshalling_op_; // Marshalling ops handler
const InitialValGenFunc make_init_value_; // Makes initial value for the flag
std::atomic<bool> inited_; // fields have been lazily initialized
// Mutable state (guarded by locks_->primary_mu).
bool modified_; // Has flag value been modified?
bool on_command_line_; // Specified on command line.
void* def_; // Lazily initialized pointer to default value
void* cur_; // Lazily initialized pointer to current value
int64_t counter_; // Mutation counter
// Lazily initialized mutexes for this flag value. We cannot inline a
// SpinLock or Mutex here because those have non-constexpr constructors and
// so would prevent constant initialization of this type.
// TODO(rogeeff): fix it once Mutex has constexpr constructor
struct CommandLineFlagLocks* locks_; // locks, laziliy allocated.
// Ensure that the lazily initialized fields of *flag have been initialized,
// and return the lock which should be locked when flag's state is mutated.
absl::Mutex* InitFlagIfNecessary() const ABSL_LOCK_RETURNED(locks_->primary_mu);
// copy construct new value of flag's type in a memory referenced by dst
// based on current flag's value
void Read(void* dst, const flags_internal::FlagOpFn dst_op) const;
// updates flag's value to *src (locked)
void Write(const void* src, const flags_internal::FlagOpFn src_op);
friend class FlagRegistry;
friend class FlagPtrMap;
friend class FlagSaverImpl;
friend bool TryParseLocked(CommandLineFlag* flag, void* dst,
absl::string_view value, std::string* err);
friend absl::Mutex* InitFlag(CommandLineFlag* flag);
private:
// Copy-construct a new value of the flag's type in a memory referenced by
// the dst based on the current flag's value.
virtual void Read(void* dst) const = 0;
};
// Update any copy of the flag value that is stored in an atomic word.
// In addition if flag has a mutation callback this function invokes it. While
// callback is being invoked the primary flag's mutex is unlocked and it is
// re-locked back after call to callback is completed. Callback invocation is
// guarded by flag's secondary mutex instead which prevents concurrent callback
// invocation. Note that it is possible for other thread to grab the primary
// lock and update flag's value at any time during the callback invocation.
// This is by design. Callback can get a value of the flag if necessary, but it
// might be different from the value initiated the callback and it also can be
// different by the time the callback invocation is completed.
// Requires that *primary_lock be held in exclusive mode; it may be released
// and reacquired by the implementation.
void UpdateCopy(CommandLineFlag* flag);
// Return true iff flag value was changed via direct-access.
bool ChangedDirectly(CommandLineFlag* flag, const void* a, const void* b);
// This macro is the "source of truth" for the list of supported flag types we
// expect to perform lock free operations on. Specifically it generates code,
// a one argument macro operating on a type, supplied as a macro argument, for

View file

@ -15,36 +15,331 @@
#include "absl/flags/internal/flag.h"
#include "absl/base/optimization.h"
#include "absl/synchronization/mutex.h"
namespace absl {
namespace flags_internal {
namespace {
// If the flag has a mutation callback this function invokes it. While the
// callback is being invoked the primary flag's mutex is unlocked and it is
// re-locked back after call to callback is completed. Callback invocation is
// guarded by flag's secondary mutex instead which prevents concurrent
// callback invocation. Note that it is possible for other thread to grab the
// primary lock and update flag's value at any time during the callback
// invocation. This is by design. Callback can get a value of the flag if
// necessary, but it might be different from the value initiated the callback
// and it also can be different by the time the callback invocation is
// completed. Requires that *primary_lock be held in exclusive mode; it may be
// released and reacquired by the implementation.
void InvokeCallback(absl::Mutex* primary_mu, absl::Mutex* callback_mu,
FlagCallback cb) ABSL_EXCLUSIVE_LOCKS_REQUIRED(primary_mu) {
if (!cb) return;
// Currently we only validate flag values for user-defined flag types.
bool ShouldValidateFlagValue(const CommandLineFlag& flag) {
#define DONT_VALIDATE(T) \
if (flag.IsOfType<T>()) return false;
ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(DONT_VALIDATE)
DONT_VALIDATE(std::string)
DONT_VALIDATE(std::vector<std::string>)
#undef DONT_VALIDATE
// When executing the callback we need the primary flag's mutex to be
// unlocked so that callback can retrieve the flag's value.
primary_mu->Unlock();
return true;
}
} // namespace
void FlagImpl::Init() {
ABSL_CONST_INIT static absl::Mutex init_lock(absl::kConstInit);
{
absl::MutexLock lock(callback_mu);
cb();
absl::MutexLock lock(&init_lock);
if (locks_ == nullptr) { // Must initialize Mutexes for this flag.
locks_ = new FlagImpl::CommandLineFlagLocks;
}
}
primary_mu->Lock();
absl::MutexLock lock(&locks_->primary_mu);
if (def_ != nullptr) {
inited_.store(true, std::memory_order_release);
} else {
// Need to initialize def and cur fields.
def_ = (*initial_value_gen_)();
cur_ = Clone(op_, def_);
StoreAtomic();
inited_.store(true, std::memory_order_release);
InvokeCallback();
}
}
// Ensures that the lazily initialized data is initialized,
// and returns pointer to the mutex guarding flags data.
absl::Mutex* FlagImpl::DataGuard() const
ABSL_LOCK_RETURNED(locks_->primary_mu) {
if (ABSL_PREDICT_FALSE(!inited_.load(std::memory_order_acquire))) {
const_cast<FlagImpl*>(this)->Init();
}
// All fields initialized; locks_ is therefore safe to read.
return &locks_->primary_mu;
}
void FlagImpl::Destroy() const {
{
absl::MutexLock l(DataGuard());
// Values are heap allocated for Abseil Flags.
if (cur_) Delete(op_, cur_);
if (def_) Delete(op_, def_);
}
delete locks_;
}
bool FlagImpl::IsModified() const {
absl::MutexLock l(DataGuard());
return modified_;
}
bool FlagImpl::IsSpecifiedOnCommandLine() const {
absl::MutexLock l(DataGuard());
return on_command_line_;
}
std::string FlagImpl::DefaultValue() const {
absl::MutexLock l(DataGuard());
return Unparse(marshalling_op_, def_);
}
std::string FlagImpl::CurrentValue() const {
absl::MutexLock l(DataGuard());
return Unparse(marshalling_op_, cur_);
}
void FlagImpl::SetCallback(
const flags_internal::FlagCallback mutation_callback) {
absl::MutexLock l(DataGuard());
callback_ = mutation_callback;
InvokeCallback();
}
void FlagImpl::InvokeCallback() const
ABSL_EXCLUSIVE_LOCKS_REQUIRED(locks_->primary_mu) {
if (!callback_) return;
// If the flag has a mutation callback this function invokes it. While the
// callback is being invoked the primary flag's mutex is unlocked and it is
// re-locked back after call to callback is completed. Callback invocation is
// guarded by flag's secondary mutex instead which prevents concurrent
// callback invocation. Note that it is possible for other thread to grab the
// primary lock and update flag's value at any time during the callback
// invocation. This is by design. Callback can get a value of the flag if
// necessary, but it might be different from the value initiated the callback
// and it also can be different by the time the callback invocation is
// completed. Requires that *primary_lock be held in exclusive mode; it may be
// released and reacquired by the implementation.
DataGuard()->Unlock();
{
absl::MutexLock lock(&locks_->callback_mu);
callback_();
}
DataGuard()->Lock();
}
bool FlagImpl::RestoreState(const CommandLineFlag& flag, const void* value,
bool modified, bool on_command_line,
int64_t counter) {
{
absl::MutexLock l(DataGuard());
if (counter_ == counter) return false;
}
Write(flag, value, op_);
{
absl::MutexLock l(DataGuard());
modified_ = modified;
on_command_line_ = on_command_line;
}
return true;
}
// Attempts to parse supplied `value` string using parsing routine in the `flag`
// argument. If parsing successful, this function stores the parsed value in
// 'dst' assuming it is a pointer to the flag's value type. In case if any error
// is encountered in either step, the error message is stored in 'err'
bool FlagImpl::TryParse(const CommandLineFlag& flag, void* dst,
absl::string_view value, std::string* err) const
ABSL_EXCLUSIVE_LOCKS_REQUIRED(locks_->primary_mu) {
void* tentative_value = Clone(op_, def_);
std::string parse_err;
if (!Parse(marshalling_op_, value, tentative_value, &parse_err)) {
auto type_name = flag.Typename();
absl::string_view err_sep = parse_err.empty() ? "" : "; ";
absl::string_view typename_sep = type_name.empty() ? "" : " ";
*err = absl::StrCat("Illegal value '", value, "' specified for",
typename_sep, type_name, " flag '", flag.Name(), "'",
err_sep, parse_err);
Delete(op_, tentative_value);
return false;
}
Copy(op_, tentative_value, dst);
Delete(op_, tentative_value);
return true;
}
void FlagImpl::Read(const CommandLineFlag& flag, void* dst,
const flags_internal::FlagOpFn dst_op) const {
absl::ReaderMutexLock l(DataGuard());
// `dst_op` is the unmarshaling operation corresponding to the declaration
// visibile at the call site. `op` is the Flag's defined unmarshalling
// operation. They must match for this operation to be well-defined.
if (ABSL_PREDICT_FALSE(dst_op != op_)) {
ABSL_INTERNAL_LOG(
ERROR,
absl::StrCat("Flag '", flag.Name(),
"' is defined as one type and declared as another"));
}
CopyConstruct(op_, cur_, dst);
}
void FlagImpl::StoreAtomic() ABSL_EXCLUSIVE_LOCKS_REQUIRED(locks_->primary_mu) {
size_t data_size = Sizeof(op_);
if (data_size <= sizeof(int64_t)) {
int64_t t = 0;
std::memcpy(&t, cur_, data_size);
atomic_.store(t, std::memory_order_release);
}
}
void FlagImpl::Write(const CommandLineFlag& flag, const void* src,
const flags_internal::FlagOpFn src_op) {
absl::MutexLock l(DataGuard());
// `src_op` is the marshalling operation corresponding to the declaration
// visible at the call site. `op` is the Flag's defined marshalling operation.
// They must match for this operation to be well-defined.
if (ABSL_PREDICT_FALSE(src_op != op_)) {
ABSL_INTERNAL_LOG(
ERROR,
absl::StrCat("Flag '", flag.Name(),
"' is defined as one type and declared as another"));
}
if (ShouldValidateFlagValue(flag)) {
void* obj = Clone(op_, src);
std::string ignored_error;
std::string src_as_str = Unparse(marshalling_op_, src);
if (!Parse(marshalling_op_, src_as_str, obj, &ignored_error)) {
ABSL_INTERNAL_LOG(ERROR,
absl::StrCat("Attempt to set flag '", flag.Name(),
"' to invalid value ", src_as_str));
}
Delete(op_, obj);
}
modified_ = true;
counter_++;
Copy(op_, src, cur_);
StoreAtomic();
InvokeCallback();
}
// Sets the value of the flag based on specified string `value`. If the flag
// was successfully set to new value, it returns true. Otherwise, sets `err`
// to indicate the error, leaves the flag unchanged, and returns false. There
// are three ways to set the flag's value:
// * Update the current flag value
// * Update the flag's default value
// * Update the current flag value if it was never set before
// The mode is selected based on 'set_mode' parameter.
bool FlagImpl::SetFromString(const CommandLineFlag& flag,
absl::string_view value, FlagSettingMode set_mode,
ValueSource source, std::string* err) {
absl::MutexLock l(DataGuard());
switch (set_mode) {
case SET_FLAGS_VALUE: {
// set or modify the flag's value
if (!TryParse(flag, cur_, value, err)) return false;
modified_ = true;
counter_++;
StoreAtomic();
InvokeCallback();
if (source == kCommandLine) {
on_command_line_ = true;
}
break;
}
case SET_FLAG_IF_DEFAULT: {
// set the flag's value, but only if it hasn't been set by someone else
if (!modified_) {
if (!TryParse(flag, cur_, value, err)) return false;
modified_ = true;
counter_++;
StoreAtomic();
InvokeCallback();
} else {
// TODO(rogeeff): review and fix this semantic. Currently we do not fail
// in this case if flag is modified. This is misleading since the flag's
// value is not updated even though we return true.
// *err = absl::StrCat(Name(), " is already set to ",
// CurrentValue(), "\n");
// return false;
return true;
}
break;
}
case SET_FLAGS_DEFAULT: {
// modify the flag's default-value
if (!TryParse(flag, def_, value, err)) return false;
if (!modified_) {
// Need to set both default value *and* current, in this case
Copy(op_, def_, cur_);
StoreAtomic();
InvokeCallback();
}
break;
}
}
return true;
}
void FlagImpl::CheckDefaultValueParsingRoundtrip(
const CommandLineFlag& flag) const {
std::string v = DefaultValue();
absl::MutexLock lock(DataGuard());
void* dst = Clone(op_, def_);
std::string error;
if (!flags_internal::Parse(marshalling_op_, v, dst, &error)) {
ABSL_INTERNAL_LOG(
FATAL,
absl::StrCat("Flag ", flag.Name(), " (from ", flag.Filename(),
"): std::string form of default value '", v,
"' could not be parsed; error=", error));
}
// We do not compare dst to def since parsing/unparsing may make
// small changes, e.g., precision loss for floating point types.
Delete(op_, dst);
}
bool FlagImpl::ValidateInputValue(absl::string_view value) const {
absl::MutexLock l(DataGuard());
void* obj = Clone(op_, def_);
std::string ignored_error;
const bool result =
flags_internal::Parse(marshalling_op_, value, obj, &ignored_error);
Delete(op_, obj);
return result;
}
} // namespace flags_internal

View file

@ -16,12 +16,15 @@
#ifndef ABSL_FLAGS_INTERNAL_FLAG_H_
#define ABSL_FLAGS_INTERNAL_FLAG_H_
#include <atomic>
#include <cstring>
#include "absl/base/thread_annotations.h"
#include "absl/flags/internal/commandlineflag.h"
#include "absl/flags/internal/registry.h"
#include "absl/memory/memory.h"
#include "absl/strings/str_cat.h"
#include "absl/synchronization/mutex.h"
namespace absl {
namespace flags_internal {
@ -66,51 +69,32 @@ using FlagCallback = void (*)();
void InvokeCallback(absl::Mutex* primary_mu, absl::Mutex* callback_mu,
FlagCallback cb) ABSL_EXCLUSIVE_LOCKS_REQUIRED(primary_mu);
// This is "unspecified" implementation of absl::Flag<T> type.
template <typename T>
class Flag final : public flags_internal::CommandLineFlag {
// The class encapsulates the Flag's data and safe access to it.
class FlagImpl {
public:
constexpr Flag(const char* name, const flags_internal::HelpGenFunc help_gen,
const char* filename,
constexpr FlagImpl(const flags_internal::FlagOpFn op,
const flags_internal::FlagMarshallingOpFn marshalling_op,
const flags_internal::InitialValGenFunc initial_value_gen)
: flags_internal::CommandLineFlag(
name, flags_internal::HelpText::FromFunctionPointer(help_gen),
filename, &flags_internal::FlagOps<T>, marshalling_op,
initial_value_gen,
/*def=*/nullptr,
/*cur=*/nullptr),
atomic_(flags_internal::AtomicInit()),
callback_(nullptr) {}
: op_(op),
marshalling_op_(marshalling_op),
initial_value_gen_(initial_value_gen) {}
T Get() const {
// Implementation notes:
//
// We are wrapping a union around the value of `T` to serve three purposes:
//
// 1. `U.value` has correct size and alignment for a value of type `T`
// 2. The `U.value` constructor is not invoked since U's constructor does
// not
// do it explicitly.
// 3. The `U.value` destructor is invoked since U's destructor does it
// explicitly. This makes `U` a kind of RAII wrapper around non default
// constructible value of T, which is destructed when we leave the
// scope. We do need to destroy U.value, which is constructed by
// CommandLineFlag::Read even though we left it in a moved-from state
// after std::move.
//
// All of this serves to avoid requiring `T` being default constructible.
union U {
T value;
U() {}
~U() { value.~T(); }
};
U u;
Read(&u.value, &flags_internal::FlagOps<T>);
return std::move(u.value);
}
// Forces destruction of the Flag's data.
void Destroy() const;
// Constant access methods
bool IsModified() const ABSL_LOCKS_EXCLUDED(locks_->primary_mu);
bool IsSpecifiedOnCommandLine() const ABSL_LOCKS_EXCLUDED(locks_->primary_mu);
std::string DefaultValue() const ABSL_LOCKS_EXCLUDED(locks_->primary_mu);
std::string CurrentValue() const ABSL_LOCKS_EXCLUDED(locks_->primary_mu);
void Read(const CommandLineFlag& flag, void* dst,
const flags_internal::FlagOpFn dst_op) const
ABSL_LOCKS_EXCLUDED(locks_->primary_mu);
// Attempts to parse supplied `value` std::string.
bool TryParse(const CommandLineFlag& flag, void* dst, absl::string_view value,
std::string* err) const
ABSL_EXCLUSIVE_LOCKS_REQUIRED(locks_->primary_mu);
template <typename T>
bool AtomicGet(T* v) const {
const int64_t r = atomic_.load(std::memory_order_acquire);
if (r != flags_internal::AtomicInit()) {
@ -121,75 +105,174 @@ class Flag final : public flags_internal::CommandLineFlag {
return false;
}
void Set(const T& v) { Write(&v, &flags_internal::FlagOps<T>); }
// Mutating access methods
void Write(const CommandLineFlag& flag, const void* src,
const flags_internal::FlagOpFn src_op)
ABSL_LOCKS_EXCLUDED(locks_->primary_mu);
bool SetFromString(const CommandLineFlag& flag, absl::string_view value,
FlagSettingMode set_mode, ValueSource source,
std::string* err) ABSL_LOCKS_EXCLUDED(locks_->primary_mu);
// If possible, updates copy of the Flag's value that is stored in an
// atomic word.
void StoreAtomic() ABSL_EXCLUSIVE_LOCKS_REQUIRED(locks_->primary_mu);
void SetCallback(const flags_internal::FlagCallback mutation_callback) {
absl::MutexLock l(InitFlagIfNecessary());
// Interfaces to operate on callbacks.
void SetCallback(const flags_internal::FlagCallback mutation_callback)
ABSL_LOCKS_EXCLUDED(locks_->primary_mu);
void InvokeCallback() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(locks_->primary_mu);
callback_ = mutation_callback;
// Interfaces to save/restore mutable flag data
template <typename T>
std::unique_ptr<flags_internal::FlagStateInterface> SaveState(
Flag<T>* flag) const ABSL_LOCKS_EXCLUDED(locks_->primary_mu) {
T&& cur_value = flag->Get();
absl::MutexLock l(DataGuard());
InvokeCallback();
return absl::make_unique<flags_internal::FlagState<T>>(
flag, std::move(cur_value), modified_, on_command_line_, counter_);
}
bool RestoreState(const CommandLineFlag& flag, const void* value,
bool modified, bool on_command_line, int64_t counter)
ABSL_LOCKS_EXCLUDED(locks_->primary_mu);
// Value validation interfaces.
void CheckDefaultValueParsingRoundtrip(const CommandLineFlag& flag) const
ABSL_LOCKS_EXCLUDED(locks_->primary_mu);
bool ValidateInputValue(absl::string_view value) const
ABSL_LOCKS_EXCLUDED(locks_->primary_mu);
private:
friend class FlagState<T>;
// Lazy initialization of the Flag's data.
void Init();
// Ensures that the lazily initialized data is initialized,
// and returns pointer to the mutex guarding flags data.
absl::Mutex* DataGuard() const ABSL_LOCK_RETURNED(locks_->primary_mu);
void Destroy() const override {
// Values are heap allocated for Abseil Flags.
if (cur_) Delete(op_, cur_);
if (def_) Delete(op_, def_);
// Immutable Flag's data.
const FlagOpFn op_; // Type-specific handler
const FlagMarshallingOpFn marshalling_op_; // Marshalling ops handler
const InitialValGenFunc initial_value_gen_; // Makes flag's initial value
delete locks_;
// Mutable Flag's data. (guarded by locks_->primary_mu).
// Indicates that locks_, cur_ and def_ fields have been lazily initialized.
std::atomic<bool> inited_{false};
// Has flag value been modified?
bool modified_ ABSL_GUARDED_BY(locks_->primary_mu) = false;
// Specified on command line.
bool on_command_line_ ABSL_GUARDED_BY(locks_->primary_mu) = false;
// Lazily initialized pointer to default value
void* def_ ABSL_GUARDED_BY(locks_->primary_mu) = nullptr;
// Lazily initialized pointer to current value
void* cur_ ABSL_GUARDED_BY(locks_->primary_mu) = nullptr;
// Mutation counter
int64_t counter_ ABSL_GUARDED_BY(locks_->primary_mu) = 0;
// For some types, a copy of the current value is kept in an atomically
// accessible field.
std::atomic<int64_t> atomic_{flags_internal::AtomicInit()};
// Mutation callback
FlagCallback callback_ = nullptr;
// Lazily initialized mutexes for this flag value. We cannot inline a
// SpinLock or Mutex here because those have non-constexpr constructors and
// so would prevent constant initialization of this type.
// TODO(rogeeff): fix it once Mutex has constexpr constructor
// The following struct contains the locks in a CommandLineFlag struct.
// They are in a separate struct that is lazily allocated to avoid problems
// with static initialization and to avoid multiple allocations.
struct CommandLineFlagLocks {
absl::Mutex primary_mu; // protects several fields in CommandLineFlag
absl::Mutex callback_mu; // used to serialize callbacks
};
CommandLineFlagLocks* locks_ = nullptr; // locks, laziliy allocated.
};
// This is "unspecified" implementation of absl::Flag<T> type.
template <typename T>
class Flag final : public flags_internal::CommandLineFlag {
public:
constexpr Flag(const char* name, const flags_internal::HelpGenFunc help_gen,
const char* filename,
const flags_internal::FlagMarshallingOpFn marshalling_op,
const flags_internal::InitialValGenFunc initial_value_gen)
: flags_internal::CommandLineFlag(
name, flags_internal::HelpText::FromFunctionPointer(help_gen),
filename),
impl_(&flags_internal::FlagOps<T>, marshalling_op, initial_value_gen) {}
T Get() const {
// See implementation notes in CommandLineFlag::Get().
union U {
T value;
U() {}
~U() { value.~T(); }
};
U u;
impl_.Read(*this, &u.value, &flags_internal::FlagOps<T>);
return std::move(u.value);
}
void StoreAtomic() override {
if (sizeof(T) <= sizeof(int64_t)) {
int64_t t = 0;
std::memcpy(&t, cur_, (std::min)(sizeof(T), sizeof(int64_t)));
atomic_.store(t, std::memory_order_release);
bool AtomicGet(T* v) const { return impl_.AtomicGet(v); }
void Set(const T& v) { impl_.Write(*this, &v, &flags_internal::FlagOps<T>); }
void SetCallback(const flags_internal::FlagCallback mutation_callback) {
impl_.SetCallback(mutation_callback);
}
// CommandLineFlag interface
bool IsModified() const override { return impl_.IsModified(); }
bool IsSpecifiedOnCommandLine() const override {
return impl_.IsSpecifiedOnCommandLine();
}
std::string DefaultValue() const override { return impl_.DefaultValue(); }
std::string CurrentValue() const override { return impl_.CurrentValue(); }
bool ValidateInputValue(absl::string_view value) const override {
return impl_.ValidateInputValue(value);
}
// Interfaces to save and restore flags to/from persistent state.
// Returns current flag state or nullptr if flag does not support
// saving and restoring a state.
std::unique_ptr<flags_internal::FlagStateInterface> SaveState() override {
T curr_value = Get();
absl::MutexLock l(InitFlagIfNecessary());
return absl::make_unique<flags_internal::FlagState<T>>(
this, std::move(curr_value), modified_, on_command_line_, counter_);
return impl_.SaveState(this);
}
// Restores the flag state to the supplied state object. If there is
// nothing to restore returns false. Otherwise returns true.
bool RestoreState(const flags_internal::FlagState<T>& flag_state) {
if (MutationCounter() == flag_state.counter_) return false;
Set(flag_state.cur_value_);
// Race condition here? This should disappear once we move the rest of the
// flag's data into Flag's internals.
absl::MutexLock l(InitFlagIfNecessary());
modified_ = flag_state.modified_;
on_command_line_ = flag_state.on_command_line_;
return true;
return impl_.RestoreState(*this, &flag_state.cur_value_,
flag_state.modified_, flag_state.on_command_line_,
flag_state.counter_);
}
// Interfaces to overate on callbacks.
void InvokeCallback() override
ABSL_EXCLUSIVE_LOCKS_REQUIRED(locks_->primary_mu) {
flags_internal::InvokeCallback(&locks_->primary_mu, &locks_->callback_mu,
callback_);
bool SetFromString(absl::string_view value,
flags_internal::FlagSettingMode set_mode,
flags_internal::ValueSource source,
std::string* error) override {
return impl_.SetFromString(*this, value, set_mode, source, error);
}
void CheckDefaultValueParsingRoundtrip() const override {
impl_.CheckDefaultValueParsingRoundtrip(*this);
}
private:
friend class FlagState<T>;
void Destroy() const override { impl_.Destroy(); }
void Read(void* dst) const override {
impl_.Read(*this, dst, &flags_internal::FlagOps<T>);
}
flags_internal::FlagOpFn TypeId() const override {
return &flags_internal::FlagOps<T>;
}
// Flag's data
// For some types, a copy of the current value is kept in an atomically
// accessible field.
std::atomic<int64_t> atomic_;
FlagCallback callback_; // Mutation callback
FlagImpl impl_;
};
template <typename T>

View file

@ -118,7 +118,7 @@ void FlagRegistry::RegisterFlag(CommandLineFlag* flag) {
(flag->IsRetired() ? old_flag->Filename() : flag->Filename()),
"'."),
true);
} else if (flag->op_ != old_flag->op_) {
} else if (flag->TypeId() != old_flag->TypeId()) {
flags_internal::ReportUsageError(
absl::StrCat("Flag '", flag->Name(),
"' was defined more than once but with "
@ -275,38 +275,49 @@ namespace {
class RetiredFlagObj final : public flags_internal::CommandLineFlag {
public:
constexpr RetiredFlagObj(const char* name, FlagOpFn ops,
FlagMarshallingOpFn marshalling_ops)
constexpr RetiredFlagObj(const char* name, FlagOpFn ops)
: flags_internal::CommandLineFlag(
name, flags_internal::HelpText::FromStaticCString(nullptr),
/*filename=*/"RETIRED", ops, marshalling_ops,
/*initial_value_gen=*/nullptr,
/*def=*/nullptr,
/*cur=*/nullptr) {}
/*filename=*/"RETIRED"),
op_(ops) {}
private:
bool IsRetired() const override { return true; }
void Destroy() const override {
// Values are heap allocated for Retired Flags.
if (cur_) Delete(op_, cur_);
if (def_) Delete(op_, def_);
if (locks_) delete locks_;
delete this;
}
flags_internal::FlagOpFn TypeId() const override { return op_; }
bool IsRetired() const override { return true; }
bool IsModified() const override { return false; }
bool IsSpecifiedOnCommandLine() const override { return false; }
std::string DefaultValue() const override { return ""; }
std::string CurrentValue() const override { return ""; }
// Any input is valid
bool ValidateInputValue(absl::string_view) const override { return true; }
std::unique_ptr<flags_internal::FlagStateInterface> SaveState() override {
return nullptr;
}
bool SetFromString(absl::string_view, flags_internal::FlagSettingMode,
flags_internal::ValueSource, std::string*) override {
return false;
}
void CheckDefaultValueParsingRoundtrip() const override {}
void Read(void*) const override {}
// Data members
const FlagOpFn op_;
};
} // namespace
bool Retire(const char* name, FlagOpFn ops,
FlagMarshallingOpFn marshalling_ops) {
auto* flag = new flags_internal::RetiredFlagObj(name, ops, marshalling_ops);
bool Retire(const char* name, FlagOpFn ops) {
auto* flag = new flags_internal::RetiredFlagObj(name, ops);
FlagRegistry::GlobalRegistry()->RegisterFlag(flag);
return true;
}

View file

@ -76,14 +76,12 @@ bool RegisterCommandLineFlag(CommandLineFlag*);
//
// Retire flag with name "name" and type indicated by ops.
bool Retire(const char* name, FlagOpFn ops,
FlagMarshallingOpFn marshalling_ops);
bool Retire(const char* name, FlagOpFn ops);
// Registered a retired flag with name 'flag_name' and type 'T'.
template <typename T>
inline bool RetiredFlag(const char* flag_name) {
return flags_internal::Retire(flag_name, flags_internal::FlagOps<T>,
flags_internal::FlagMarshallingOps<T>);
return flags_internal::Retire(flag_name, flags_internal::FlagOps<T>);
}
// If the flag is retired, returns true and indicates in |*type_is_bool|

View file

@ -196,8 +196,8 @@ TEST(IOStreamStateSaver, RoundTripFloats) {
EXPECT_EQ(-d, StreamRoundTrip<double>(-d));
// Avoid undefined behavior (overflow/underflow).
if (d <= std::numeric_limits<int64_t>::max() &&
d >= std::numeric_limits<int64_t>::lowest()) {
if (f <= static_cast<float>(std::numeric_limits<int64_t>::max()) &&
f >= static_cast<float>(std::numeric_limits<int64_t>::lowest())) {
int64_t x = static_cast<int64_t>(f);
EXPECT_EQ(x, StreamRoundTrip<int64_t>(x));
}
@ -264,8 +264,8 @@ TEST(IOStreamStateSaver, RoundTripDoubles) {
}
// Avoid undefined behavior (overflow/underflow).
if (d <= std::numeric_limits<int64_t>::max() &&
d >= std::numeric_limits<int64_t>::lowest()) {
if (d <= static_cast<double>(std::numeric_limits<int64_t>::max()) &&
d >= static_cast<double>(std::numeric_limits<int64_t>::lowest())) {
int64_t x = static_cast<int64_t>(d);
EXPECT_EQ(x, StreamRoundTrip<int64_t>(x));
}

View file

@ -35,8 +35,7 @@ struct ResizeUninitializedTraits {
static void Resize(string_type* s, size_t new_size) { s->resize(new_size); }
};
// __resize_default_init is provided by libc++ >= 8.0 and by Google's internal
// ::string implementation.
// __resize_default_init is provided by libc++ >= 8.0
template <typename string_type>
struct ResizeUninitializedTraits<
string_type, absl::void_t<decltype(std::declval<string_type&>()

View file

@ -28,6 +28,5 @@ time docker run \
--rm \
-e CFLAGS="-Werror" \
-e CXXFLAGS="-Werror" \
gcr.io/google.com/absl-177019/linux_gcc-latest:20190703 \
gcr.io/google.com/absl-177019/linux_gcc-latest:20191018 \
/bin/bash CMake/install_test_project/test.sh $@

View file

@ -36,7 +36,7 @@ if [ -z ${EXCEPTIONS_MODE:-} ]; then
EXCEPTIONS_MODE="-fno-exceptions -fexceptions"
fi
readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/linux_clang-latest:20190813"
readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/linux_clang-latest:20191018"
# USE_BAZEL_CACHE=1 only works on Kokoro.
# Without access to the credentials this won't work.

View file

@ -36,7 +36,7 @@ if [ -z ${EXCEPTIONS_MODE:-} ]; then
EXCEPTIONS_MODE="-fno-exceptions -fexceptions"
fi
readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/linux_clang-latest:20190813"
readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/linux_clang-latest:20191018"
# USE_BAZEL_CACHE=1 only works on Kokoro.
# Without access to the credentials this won't work.

View file

@ -36,7 +36,7 @@ if [ -z ${EXCEPTIONS_MODE:-} ]; then
EXCEPTIONS_MODE="-fno-exceptions -fexceptions"
fi
readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/linux_clang-latest:20190813"
readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/linux_clang-latest:20191018"
# USE_BAZEL_CACHE=1 only works on Kokoro.
# Without access to the credentials this won't work.

View file

@ -36,7 +36,7 @@ if [ -z ${EXCEPTIONS_MODE:-} ]; then
EXCEPTIONS_MODE="-fno-exceptions -fexceptions"
fi
readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/linux_clang-latest:20190813"
readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/linux_clang-latest:20191018"
# USE_BAZEL_CACHE=1 only works on Kokoro.
# Without access to the credentials this won't work.

View file

@ -36,7 +36,7 @@ if [ -z ${EXCEPTIONS_MODE:-} ]; then
EXCEPTIONS_MODE="-fno-exceptions -fexceptions"
fi
readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/linux_gcc-4.9:20190702"
readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/linux_gcc-4.9:20191018"
# USE_BAZEL_CACHE=1 only works on Kokoro.
# Without access to the credentials this won't work.

View file

@ -36,7 +36,7 @@ if [ -z ${EXCEPTIONS_MODE:-} ]; then
EXCEPTIONS_MODE="-fno-exceptions -fexceptions"
fi
readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/linux_gcc-latest:20190703"
readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/linux_gcc-latest:20191018"
# USE_BAZEL_CACHE=1 only works on Kokoro.
# Without access to the credentials this won't work.

View file

@ -47,7 +47,7 @@ for std in ${ABSL_CMAKE_CXX_STANDARDS}; do
--rm \
-e CFLAGS="-Werror" \
-e CXXFLAGS="-Werror" \
gcr.io/google.com/absl-177019/linux_gcc-latest:20190703 \
gcr.io/google.com/absl-177019/linux_gcc-latest:20191018 \
/bin/bash -c "
cd /buildfs && \
cmake /abseil-cpp \