diff options
Diffstat (limited to 'absl/flags/internal/commandlineflag.h')
-rw-r--r-- | absl/flags/internal/commandlineflag.h | 365 |
1 files changed, 365 insertions, 0 deletions
diff --git a/absl/flags/internal/commandlineflag.h b/absl/flags/internal/commandlineflag.h new file mode 100644 index 000000000000..4815cdc7c9a2 --- /dev/null +++ b/absl/flags/internal/commandlineflag.h @@ -0,0 +1,365 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_FLAGS_INTERNAL_COMMANDLINEFLAG_H_ +#define ABSL_FLAGS_INTERNAL_COMMANDLINEFLAG_H_ + +#include <atomic> + +#include "absl/base/macros.h" +#include "absl/flags/marshalling.h" +#include "absl/synchronization/mutex.h" +#include "absl/types/optional.h" + +namespace absl { +namespace flags_internal { + +// Type-specific operations, eg., parsing, copying, etc. are provided +// by function specific to that type with a signature matching FlagOpFn. +enum FlagOp { + kDelete, + kClone, + kCopy, + kCopyConstruct, + kSizeof, + kParse, + kUnparse +}; +using FlagOpFn = void* (*)(FlagOp, const void*, void*); +using FlagMarshallingOpFn = void* (*)(FlagOp, const void*, void*, void*); + +// Options that control SetCommandLineOptionWithMode. +enum FlagSettingMode { + // update the flag's value unconditionally (can call this multiple times). + SET_FLAGS_VALUE, + // update the flag's value, but *only if* it has not yet been updated + // with SET_FLAGS_VALUE, SET_FLAG_IF_DEFAULT, or "FLAGS_xxx = nondef". + SET_FLAG_IF_DEFAULT, + // set the flag's default value to this. If the flag has not been updated + // yet (via SET_FLAGS_VALUE, SET_FLAG_IF_DEFAULT, or "FLAGS_xxx = nondef") + // change the flag's current value to the new default value as well. + SET_FLAGS_DEFAULT +}; + +// Options that control SetFromString: Source of a value. +enum ValueSource { + // Flag is being set by value specified on a command line. + kCommandLine, + // Flag is being set by value specified in the code. + kProgrammaticChange, +}; + +// Signature for the help generation function used as an argument for the +// absl::Flag constructor. +using HelpGenFunc = std::string (*)(); + +// Signature for the function generating the initial flag value based (usually +// based on default value supplied in flag's definition) +using InitialValGenFunc = void* (*)(); + +// Signature for the mutation callback used by watched Flags +// The callback is noexcept. +// TODO(rogeeff): add noexcept after C++17 support is added. +using FlagCallback = void (*)(); + +extern const char kStrippedFlagHelp[]; + +// The per-type function +template <typename T> +void* FlagOps(FlagOp op, const void* v1, void* v2) { + switch (op) { + case kDelete: + delete static_cast<const T*>(v1); + return nullptr; + case kClone: + return new T(*static_cast<const T*>(v1)); + case kCopy: + *static_cast<T*>(v2) = *static_cast<const T*>(v1); + return nullptr; + case kCopyConstruct: + new (v2) T(*static_cast<const T*>(v1)); + return nullptr; + case kSizeof: + return reinterpret_cast<void*>(sizeof(T)); + default: + return nullptr; + } +} + +template <typename T> +void* FlagMarshallingOps(FlagOp op, const void* v1, void* v2, void* v3) { + switch (op) { + case kParse: { + // initialize the temporary instance of type T based on current value in + // destination (which is going to be flag's default value). + T temp(*static_cast<T*>(v2)); + if (!absl::ParseFlag<T>(*static_cast<const absl::string_view*>(v1), &temp, + static_cast<std::string*>(v3))) { + return nullptr; + } + *static_cast<T*>(v2) = std::move(temp); + return v2; + } + case kUnparse: + *static_cast<std::string*>(v2) = + absl::UnparseFlag<T>(*static_cast<const T*>(v1)); + return nullptr; + default: + return nullptr; + } +} + +// Functions that invoke flag-type-specific operations. +inline void Delete(FlagOpFn op, const void* obj) { + op(flags_internal::kDelete, obj, nullptr); +} + +inline void* Clone(FlagOpFn op, const void* obj) { + return op(flags_internal::kClone, obj, nullptr); +} + +inline void Copy(FlagOpFn op, const void* src, void* dst) { + op(flags_internal::kCopy, src, dst); +} + +inline void CopyConstruct(FlagOpFn op, const void* src, void* dst) { + op(flags_internal::kCopyConstruct, src, dst); +} + +inline bool Parse(FlagMarshallingOpFn op, absl::string_view text, void* dst, + std::string* error) { + return op(flags_internal::kParse, &text, dst, error) != nullptr; +} + +inline std::string Unparse(FlagMarshallingOpFn op, const void* val) { + std::string result; + op(flags_internal::kUnparse, val, &result, nullptr); + return result; +} + +inline size_t Sizeof(FlagOpFn op) { + // This sequence of casts reverses the sequence from base::internal::FlagOps() + return static_cast<size_t>(reinterpret_cast<intptr_t>( + 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*, +// const char*(*)()> since anybody passing 0 or nullptr in to a CommandLineFlag +// would find an ambiguity. +class HelpText { + public: + static constexpr HelpText FromFunctionPointer(const HelpGenFunc fn) { + return HelpText(fn, nullptr); + } + static constexpr HelpText FromStaticCString(const char* msg) { + return HelpText(nullptr, msg); + } + + std::string GetHelpText() const; + + HelpText() = delete; + HelpText(const HelpText&) = default; + HelpText(HelpText&&) = default; + + private: + explicit constexpr HelpText(const HelpGenFunc fn, const char* msg) + : help_function_(fn), help_message_(msg) {} + + HelpGenFunc help_function_; + const char* help_message_; +}; + +// Holds all information for a flag. +struct CommandLineFlag { + constexpr CommandLineFlag( + const char* name_arg, HelpText help_text, const char* filename_arg, + const flags_internal::FlagOpFn op_arg, + const flags_internal::FlagMarshallingOpFn marshalling_op_arg, + const flags_internal::InitialValGenFunc initial_value_gen, + const bool retired_arg, void* def_arg, void* cur_arg) + : name(name_arg), + help(help_text), + filename(filename_arg), + op(op_arg), + marshalling_op(marshalling_op_arg), + make_init_value(initial_value_gen), + retired(retired_arg), + inited(false), + modified(false), + on_command_line(false), + validator(nullptr), + callback(nullptr), + def(def_arg), + cur(cur_arg), + counter(0), + atomic(kAtomicInit), + locks(nullptr) {} + + // Not copyable/assignable. + CommandLineFlag(const CommandLineFlag&) = delete; + CommandLineFlag& operator=(const CommandLineFlag&) = delete; + + absl::string_view Name() const { return name; } + std::string Help() const { return help.GetHelpText(); } + bool IsRetired() const { return this->retired; } + bool IsSpecifiedOnCommandLine() const { return on_command_line; } + // Returns true iff this is a handle to an Abseil Flag. + bool IsAbseilFlag() const { + // Set to null for V1 flags + return this->make_init_value != nullptr; + } + + absl::string_view Typename() const; + std::string Filename() const; + std::string DefaultValue() const; + std::string CurrentValue() const; + + // Return true iff flag has type T. + template <typename T> + inline bool IsOfType() const { + return this->op == &flags_internal::FlagOps<T>; + } + + // Attempts to retrieve the flag value. Returns value on success, + // absl::nullopt otherwise. + template <typename T> + absl::optional<T> Get() { + if (IsRetired() || flags_internal::FlagOps<T> != this->op) + return absl::nullopt; + + T res; + Read(&res, flags_internal::FlagOps<T>); + + return res; + } + + void SetCallback(const flags_internal::FlagCallback mutation_callback); + + // 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 + // 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 SetFromString(absl::string_view value, + flags_internal::FlagSettingMode set_mode, + flags_internal::ValueSource source, std::string* error); + + // Constant configuration for a particular flag. + private: + const char* const name; + const HelpText help; + const char* const filename; + + public: + const FlagOpFn op; // Type-specific handler + const FlagMarshallingOpFn marshalling_op; // Marshalling ops handler + const InitialValGenFunc make_init_value; // Makes initial value for the flag + const bool retired; // Is the flag retired? + 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. + bool (*validator)(); // Validator function, or nullptr + FlagCallback callback; // Mutation callback, or nullptr + void* def; // Lazily initialized pointer to default value + void* cur; // Lazily initialized pointer to current value + int64_t counter; // Mutation counter + + // For some types, a copy of the current value is kept in an atomically + // accessible field. + static const int64_t kAtomicInit = 0xababababababababll; + std::atomic<int64_t> atomic; + + // 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. + + // 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); + + ABSL_DEPRECATED( + "temporary until FlagName call sites are migrated and validator API is " + "changed") + const char* NameAsCString() const { return name; } + + private: + friend class FlagRegistry; +}; + +// Ensure that the lazily initialized fields of *flag have been initialized, +// and return &flag->locks->primary_mu. +absl::Mutex* InitFlagIfNecessary(CommandLineFlag* flag); +// 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, absl::Mutex* primary_lock); +// Return true iff flag value was changed via direct-access. +bool ChangedDirectly(CommandLineFlag* flag, const void* a, const void* b); +// Direct-access flags can be modified without going through the +// flag API. Detect such changes and updated the modified bit. +void UpdateModifiedBit(CommandLineFlag* flag); +// Invoke the flag validators for old flags. +// TODO(rogeeff): implement proper validators for Abseil Flags +bool Validate(CommandLineFlag* flag, const void* value); + +// 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 +// each type in the list. +#define ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(A) \ + A(bool) \ + A(short) \ + A(unsigned short) \ + A(int) \ + A(unsigned int) \ + A(long) \ + A(unsigned long) \ + A(long long) \ + A(unsigned long long) \ + A(double) \ + A(float) + +} // namespace flags_internal +} // namespace absl + +#endif // ABSL_FLAGS_INTERNAL_COMMANDLINEFLAG_H_ |