about summary refs log tree commit diff
path: root/absl/flags
diff options
context:
space:
mode:
Diffstat (limited to 'absl/flags')
-rw-r--r--absl/flags/BUILD.bazel3
-rw-r--r--absl/flags/flag.cc4
-rw-r--r--absl/flags/flag.h8
-rw-r--r--absl/flags/internal/commandlineflag.cc6
-rw-r--r--absl/flags/internal/commandlineflag.h24
-rw-r--r--absl/flags/internal/flag.h85
-rw-r--r--absl/flags/internal/registry.cc106
7 files changed, 124 insertions, 112 deletions
diff --git a/absl/flags/BUILD.bazel b/absl/flags/BUILD.bazel
index 2bf562f898c7..2e0dc3891f56 100644
--- a/absl/flags/BUILD.bazel
+++ b/absl/flags/BUILD.bazel
@@ -40,6 +40,8 @@ cc_library(
     deps = [
         ":handle",
         ":registry",
+        "//absl/memory",
+        "//absl/strings",
         "//absl/synchronization",
     ],
 )
@@ -184,6 +186,7 @@ cc_library(
         ":marshalling",
         "//absl/base",
         "//absl/base:core_headers",
+        "//absl/memory",
         "//absl/strings",
     ],
 )
diff --git a/absl/flags/flag.cc b/absl/flags/flag.cc
index a02d069e9b00..69038bbf48a0 100644
--- a/absl/flags/flag.cc
+++ b/absl/flags/flag.cc
@@ -49,9 +49,7 @@ namespace flags_internal {
 
 ABSL_CONST_INIT static absl::Mutex construction_guard(absl::kConstInit);
 
-void LockGlobalConstructionGuard() { construction_guard.Lock(); }
-
-void UnlockGlobalConstructionGuard() { construction_guard.Unlock(); }
+absl::Mutex* GetGlobalConstructionGuard() { return &construction_guard; }
 
 }  // namespace flags_internal
 
diff --git a/absl/flags/flag.h b/absl/flags/flag.h
index 86ad59dd9f16..4927757b8fbe 100644
--- a/absl/flags/flag.h
+++ b/absl/flags/flag.h
@@ -80,8 +80,7 @@ using Flag = flags_internal::Flag<T>;
 // if two threads attempt to construct the flag concurrently only one wins.
 
 namespace flags_internal {
-void LockGlobalConstructionGuard();
-void UnlockGlobalConstructionGuard();
+absl::Mutex* GetGlobalConstructionGuard();
 }  // namespace flags_internal
 
 template <typename T>
