diff options
Diffstat (limited to 'absl/flags/internal/commandlineflag.cc')
-rw-r--r-- | absl/flags/internal/commandlineflag.cc | 439 |
1 files changed, 275 insertions, 164 deletions
diff --git a/absl/flags/internal/commandlineflag.cc b/absl/flags/internal/commandlineflag.cc index 744447219c71..98a46695304b 100644 --- a/absl/flags/internal/commandlineflag.cc +++ b/absl/flags/internal/commandlineflag.cc @@ -37,44 +37,6 @@ const char kStrippedFlagHelp[] = "\001\002\003\004 (unknown) \004\003\002\001"; namespace { -void StoreAtomic(CommandLineFlag* flag, const void* data, size_t size) { - int64_t t = 0; - assert(size <= sizeof(int64_t)); - memcpy(&t, data, size); - flag->atomic.store(t, std::memory_order_release); -} - -// 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(CommandLineFlag* flag, absl::Mutex* primary_lock) - EXCLUSIVE_LOCKS_REQUIRED(primary_lock) { - if (!flag->callback) return; - - // The callback lock is guaranteed initialized, because *primary_lock exists. - absl::Mutex* callback_mu = &flag->locks->callback_mu; - - // When executing the callback we need the primary flag's mutex to be unlocked - // so that callback can retrieve the flag's value. - primary_lock->Unlock(); - - { - absl::MutexLock lock(callback_mu); - - flag->callback(); - } - - primary_lock->Lock(); -} - // Currently we only validate flag values for user-defined flag types. bool ShouldValidateFlagValue(const CommandLineFlag& flag) { #define DONT_VALIDATE(T) \ @@ -89,145 +51,72 @@ bool ShouldValidateFlagValue(const CommandLineFlag& flag) { } // namespace -// 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, absl::Mutex* primary_lock) - EXCLUSIVE_LOCKS_REQUIRED(primary_lock) { -#define STORE_ATOMIC(T) \ - else if (flag->IsOfType<T>()) { \ - StoreAtomic(flag, flag->cur, sizeof(T)); \ - } - - if (false) { - } - ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(STORE_ATOMIC) -#undef STORE_ATOMIC +absl::Mutex* InitFlag(CommandLineFlag* flag) { + ABSL_CONST_INIT static absl::Mutex init_lock(absl::kConstInit); + absl::Mutex* mu; - InvokeCallback(flag, primary_lock); -} + { + absl::MutexLock lock(&init_lock); -// Ensure that the lazily initialized fields of *flag have been initialized, -// and return &flag->locks->primary_mu. -absl::Mutex* InitFlagIfNecessary(CommandLineFlag* flag) - LOCK_RETURNED(flag->locks->primary_mu) { - absl::Mutex* mu; - if (!flag->inited.load(std::memory_order_acquire)) { - // Need to initialize lazily initialized fields. - ABSL_CONST_INIT static absl::Mutex init_lock(absl::kConstInit); - init_lock.Lock(); if (flag->locks == nullptr) { // Must initialize Mutexes for this flag. flag->locks = new flags_internal::CommandLineFlagLocks; } + mu = &flag->locks->primary_mu; - init_lock.Unlock(); - mu->Lock(); - if (!flag->retired && - flag->def == nullptr) { // Need to initialize def and cur fields. + } + + { + absl::MutexLock lock(mu); + + if (!flag->retired && 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, mu); + UpdateCopy(flag); + flag->inited.store(true, std::memory_order_release); + flag->InvokeCallback(); } - mu->Unlock(); - flag->inited.store(true, std::memory_order_release); - } else { // All fields initialized; flag->locks is therefore safe to read. - mu = &flag->locks->primary_mu; } + + flag->inited.store(true, std::memory_order_release); return mu; } -// 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); \ +// Ensure that the lazily initialized fields of *flag have been initialized, +// and return &flag->locks->primary_mu. +absl::Mutex* CommandLineFlag::InitFlagIfNecessary() const + LOCK_RETURNED(locks->primary_mu) { + if (!this->inited.load(std::memory_order_acquire)) { + return InitFlag(const_cast<CommandLineFlag*>(this)); } - 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; + // All fields initialized; this->locks is therefore safe to read. + return &this->locks->primary_mu; } -// Direct-access flags can be modified without going through the -// flag API. Detect such changes and updated the modified bit. -void UpdateModifiedBit(CommandLineFlag* flag) { - if (!flag->IsAbseilFlag()) { - absl::MutexLock l(InitFlagIfNecessary(flag)); - if (!flag->modified && ChangedDirectly(flag, flag->cur, flag->def)) { - flag->modified = true; - } +void CommandLineFlag::Destroy() const { + // Values are heap allocated for retired and Abseil Flags. + if (IsRetired() || IsAbseilFlag()) { + if (this->cur) Delete(this->op, this->cur); + if (this->def) Delete(this->op, this->def); } -} -bool Validate(CommandLineFlag*, const void*) { - return true; + delete this->locks; } -std::string HelpText::GetHelpText() const { - if (help_function_) return help_function_(); - if (help_message_) return help_message_; - - return {}; +bool CommandLineFlag::IsModified() const { + absl::MutexLock l(InitFlagIfNecessary()); + return modified; } -const int64_t CommandLineFlag::kAtomicInit; - -void CommandLineFlag::Read(void* dst, - const flags_internal::FlagOpFn dst_op) const { - absl::ReaderMutexLock l( - InitFlagIfNecessary(const_cast<CommandLineFlag*>(this))); - - // `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::SetModified(bool is_modified) { + absl::MutexLock l(InitFlagIfNecessary()); + modified = is_modified; } -void CommandLineFlag::Write(const void* src, - const flags_internal::FlagOpFn src_op) { - absl::Mutex* mu = InitFlagIfNecessary(this); - absl::MutexLock l(mu); - - // `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) || - !Validate(this, 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, mu); +bool CommandLineFlag::IsSpecifiedOnCommandLine() const { + absl::MutexLock l(InitFlagIfNecessary()); + return on_command_line; } absl::string_view CommandLineFlag::Typename() const { @@ -259,21 +148,96 @@ std::string CommandLineFlag::Filename() const { } std::string CommandLineFlag::DefaultValue() const { + absl::MutexLock l(InitFlagIfNecessary()); + return Unparse(this->marshalling_op, this->def); } std::string CommandLineFlag::CurrentValue() const { + absl::MutexLock l(InitFlagIfNecessary()); + return Unparse(this->marshalling_op, this->cur); } +bool CommandLineFlag::HasValidatorFn() const { + absl::MutexLock l(InitFlagIfNecessary()); + + return this->validator != nullptr; +} + +bool CommandLineFlag::SetValidatorFn(FlagValidator fn) { + absl::MutexLock l(InitFlagIfNecessary()); + + // ok to register the same function over and over again + if (fn == this->validator) return true; + + // Can't set validator to a different function, unless reset first. + if (fn != nullptr && this->validator != nullptr) { + ABSL_INTERNAL_LOG( + WARNING, absl::StrCat("Ignoring SetValidatorFn() for flag '", Name(), + "': validate-fn already registered")); + + return false; + } + + this->validator = fn; + return true; +} + +bool CommandLineFlag::InvokeValidator(const void* value) const + EXCLUSIVE_LOCKS_REQUIRED(this->locks->primary_mu) { + if (!this->validator) { + return true; + } + + (void)value; + + ABSL_INTERNAL_LOG( + FATAL, + absl::StrCat("Flag '", Name(), + "' of encapsulated type should not have a validator")); + + return false; +} + void CommandLineFlag::SetCallback( const flags_internal::FlagCallback mutation_callback) { - absl::Mutex* mu = InitFlagIfNecessary(this); - absl::MutexLock l(mu); + absl::MutexLock l(InitFlagIfNecessary()); callback = mutation_callback; - InvokeCallback(this, mu); + InvokeCallback(); +} + +// 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 CommandLineFlag::InvokeCallback() + EXCLUSIVE_LOCKS_REQUIRED(this->locks->primary_mu) { + if (!this->callback) return; + + // The callback lock is guaranteed initialized, because *locks->primary_mu + // exists. + absl::Mutex* callback_mu = &this->locks->callback_mu; + + // When executing the callback we need the primary flag's mutex to be unlocked + // so that callback can retrieve the flag's value. + this->locks->primary_mu.Unlock(); + + { + absl::MutexLock lock(callback_mu); + this->callback(); + } + + this->locks->primary_mu.Lock(); } // Attempts to parse supplied `value` string using parsing routine in the `flag` @@ -282,8 +246,9 @@ void CommandLineFlag::SetCallback( // 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' -static bool TryParseLocked(CommandLineFlag* flag, void* dst, - absl::string_view value, std::string* err) { +bool TryParseLocked(CommandLineFlag* flag, void* dst, absl::string_view value, + std::string* err) + 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)) { @@ -297,7 +262,7 @@ static bool TryParseLocked(CommandLineFlag* flag, void* dst, return false; } - if (!Validate(flag, tentative_value)) { + if (!flag->InvokeValidator(tentative_value)) { *err = absl::StrCat("Failed validation of new value '", Unparse(flag->marshalling_op, tentative_value), "' for flag '", flag->Name(), "'"); @@ -324,17 +289,23 @@ bool CommandLineFlag::SetFromString(absl::string_view value, ValueSource source, std::string* err) { if (IsRetired()) return false; - UpdateModifiedBit(this); + absl::MutexLock l(InitFlagIfNecessary()); - absl::Mutex* mu = InitFlagIfNecessary(this); - absl::MutexLock l(mu); + // Direct-access flags can be modified without going through the + // flag API. Detect such changes and update the flag->modified bit. + if (!IsAbseilFlag()) { + if (!this->modified && ChangedDirectly(this, this->cur, this->def)) { + this->modified = true; + } + } switch (set_mode) { case SET_FLAGS_VALUE: { // set or modify the flag's value if (!TryParseLocked(this, this->cur, value, err)) return false; this->modified = true; - UpdateCopy(this, mu); + UpdateCopy(this); + InvokeCallback(); if (source == kCommandLine) { this->on_command_line = true; @@ -346,7 +317,8 @@ bool CommandLineFlag::SetFromString(absl::string_view value, if (!this->modified) { if (!TryParseLocked(this, this->cur, value, err)) return false; this->modified = true; - UpdateCopy(this, mu); + 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 @@ -365,7 +337,8 @@ bool CommandLineFlag::SetFromString(absl::string_view value, if (!this->modified) { // Need to set both defvalue *and* current, in this case Copy(this->op, this->def, this->cur); - UpdateCopy(this, mu); + UpdateCopy(this); + InvokeCallback(); } break; } @@ -379,5 +352,143 @@ bool CommandLineFlag::SetFromString(absl::string_view value, return true; } +void CommandLineFlag::StoreAtomic(size_t size) { + int64_t t = 0; + assert(size <= sizeof(int64_t)); + memcpy(&t, this->cur, size); + this->atomic.store(t, std::memory_order_release); +} + +void CommandLineFlag::CheckDefaultValueParsingRoundtrip() const { + std::string v = DefaultValue(); + + absl::MutexLock lock(InitFlagIfNecessary()); + + void* dst = Clone(this->op, this->def); + std::string error; + if (!flags_internal::Parse(this->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(this->op, dst); +} + +bool CommandLineFlag::ValidateDefaultValue() const { + absl::MutexLock lock(InitFlagIfNecessary()); + return InvokeValidator(this->def); +} + +bool CommandLineFlag::ValidateInputValue(absl::string_view value) const { + absl::MutexLock l(InitFlagIfNecessary()); // protect default value access + + void* obj = Clone(this->op, this->def); + std::string ignored_error; + const bool result = + flags_internal::Parse(this->marshalling_op, value, obj, &ignored_error) && + InvokeValidator(obj); + Delete(this->op, obj); + return result; +} + +const int64_t CommandLineFlag::kAtomicInit; + +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_; + + 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(sizeof(T)); \ + } + + 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 |