// // 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_FLAG_H_ #define ABSL_FLAGS_INTERNAL_FLAG_H_ #include <stddef.h> #include <stdint.h> #include <atomic> #include <cstring> #include <memory> #include <string> #include <type_traits> #include <typeinfo> #include "absl/base/attributes.h" #include "absl/base/call_once.h" #include "absl/base/casts.h" #include "absl/base/config.h" #include "absl/base/optimization.h" #include "absl/base/thread_annotations.h" #include "absl/flags/commandlineflag.h" #include "absl/flags/config.h" #include "absl/flags/internal/commandlineflag.h" #include "absl/flags/internal/registry.h" #include "absl/flags/internal/sequence_lock.h" #include "absl/flags/marshalling.h" #include "absl/meta/type_traits.h" #include "absl/strings/string_view.h" #include "absl/synchronization/mutex.h" #include "absl/utility/utility.h" namespace absl { ABSL_NAMESPACE_BEGIN /////////////////////////////////////////////////////////////////////////////// // Forward declaration of absl::Flag<T> public API. namespace flags_internal { template <typename T> class Flag; } // namespace flags_internal Flag; template <typename T> ABSL_MUST_USE_RESULT T GetFlag(const absl::Flag<T>& flag); template <typename T> void SetFlag(absl::Flag<T>* flag, const T& v); template <typename T, typename V> void SetFlag(absl::Flag<T>* flag, const V& v); template <typename U> const CommandLineFlag& GetFlagReflectionHandle(const absl::Flag<U>& f); /////////////////////////////////////////////////////////////////////////////// // Flag value type operations, eg., parsing, copying, etc. are provided // by function specific to that type with a signature matching FlagOpFn. namespace flags_internal { enum class FlagOp { … }; FlagOpFn; // Forward declaration for Flag value specific operations. template <typename T> void* FlagOps(FlagOp op, const void* v1, void* v2, void* v3); // Allocate aligned memory for a flag value. inline void* Alloc(FlagOpFn op) { … } // Deletes memory interpreting obj as flag value type pointer. inline void Delete(FlagOpFn op, void* obj) { … } // Copies src to dst interpreting as flag value type pointers. inline void Copy(FlagOpFn op, const void* src, void* dst) { … } // Construct a copy of flag value in a location pointed by dst // based on src - pointer to the flag's value. inline void CopyConstruct(FlagOpFn op, const void* src, void* dst) { … } // Makes a copy of flag value pointed by obj. inline void* Clone(FlagOpFn op, const void* obj) { … } // Returns true if parsing of input text is successful. inline bool Parse(FlagOpFn op, absl::string_view text, void* dst, std::string* error) { … } // Returns string representing supplied value. inline std::string Unparse(FlagOpFn op, const void* val) { … } // Returns size of flag value type. inline size_t Sizeof(FlagOpFn op) { … } // Returns fast type id corresponding to the value type. inline FlagFastTypeId FastTypeId(FlagOpFn op) { … } // Returns fast type id corresponding to the value type. inline const std::type_info* RuntimeTypeId(FlagOpFn op) { … } // Returns offset of the field value_ from the field impl_ inside of // absl::Flag<T> data. Given FlagImpl pointer p you can get the // location of the corresponding value as: // reinterpret_cast<char*>(p) + ValueOffset(). inline ptrdiff_t ValueOffset(FlagOpFn op) { … } // Returns an address of RTTI's typeid(T). template <typename T> inline const std::type_info* GenRuntimeTypeId() { … } /////////////////////////////////////////////////////////////////////////////// // Flag help auxiliary structs. // This is help argument for absl::Flag encapsulating the string literal pointer // or pointer to function generating it as well as enum descriminating two // cases. HelpGenFunc; template <size_t N> struct FixedCharArray { … }; template <typename Gen, size_t N = Gen::Value().size()> constexpr FixedCharArray<N + 1> HelpStringAsArray(int) { … } template <typename Gen> constexpr std::false_type HelpStringAsArray(char) { … } FlagHelpMsg; enum class FlagHelpKind : uint8_t { … }; struct FlagHelpArg { … }; extern const char kStrippedFlagHelp[]; // These two HelpArg overloads allows us to select at compile time one of two // way to pass Help argument to absl::Flag. We'll be passing // AbslFlagHelpGenFor##name as Gen and integer 0 as a single argument to prefer // first overload if possible. If help message is evaluatable on constexpr // context We'll be able to make FixedCharArray out of it and we'll choose first // overload. In this case the help message expression is immediately evaluated // and is used to construct the absl::Flag. No additional code is generated by // ABSL_FLAG Otherwise SFINAE kicks in and first overload is dropped from the // consideration, in which case the second overload will be used. The second // overload does not attempt to evaluate the help message expression // immediately and instead delays the evaluation by returning the function // pointer (&T::NonConst) generating the help message when necessary. This is // evaluatable in constexpr context, but the cost is an extra function being // generated in the ABSL_FLAG code. template <typename Gen, size_t N> constexpr FlagHelpArg HelpArg(const FixedCharArray<N>& value) { … } template <typename Gen> constexpr FlagHelpArg HelpArg(std::false_type) { … } /////////////////////////////////////////////////////////////////////////////// // Flag default value auxiliary structs. // Signature for the function generating the initial flag value (usually // based on default value supplied in flag's definition) FlagDfltGenFunc; FlagDefaultSrc; enum class FlagDefaultKind : uint8_t { … }; struct FlagDefaultArg { … }; // This struct and corresponding overload to InitDefaultValue are used to // facilitate usage of {} as default value in ABSL_FLAG macro. // TODO(rogeeff): Fix handling types with explicit constructors. struct EmptyBraces { … }; template <typename T> constexpr T InitDefaultValue(T t) { … } template <typename T> constexpr T InitDefaultValue(EmptyBraces) { … } template <typename ValueT, typename GenT, typename std::enable_if<std::is_integral<ValueT>::value, int>::type = ((void)GenT{ … } template <typename ValueT, typename GenT> constexpr FlagDefaultArg DefaultArg(char) { … } /////////////////////////////////////////////////////////////////////////////// // Flag storage selector traits. Each trait indicates what kind of storage kind // to use for the flag value. FlagUseValueAndInitBitStorage; FlagUseOneWordStorage; FlagUseSequenceLockStorage; enum class FlagValueStorageKind : uint8_t { … }; // This constexpr function returns the storage kind for the given flag value // type. template <typename T> static constexpr FlagValueStorageKind StorageKind() { … } // This is a base class for the storage classes used by kOneWordAtomic and // kValueAndInitBit storage kinds. It literally just stores the one word value // as an atomic. By default, it is initialized to a magic value that is unlikely // a valid value for the flag value type. struct FlagOneWordValue { … }; // This class represents a memory layout used by kValueAndInitBit storage kind. template <typename T> struct alignas(8) FlagValueAndInitBit { … }; // This class implements an aligned pointer with two options stored via masks // in unused bits of the pointer value (due to alignment requirement). // - IsUnprotectedReadCandidate - indicates that the value can be switched to // unprotected read without a lock. // - HasBeenRead - indicates that the value has been read at least once. // - AllowsUnprotectedRead - combination of the two options above and indicates // that the value can now be read without a lock. // Further details of these options and their use is covered in the description // of the FlagValue<T, FlagValueStorageKind::kHeapAllocated> specialization. class MaskedPointer { … }; // This class implements a type erased storage of the heap allocated flag value. // It is used as a base class for the storage class for kHeapAllocated storage // kind. The initial_buffer is expected to have an alignment of at least // MaskedPointer::RequiredAlignment(), so that the bits used by the // MaskedPointer to store masks are set to 0. This guarantees that value starts // in an uninitialized state. struct FlagMaskedPointerValue { … }; // This is the forward declaration for the template that represents a storage // for the flag values. This template is expected to be explicitly specialized // for each storage kind and it does not have a generic default // implementation. template <typename T, FlagValueStorageKind Kind = flags_internal::StorageKind<T>()> struct FlagValue; // This specialization represents the storage of flag values types with the // kValueAndInitBit storage kind. It is based on the FlagOneWordValue class // and relies on memory layout in FlagValueAndInitBit<T> to indicate that the // value has been initialized or not. FlagValue<T, FlagValueStorageKind::kValueAndInitBit>; // This specialization represents the storage of flag values types with the // kOneWordAtomic storage kind. It is based on the FlagOneWordValue class // and relies on the magic uninitialized state of default constructed instead of // FlagOneWordValue to indicate that the value has been initialized or not. FlagValue<T, FlagValueStorageKind::kOneWordAtomic>; // This specialization represents the storage of flag values types with the // kSequenceLocked storage kind. This storage is used by trivially copyable // types with size greater than 8 bytes. This storage relies on uninitialized // state of the SequenceLock to indicate that the value has been initialized or // not. This storage also provides lock-free read access to the underlying // value once it is initialized. FlagValue<T, FlagValueStorageKind::kSequenceLocked>; // This specialization represents the storage of flag values types with the // kHeapAllocated storage kind. This is a storage of last resort and is used // if none of other storage kinds are applicable. // // Generally speaking the values with this storage kind can't be accessed // atomically and thus can't be read without holding a lock. If we would ever // want to avoid the lock, we'd need to leak the old value every time new flag // value is being set (since we are in danger of having a race condition // otherwise). // // Instead of doing that, this implementation attempts to cater to some common // use cases by allowing at most 2 values to be leaked - default value and // value set from the command line. // // This specialization provides an initial buffer for the first flag value. This // is where the default value is going to be stored. We attempt to reuse this // buffer if possible, including storing the value set from the command line // there. // // As long as we only read this value, we can access it without a lock (in // practice we still use the lock for the very first read to be able set // "has been read" option on this flag). // // If flag is specified on the command line we store the parsed value either // in the internal buffer (if the default value never been read) or we leak the // default value and allocate the new storage for the parse value. This value is // also a candidate for an unprotected read. If flag is set programmatically // after the command line is parsed, the storage for this value is going to be // leaked. Note that in both scenarios we are not going to have a real leak. // Instead we'll store the leaked value pointers in the internal freelist to // avoid triggering the memory leak checker complains. // // If the flag is ever set programmatically, it stops being the candidate for an // unprotected read, and any follow up access to the flag value requires a lock. // Note that if the value if set programmatically before the command line is // parsed, we can switch back to enabling unprotected reads for that value. FlagValue<T, FlagValueStorageKind::kHeapAllocated>; /////////////////////////////////////////////////////////////////////////////// // Flag callback auxiliary structs. // Signature for the mutation callback used by watched Flags // The callback is noexcept. // TODO(rogeeff): add noexcept after C++17 support is added. FlagCallbackFunc; struct FlagCallback { … }; /////////////////////////////////////////////////////////////////////////////// // Flag implementation, which does not depend on flag value type. // The class encapsulates the Flag's data and access to it. struct DynValueDeleter { … }; class FlagState; // These are only used as constexpr global objects. // They do not use a virtual destructor to simplify their implementation. // They are not destroyed except at program exit, so leaks do not matter. #if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wnon-virtual-dtor" #endif class FlagImpl final : public CommandLineFlag { … }; #if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic pop #endif /////////////////////////////////////////////////////////////////////////////// // The Flag object parameterized by the flag's value type. This class implements // flag reflection handle interface. template <typename T> class Flag { … }; /////////////////////////////////////////////////////////////////////////////// // Trampoline for friend access class FlagImplPeer { … }; /////////////////////////////////////////////////////////////////////////////// // Implementation of Flag value specific operations routine. template <typename T> void* FlagOps(FlagOp op, const void* v1, void* v2, void* v3) { … } /////////////////////////////////////////////////////////////////////////////// // This class facilitates Flag object registration and tail expression-based // flag definition, for example: // ABSL_FLAG(int, foo, 42, "Foo help").OnUpdate(NotifyFooWatcher); struct FlagRegistrarEmpty { … }; template <typename T, bool do_register> class FlagRegistrar { … }; /////////////////////////////////////////////////////////////////////////////// // Test only API uint64_t NumLeakedFlagValues(); } // namespace flags_internal ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_FLAGS_INTERNAL_FLAG_H_