summaryrefslogtreecommitdiff
path: root/absl/flags/internal/flag.h
diff options
context:
space:
mode:
Diffstat (limited to 'absl/flags/internal/flag.h')
-rw-r--r--absl/flags/internal/flag.h237
1 files changed, 160 insertions, 77 deletions
diff --git a/absl/flags/internal/flag.h b/absl/flags/internal/flag.h
index 2b21c440..ce0ccf2d 100644
--- a/absl/flags/internal/flag.h
+++ b/absl/flags/internal/flag.h
@@ -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>