#include "absl/flags/internal/flag.h"
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <array>
#include <atomic>
#include <cstring>
#include <memory>
#include <string>
#include <typeinfo>
#include <vector>
#include "absl/base/attributes.h"
#include "absl/base/call_once.h"
#include "absl/base/casts.h"
#include "absl/base/config.h"
#include "absl/base/const_init.h"
#include "absl/base/dynamic_annotations.h"
#include "absl/base/optimization.h"
#include "absl/flags/config.h"
#include "absl/flags/internal/commandlineflag.h"
#include "absl/flags/usage_config.h"
#include "absl/memory/memory.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "absl/synchronization/mutex.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace flags_internal {
const char kStrippedFlagHelp[] = "\001\002\003\004 (unknown) \004\003\002\001";
namespace {
bool ShouldValidateFlagValue(FlagFastTypeId flag_type_id) {
#define DONT_VALIDATE(T, _) \
if (flag_type_id == base_internal::FastTypeId<T>()) return false;
ABSL_FLAGS_INTERNAL_SUPPORTED_TYPES(DONT_VALIDATE)
#undef DONT_VALIDATE
return true;
}
class MutexRelock {
public:
explicit MutexRelock(absl::Mutex& mu) : mu_(mu) { mu_.Unlock(); }
~MutexRelock() { mu_.Lock(); }
MutexRelock(const MutexRelock&) = delete;
MutexRelock& operator=(const MutexRelock&) = delete;
private:
absl::Mutex& mu_;
};
ABSL_CONST_INIT absl::Mutex s_freelist_guard(absl::kConstInit);
ABSL_CONST_INIT std::vector<void*>* s_freelist = nullptr;
void AddToFreelist(void* p) {
absl::MutexLock l(&s_freelist_guard);
if (!s_freelist) {
s_freelist = new std::vector<void*>;
}
s_freelist->push_back(p);
}
}
uint64_t NumLeakedFlagValues() {
absl::MutexLock l(&s_freelist_guard);
return s_freelist == nullptr ? 0u : s_freelist->size();
}
class FlagImpl;
class FlagState : public flags_internal::FlagStateInterface {
public:
template <typename V>
FlagState(FlagImpl& flag_impl, const V& v, bool modified,
bool on_command_line, int64_t counter)
: flag_impl_(flag_impl),
value_(v),
modified_(modified),
on_command_line_(on_command_line),
counter_(counter) {}
~FlagState() override {
if (flag_impl_.ValueStorageKind() != FlagValueStorageKind::kHeapAllocated &&
flag_impl_.ValueStorageKind() != FlagValueStorageKind::kSequenceLocked)
return;
flags_internal::Delete(flag_impl_.op_, value_.heap_allocated);
}
private:
friend class FlagImpl;
void Restore() const override {
if (!flag_impl_.RestoreState(*this)) return;
ABSL_INTERNAL_LOG(INFO,
absl::StrCat("Restore saved value of ", flag_impl_.Name(),
" to: ", flag_impl_.CurrentValue()));
}
FlagImpl& flag_impl_;
union SavedValue {
explicit SavedValue(void* v) : heap_allocated(v) {}
explicit SavedValue(int64_t v) : one_word(v) {}
void* heap_allocated;
int64_t one_word;
} value_;
bool modified_;
bool on_command_line_;
int64_t counter_;
};
DynValueDeleter::DynValueDeleter(FlagOpFn op_arg) : op(op_arg) {}
void DynValueDeleter::operator()(void* ptr) const {
if (op == nullptr) return;
Delete(op, ptr);
}
MaskedPointer::MaskedPointer(ptr_t rhs, bool is_candidate) : ptr_(rhs) {
if (is_candidate) {
ApplyMask(kUnprotectedReadCandidate);
}
}
bool MaskedPointer::IsUnprotectedReadCandidate() const {
return CheckMask(kUnprotectedReadCandidate);
}
bool MaskedPointer::HasBeenRead() const { return CheckMask(kHasBeenRead); }
void MaskedPointer::Set(FlagOpFn op, const void* src, bool is_candidate) {
flags_internal::Copy(op, src, Ptr());
if (is_candidate) {
ApplyMask(kUnprotectedReadCandidate);
}
}
void MaskedPointer::MarkAsRead() { ApplyMask(kHasBeenRead); }
void MaskedPointer::ApplyMask(mask_t mask) {
ptr_ = reinterpret_cast<ptr_t>(reinterpret_cast<mask_t>(ptr_) | mask);
}
bool MaskedPointer::CheckMask(mask_t mask) const {
return (reinterpret_cast<mask_t>(ptr_) & mask) != 0;
}
void FlagImpl::Init() {
new (&data_guard_) absl::Mutex;
auto def_kind = static_cast<FlagDefaultKind>(def_kind_);
switch (ValueStorageKind()) {
case FlagValueStorageKind::kValueAndInitBit:
case FlagValueStorageKind::kOneWordAtomic: {
alignas(int64_t) std::array<char, sizeof(int64_t)> buf{};
if (def_kind == FlagDefaultKind::kGenFunc) {
(*default_value_.gen_func)(buf.data());
} else {
assert(def_kind != FlagDefaultKind::kDynamicValue);
std::memcpy(buf.data(), &default_value_, Sizeof(op_));
}
if (ValueStorageKind() == FlagValueStorageKind::kValueAndInitBit) {
uint8_t initialized = 1;
std::memcpy(buf.data() + Sizeof(op_), &initialized,
sizeof(initialized));
}
ABSL_ANNOTATE_MEMORY_IS_INITIALIZED(buf.data(), buf.size());
OneWordValue().store(absl::bit_cast<int64_t>(buf),
std::memory_order_release);
break;
}
case FlagValueStorageKind::kSequenceLocked: {
assert(def_kind == FlagDefaultKind::kGenFunc);
(*default_value_.gen_func)(AtomicBufferValue());
break;
}
case FlagValueStorageKind::kHeapAllocated:
assert(def_kind == FlagDefaultKind::kGenFunc);
MaskedPointer ptr_value = PtrStorage().load(std::memory_order_acquire);
(*default_value_.gen_func)(ptr_value.Ptr());
PtrStorage().store(MaskedPointer(ptr_value.Ptr(), true),
std::memory_order_release);
break;
}
seq_lock_.MarkInitialized();
}
absl::Mutex* FlagImpl::DataGuard() const {
absl::call_once(const_cast<FlagImpl*>(this)->init_control_, &FlagImpl::Init,
const_cast<FlagImpl*>(this));
return reinterpret_cast<absl::Mutex*>(&data_guard_);
}
void FlagImpl::AssertValidType(FlagFastTypeId rhs_type_id,
const std::type_info* (*gen_rtti)()) const {
FlagFastTypeId lhs_type_id = flags_internal::FastTypeId(op_);
if (ABSL_PREDICT_TRUE(lhs_type_id == rhs_type_id)) return;
const std::type_info* lhs_runtime_type_id =
flags_internal::RuntimeTypeId(op_);
const std::type_info* rhs_runtime_type_id = (*gen_rtti)();
if (lhs_runtime_type_id == rhs_runtime_type_id) return;
#ifdef ABSL_INTERNAL_HAS_RTTI
if (*lhs_runtime_type_id == *rhs_runtime_type_id) return;
#endif
ABSL_INTERNAL_LOG(
FATAL, absl::StrCat("Flag '", Name(),
"' is defined as one type and declared as another"));
}
std::unique_ptr<void, DynValueDeleter> FlagImpl::MakeInitValue() const {
void* res = nullptr;
switch (DefaultKind()) {
case FlagDefaultKind::kDynamicValue:
res = flags_internal::Clone(op_, default_value_.dynamic_value);
break;
case FlagDefaultKind::kGenFunc:
res = flags_internal::Alloc(op_);
(*default_value_.gen_func)(res);
break;
default:
res = flags_internal::Clone(op_, &default_value_);
break;
}
return {res, DynValueDeleter{op_}};
}
void FlagImpl::StoreValue(const void* src, ValueSource source) {
switch (ValueStorageKind()) {
case FlagValueStorageKind::kValueAndInitBit:
case FlagValueStorageKind::kOneWordAtomic: {
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::kSequenceLocked: {
seq_lock_.Write(AtomicBufferValue(), src, Sizeof(op_));
break;
}
case FlagValueStorageKind::kHeapAllocated:
MaskedPointer ptr_value = PtrStorage().load(std::memory_order_acquire);
if (ptr_value.IsUnprotectedReadCandidate() && ptr_value.HasBeenRead()) {
AddToFreelist(ptr_value.Ptr());
ptr_value = MaskedPointer(Clone(op_, src), source == kCommandLine);
} else {
ptr_value.Set(op_, src, source == kCommandLine);
}
PtrStorage().store(ptr_value, std::memory_order_release);
seq_lock_.IncrementModificationCount();
break;
}
modified_ = true;
InvokeCallback();
}
absl::string_view FlagImpl::Name() const { return name_; }
std::string FlagImpl::Filename() const {
return flags_internal::GetUsageConfig().normalize_filename(filename_);
}
std::string FlagImpl::Help() const {
return HelpSourceKind() == FlagHelpKind::kLiteral ? help_.literal
: help_.gen_func();
}
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_;
}
std::string FlagImpl::DefaultValue() const {
absl::MutexLock l(DataGuard());
auto obj = MakeInitValue();
return flags_internal::Unparse(op_, obj.get());
}
std::string FlagImpl::CurrentValue() const {
auto* guard = DataGuard(); switch (ValueStorageKind()) {
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::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::kHeapAllocated: {
absl::MutexLock l(guard);
return flags_internal::Unparse(
op_, PtrStorage().load(std::memory_order_acquire).Ptr());
}
}
return "";
}
void FlagImpl::SetCallback(const FlagCallbackFunc mutation_callback) {
absl::MutexLock l(DataGuard());
if (callback_ == nullptr) {
callback_ = new FlagCallback;
}
callback_->func = mutation_callback;
InvokeCallback();
}
void FlagImpl::InvokeCallback() const {
if (!callback_) return;
FlagCallbackFunc cb = callback_->func;
MutexRelock relock(*DataGuard());
absl::MutexLock lock(&callback_->guard);
cb();
}
std::unique_ptr<FlagStateInterface> FlagImpl::SaveState() {
absl::MutexLock l(DataGuard());
bool modified = modified_;
bool on_command_line = on_command_line_;
switch (ValueStorageKind()) {
case FlagValueStorageKind::kValueAndInitBit:
case FlagValueStorageKind::kOneWordAtomic: {
return absl::make_unique<FlagState>(
*this, OneWordValue().load(std::memory_order_acquire), modified,
on_command_line, ModificationCount());
}
case FlagValueStorageKind::kSequenceLocked: {
void* cloned = flags_internal::Alloc(op_);
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::kHeapAllocated: {
return absl::make_unique<FlagState>(
*this,
flags_internal::Clone(
op_, PtrStorage().load(std::memory_order_acquire).Ptr()),
modified, on_command_line, ModificationCount());
}
}
return nullptr;
}
bool FlagImpl::RestoreState(const FlagState& flag_state) {
absl::MutexLock l(DataGuard());
if (flag_state.counter_ == ModificationCount()) {
return false;
}
switch (ValueStorageKind()) {
case FlagValueStorageKind::kValueAndInitBit:
case FlagValueStorageKind::kOneWordAtomic:
StoreValue(&flag_state.value_.one_word, kProgrammaticChange);
break;
case FlagValueStorageKind::kSequenceLocked:
case FlagValueStorageKind::kHeapAllocated:
StoreValue(flag_state.value_.heap_allocated, kProgrammaticChange);
break;
}
modified_ = flag_state.modified_;
on_command_line_ = flag_state.on_command_line_;
return true;
}
template <typename StorageT>
StorageT* FlagImpl::OffsetValue() const {
char* p = reinterpret_cast<char*>(const_cast<FlagImpl*>(this));
ptrdiff_t offset = flags_internal::ValueOffset(op_);
return reinterpret_cast<StorageT*>(p + offset);
}
std::atomic<uint64_t>* FlagImpl::AtomicBufferValue() const {
assert(ValueStorageKind() == FlagValueStorageKind::kSequenceLocked);
return OffsetValue<std::atomic<uint64_t>>();
}
std::atomic<int64_t>& FlagImpl::OneWordValue() const {
assert(ValueStorageKind() == FlagValueStorageKind::kOneWordAtomic ||
ValueStorageKind() == FlagValueStorageKind::kValueAndInitBit);
return OffsetValue<FlagOneWordValue>()->value;
}
std::atomic<MaskedPointer>& FlagImpl::PtrStorage() const {
assert(ValueStorageKind() == FlagValueStorageKind::kHeapAllocated);
return OffsetValue<FlagMaskedPointerValue>()->value;
}
std::unique_ptr<void, DynValueDeleter> FlagImpl::TryParse(
absl::string_view value, std::string& err) const {
std::unique_ptr<void, DynValueDeleter> tentative_value = MakeInitValue();
std::string parse_err;
if (!flags_internal::Parse(op_, value, tentative_value.get(), &parse_err)) {
absl::string_view err_sep = parse_err.empty() ? "" : "; ";
err = absl::StrCat("Illegal value '", value, "' specified for flag '",
Name(), "'", err_sep, parse_err);
return nullptr;
}
return tentative_value;
}
void FlagImpl::Read(void* dst) const {
auto* guard = DataGuard(); switch (ValueStorageKind()) {
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::kSequenceLocked: {
ReadSequenceLockedData(dst);
break;
}
case FlagValueStorageKind::kHeapAllocated: {
absl::MutexLock l(guard);
MaskedPointer ptr_value = PtrStorage().load(std::memory_order_acquire);
flags_internal::CopyConstruct(op_, ptr_value.Ptr(), dst);
if (ptr_value.IsUnprotectedReadCandidate() && !ptr_value.HasBeenRead()) {
ptr_value.MarkAsRead();
PtrStorage().store(ptr_value, std::memory_order_release);
}
break;
}
}
}
int64_t FlagImpl::ReadOneWord() const {
assert(ValueStorageKind() == FlagValueStorageKind::kOneWordAtomic ||
ValueStorageKind() == FlagValueStorageKind::kValueAndInitBit);
auto* guard = DataGuard(); (void)guard;
return OneWordValue().load(std::memory_order_acquire);
}
bool FlagImpl::ReadOneBool() const {
assert(ValueStorageKind() == FlagValueStorageKind::kValueAndInitBit);
auto* guard = DataGuard(); (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_);
if (ABSL_PREDICT_TRUE(seq_lock_.TryRead(dst, AtomicBufferValue(), size))) {
return;
}
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());
if (ShouldValidateFlagValue(flags_internal::FastTypeId(op_))) {
std::unique_ptr<void, DynValueDeleter> obj{flags_internal::Clone(op_, src),
DynValueDeleter{op_}};
std::string ignored_error;
std::string src_as_str = flags_internal::Unparse(op_, src);
if (!flags_internal::Parse(op_, src_as_str, obj.get(), &ignored_error)) {
ABSL_INTERNAL_LOG(ERROR, absl::StrCat("Attempt to set flag '", Name(),
"' to invalid value ", src_as_str));
}
}
StoreValue(src, kProgrammaticChange);
}
bool FlagImpl::ParseFrom(absl::string_view value, FlagSettingMode set_mode,
ValueSource source, std::string& err) {
absl::MutexLock l(DataGuard());
switch (set_mode) {
case SET_FLAGS_VALUE: {
auto tentative_value = TryParse(value, err);
if (!tentative_value) return false;
StoreValue(tentative_value.get(), source);
if (source == kCommandLine) {
on_command_line_ = true;
}
break;
}
case SET_FLAG_IF_DEFAULT: {
if (modified_) {
return true;
}
auto tentative_value = TryParse(value, err);
if (!tentative_value) return false;
StoreValue(tentative_value.get(), source);
break;
}
case SET_FLAGS_DEFAULT: {
auto tentative_value = TryParse(value, err);
if (!tentative_value) return false;
if (DefaultKind() == FlagDefaultKind::kDynamicValue) {
void* old_value = default_value_.dynamic_value;
default_value_.dynamic_value = tentative_value.release();
tentative_value.reset(old_value);
} else {
default_value_.dynamic_value = tentative_value.release();
def_kind_ = static_cast<uint8_t>(FlagDefaultKind::kDynamicValue);
}
if (!modified_) {
StoreValue(default_value_.dynamic_value, source);
modified_ = false;
}
break;
}
}
return true;
}
void FlagImpl::CheckDefaultValueParsingRoundtrip() const {
std::string v = DefaultValue();
absl::MutexLock lock(DataGuard());
auto dst = MakeInitValue();
std::string error;
if (!flags_internal::Parse(op_, v, dst.get(), &error)) {
ABSL_INTERNAL_LOG(
FATAL,
absl::StrCat("Flag ", Name(), " (from ", Filename(),
"): string form of default value '", v,
"' could not be parsed; error=", error));
}
}
bool FlagImpl::ValidateInputValue(absl::string_view value) const {
absl::MutexLock l(DataGuard());
auto obj = MakeInitValue();
std::string ignored_error;
return flags_internal::Parse(op_, value, obj.get(), &ignored_error);
}
} ABSL_NAMESPACE_END
}