@@ -100,7 +99,7 @@ class Flag {
 
   flags_internal::Flag<T>* GetImpl() const {
     if (!inited_.load(std::memory_order_acquire)) {
-      flags_internal::LockGlobalConstructionGuard();
+      absl::MutexLock l(flags_internal::GetGlobalConstructionGuard());
 
       if (inited_.load(std::memory_order_acquire)) {
         return impl_;
@@ -109,8 +108,6 @@ class Flag {
       impl_ = new flags_internal::Flag<T>(name_, help_gen_, filename_,
                                           marshalling_op_, initial_value_gen_);
       inited_.store(true, std::memory_order_release);
-
-      flags_internal::UnlockGlobalConstructionGuard();
     }
 
     return impl_;
@@ -130,7 +127,6 @@ class Flag {
   std::string Filename() const { return GetImpl()->Filename(); }
   std::string DefaultValue() const { return GetImpl()->DefaultValue(); }
   std::string CurrentValue() const { return GetImpl()->CurrentValue(); }
-  bool HasValidatorFn() const { return GetImpl()->HasValidatorFn(); }
   bool InvokeValidator(const void* value) const {
     return GetImpl()->InvokeValidator(value);
   }
diff --git a/absl/flags/internal/commandlineflag.cc b/absl/flags/internal/commandlineflag.cc
index 53e2b84ecc13..99f73611d8f1 100644
--- a/absl/flags/internal/commandlineflag.cc
+++ b/absl/flags/internal/commandlineflag.cc
@@ -149,6 +149,12 @@ std::string CommandLineFlag::CurrentValue() const {
   return Unparse(marshalling_op_, cur_);
 }
 
+int64_t CommandLineFlag::MutationCounter() const {
+  absl::MutexLock l(InitFlagIfNecessary());
+
+  return counter_;
+}
+
 // Attempts to parse supplied `value` string using parsing routine in the `flag`
 // argument. If parsing is successful, it will try to validate that the parsed
 // value is valid for the specified 'flag'. Finally this function stores the
diff --git a/absl/flags/internal/commandlineflag.h b/absl/flags/internal/commandlineflag.h
index 284286b6be18..528d3106bfa8 100644
--- a/absl/flags/internal/commandlineflag.h
+++ b/absl/flags/internal/commandlineflag.h
@@ -17,6 +17,7 @@
 #define ABSL_FLAGS_INTERNAL_COMMANDLINEFLAG_H_
 
 #include <atomic>
+#include <memory>
 
 #include "absl/base/macros.h"
 #include "absl/flags/marshalling.h"
@@ -186,6 +187,16 @@ class HelpText {
   const char* help_message_;
 };
 
+// Handle to FlagState objects. Specific flag state objects will restore state
+// of a flag produced this flag state from method CommandLineFlag::SaveState().
+class FlagStateInterface {
+ public:
+  virtual ~FlagStateInterface() {}
+
+  // Restores the flag originated this object to the saved state.
+  virtual void Restore() const = 0;
+};
+
 // Holds all information for a flag.
 class CommandLineFlag {
  public:
@@ -239,7 +250,6 @@ class CommandLineFlag {
   virtual void StoreAtomic() {}
 
   // Interfaces to operate on validators.
-  virtual bool HasValidatorFn() const { return false; }
   virtual bool InvokeValidator(const void* /*value*/) const { return true; }
   // Invoke the flag validators for old flags.
   // TODO(rogeeff): implement proper validators for Abseil Flags
@@ -264,6 +274,10 @@ class CommandLineFlag {
     return res;
   }
 
+  // Interface to save flag to some persistent state. Returns current flag state
+  // or nullptr if flag does not support saving and restoring a state.
+  virtual std::unique_ptr<FlagStateInterface> SaveState() = 0;
+
   // Interfaces to overate on callbacks.
   virtual void InvokeCallback() {}
 
@@ -285,6 +299,9 @@ class CommandLineFlag {
  protected:
   ~CommandLineFlag() = default;
 
+  // Thread safe access to mutation counter.
+  int64_t MutationCounter() const;
+
   const char* const name_;
   const HelpText help_;
   const char* const filename_;
@@ -323,11 +340,6 @@ class CommandLineFlag {
   friend bool TryParseLocked(CommandLineFlag* flag, void* dst,
                              absl::string_view value, std::string* err);
   friend absl::Mutex* InitFlag(CommandLineFlag* flag);
-
-  // This is a short term, until we completely rework persistent state
-  // storage API.
-  virtual void* GetValidator() const { return nullptr; }
-  virtual bool SetValidator(void*) { return false; }
 };
 
 // Update any copy of the flag value that is stored in an atomic word.
diff --git a/absl/flags/internal/flag.h b/absl/flags/internal/flag.h
index 16330380f548..2b21c4407363 100644
--- a/absl/flags/internal/flag.h
+++ b/absl/flags/internal/flag.h
@@ -20,12 +20,44 @@
 
 #include "absl/flags/internal/commandlineflag.h"
 #include "absl/flags/internal/registry.h"
+#include "absl/memory/memory.h"
+#include "absl/strings/str_cat.h"
 
 namespace absl {
 namespace flags_internal {
 
 constexpr int64_t AtomicInit() { return 0xababababababababll; }
 
+template <typename T>
+class Flag;
+
+template <typename T>
+class FlagState : public flags_internal::FlagStateInterface {
+ public:
+  FlagState(Flag<T>* flag, T&& cur, bool modified, bool on_command_line,
+            int64_t counter)
+      : flag_(flag),
+        cur_value_(std::move(cur)),
+        modified_(modified),
+        on_command_line_(on_command_line),
+        counter_(counter) {}
+
+  ~FlagState() override = default;
+
+ private:
+  friend class Flag<T>;
+
+  // Restores the flag to the saved state.
+  void Restore() const override;
+
+  // Flag and saved flag data.
+  Flag<T>* flag_;
+  T cur_value_;
+  bool modified_;
+  bool on_command_line_;
+  int64_t counter_;
+};
+
 // Signature for the mutation callback used by watched Flags
 // The callback is noexcept.
 // TODO(rogeeff): add noexcept after C++17 support is added.
@@ -98,15 +130,12 @@ class Flag final : public flags_internal::CommandLineFlag {
 
     InvokeCallback();
   }
-  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 {
-    // Values are heap allocated Abseil Flags.
+    // Values are heap allocated for Abseil Flags.
     if (cur_) Delete(op_, cur_);
     if (def_) Delete(op_, def_);
 
@@ -121,6 +150,41 @@ class Flag final : public flags_internal::CommandLineFlag {
     }
   }
 
+  // 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_);
+  }
+
+  // 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_);
+
+    // Race condition here? This should disappear once we move the rest of the
+    // flag's data into Flag's internals.
+
+    absl::MutexLock l(InitFlagIfNecessary());
+    modified_ = flag_state.modified_;
+    on_command_line_ = flag_state.on_command_line_;
+    return true;
+  }
+
+  // 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_);
+  }
+
   // Flag's data
   // For some types, a copy of the current value is kept in an atomically
   // accessible field.
@@ -128,6 +192,15 @@ class Flag final : public flags_internal::CommandLineFlag {
   FlagCallback callback_;  // Mutation callback
 };
 
+template <typename T>
+inline void FlagState<T>::Restore() const {
+  if (flag_->RestoreState(*this)) {
+    ABSL_INTERNAL_LOG(INFO,
+                      absl::StrCat("Restore saved value of ", flag_->Name(),
+                                   " to: ", flag_->CurrentValue()));
+  }
+}
+
 // This class facilitates Flag object registration and tail expression-based
 // flag definition, for example:
 // ABSL_FLAG(int, foo, 42, "Foo help").OnUpdate(NotifyFooWatcher);
diff --git a/absl/flags/internal/registry.cc b/absl/flags/internal/registry.cc
index 4bea313bb1eb..6b2564d13a78 100644
--- a/absl/flags/internal/registry.cc
+++ b/absl/flags/internal/registry.cc
@@ -188,114 +188,34 @@ CommandLineFlag* FlagRegistry::FindRetiredFlagLocked(absl::string_view name) {
 
 class FlagSaverImpl {
  public:
-  // Constructs an empty FlagSaverImpl object.
-  FlagSaverImpl() {}
-  ~FlagSaverImpl() {
-    // reclaim memory from each of our CommandLineFlags
-    for (const SavedFlag& src : backup_registry_) {
-      Delete(src.op, src.current);
-      Delete(src.op, src.default_value);
-    }
-  }
+  FlagSaverImpl() = default;
+  FlagSaverImpl(const FlagSaverImpl&) = delete;
+  void operator=(const FlagSaverImpl&) = delete;
 
   // Saves the flag states from the flag registry into this object.
   // It's an error to call this more than once.
-  // Must be called when the registry mutex is not held.
   void SaveFromRegistry() {
     assert(backup_registry_.empty());  // call only once!
-    SavedFlag saved;
     flags_internal::ForEachFlag([&](flags_internal::CommandLineFlag* flag) {
-      if (flag->IsRetired()) return;
-
-      saved.name = flag->Name();
-      saved.op = flag->op_;
-      saved.marshalling_op = flag->marshalling_op_;
-      {
-        absl::MutexLock l(flag->InitFlagIfNecessary());
-        saved.validator = flag->GetValidator();
-        saved.modified = flag->modified_;
-        saved.on_command_line = flag->on_command_line_;
-        saved.current = Clone(saved.op, flag->cur_);
-        saved.default_value = Clone(saved.op, flag->def_);
-        saved.counter = flag->counter_;
+      if (auto flag_state = flag->SaveState()) {
+        backup_registry_.emplace_back(std::move(flag_state));
       }
-      backup_registry_.push_back(saved);
     });
   }
 
-  // Restores the saved flag states into the flag registry.  We
-  // assume no flags were added or deleted from the registry since
-  // the SaveFromRegistry; if they were, that's trouble!  Must be
-  // called when the registry mutex is not held.
+  // Restores the saved flag states into the flag registry.
   void RestoreToRegistry() {
-    FlagRegistry* const global_registry = FlagRegistry::GlobalRegistry();
-    FlagRegistryLock frl(global_registry);
-    for (const SavedFlag& src : backup_registry_) {
-      CommandLineFlag* flag = global_registry->FindFlagLocked(src.name);
-      // If null, flag got deleted from registry.
-      if (!flag) continue;
-
-      bool restored = false;
-      {
-        // This function encapsulate the lock.
-        flag->SetValidator(src.validator);
-
-        absl::MutexLock l(flag->InitFlagIfNecessary());
-        flag->modified_ = src.modified;
-        flag->on_command_line_ = src.on_command_line;
-        if (flag->counter_ != src.counter ||
-            ChangedDirectly(flag, src.default_value, flag->def_)) {
-          restored = true;
-          Copy(src.op, src.default_value, flag->def_);
-        }
-        if (flag->counter_ != src.counter ||
-            ChangedDirectly(flag, src.current, flag->cur_)) {
-          restored = true;
-          Copy(src.op, src.current, flag->cur_);
-          UpdateCopy(flag);
-          flag->InvokeCallback();
-        }
-      }
-
-      if (restored) {
-        flag->counter_++;
-
-        // Revalidate the flag because the validator might store state based
-        // on the flag's value, which just changed due to the restore.
-        // Failing validation is ignored because it's assumed that the flag
-        // was valid previously and there's little that can be done about it
-        // here, anyway.
-        flag->ValidateInputValue(flag->CurrentValue());
-
-        ABSL_INTERNAL_LOG(
-            INFO, absl::StrCat("Restore saved value of ", flag->Name(), ": ",
-                               Unparse(src.marshalling_op, src.current)));
-      }
+    for (const auto& flag_state : backup_registry_) {
+      flag_state->Restore();
     }
   }
 
  private:
-  struct SavedFlag {
-    absl::string_view name;
-    FlagOpFn op;
-    FlagMarshallingOpFn marshalling_op;
-    int64_t counter;
-    void* validator;
-    bool modified;
-    bool on_command_line;
-    const void* current;        // nullptr after restore
-    const void* default_value;  // nullptr after restore
-  };
-
-  std::vector<SavedFlag> backup_registry_;
-
-  FlagSaverImpl(const FlagSaverImpl&);  // no copying!
-  void operator=(const FlagSaverImpl&);
+  std::vector<std::unique_ptr<flags_internal::FlagStateInterface>>
+      backup_registry_;
 };
 
-FlagSaver::FlagSaver() : impl_(new FlagSaverImpl()) {
-  impl_->SaveFromRegistry();
-}
+FlagSaver::FlagSaver() : impl_(new FlagSaverImpl) { impl_->SaveFromRegistry(); }
 
 void FlagSaver::Ignore() {
   delete impl_;
@@ -376,6 +296,10 @@ class RetiredFlagObj final : public flags_internal::CommandLineFlag {
 
     delete this;
   }
+
+  std::unique_ptr<flags_internal::FlagStateInterface> SaveState() override {
+    return nullptr;
+  }
 };
 
 }  // namespace