@ -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,6 +69,124 @@ using FlagCallback = void (*)();
void InvokeCallback ( absl : : Mutex * primary_mu , absl : : Mutex * callback_mu ,
FlagCallback cb ) ABSL_EXCLUSIVE_LOCKS_REQUIRED ( primary_mu ) ;
// The class encapsulates the Flag's data and safe access to it.
class FlagImpl {
public :
constexpr FlagImpl ( const flags_internal : : FlagOpFn op ,
const flags_internal : : FlagMarshallingOpFn marshalling_op ,
const flags_internal : : InitialValGenFunc initial_value_gen )
: op_ ( op ) ,
marshalling_op_ ( marshalling_op ) ,
initial_value_gen_ ( initial_value_gen ) { }
// 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 ( ) ) {
std : : memcpy ( v , & r , sizeof ( T ) ) ;
return true ;
}
return false ;
}
// 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 ) ;
// 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 ) ;
// 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 ( ) ) ;
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 :
// 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 ) ;
// 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
// 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 {
@ -76,30 +197,11 @@ class Flag final : public flags_internal::CommandLineFlag {
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 ) { }
filename ) ,
impl_ ( & flags_internal : : FlagOps < T > , marshalling_op , 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.
// See implementation notes in CommandLineFlag::Get().
union U {
T value ;
U ( ) { }
@ -107,89 +209,70 @@ class Flag final : public flags_internal::CommandLineFlag {
} ;
U u ;
Read ( & u . value , & flags_internal : : FlagOps < T > ) ;
impl_ . Read ( * this , & u . value , & flags_internal : : FlagOps < T > ) ;
return std : : move ( u . value ) ;
}
bool AtomicGet ( T * v ) const {
const int64_t r = atomic_ . load ( std : : memory_order_acquire ) ;
if ( r ! = flags_internal : : AtomicInit ( ) ) {
std : : memcpy ( v , & r , sizeof ( T ) ) ;
return true ;
}
return false ;
}
bool AtomicGet ( T * v ) const { return impl_ . AtomicGet ( v ) ; }
void Set ( const T & v ) { Write ( & v , & flags_internal : : FlagOps < T > ) ; }
void Set ( const T & v ) { impl_ . Write ( * this , & v , & flags_internal : : FlagOps < T > ) ; }
void SetCallback ( const flags_internal : : FlagCallback mutation_callback ) {
absl : : MutexLock l ( InitFlagIfNecessary ( ) ) ;
callback_ = mutation_callback ;
InvokeCallback ( ) ;
impl_ . SetCallback ( mutation_callback ) ;
}
private :
friend class FlagState < T > ;
void Destroy ( ) const override {
// Values are heap allocated for Abseil Flags.
if ( cur_ ) Delete ( op_ , cur_ ) ;
if ( def_ ) Delete ( op_ , def_ ) ;
delete locks_ ;
// 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 ( ) ; }
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 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_ ) ;
return impl_ . RestoreState ( * this , & flag_state . cur_value_ ,
flag_state . modified_ , flag_state . on_command_line_ ,
flag_state . counter_ ) ;
}
// Race condition here? This should disappear once we move the rest of the
// flag's data into Flag's internals.
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 ) ;
}
absl : : MutexLock l ( InitFlagIfNecessary ( ) ) ;
modified_ = flag_state . modified_ ;
on_command_line_ = flag_state . on_command_line_ ;
return true ;
void CheckDefaultValueParsingRoundtrip ( ) const override {
impl_ . CheckDefaultValueParsingRoundtrip ( * this ) ;
}
// 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_ ) ;
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 >