about summary refs log tree commit diff
path: root/absl/flags/internal
diff options
context:
space:
mode:
Diffstat (limited to 'absl/flags/internal')
-rw-r--r--absl/flags/internal/commandlineflag.cc93
-rw-r--r--absl/flags/internal/commandlineflag.h64
-rw-r--r--absl/flags/internal/flag.cc51
-rw-r--r--absl/flags/internal/flag.h49
-rw-r--r--absl/flags/internal/registry.cc62
-rw-r--r--absl/flags/internal/registry.h21
-rw-r--r--absl/flags/internal/usage_test.cc2
7 files changed, 165 insertions, 177 deletions
diff --git a/absl/flags/internal/commandlineflag.cc b/absl/flags/internal/commandlineflag.cc
index 98a46695304b..2063cda64d5d 100644
--- a/absl/flags/internal/commandlineflag.cc
+++ b/absl/flags/internal/commandlineflag.cc
@@ -68,7 +68,7 @@ absl::Mutex* InitFlag(CommandLineFlag* flag) {
   {
     absl::MutexLock lock(mu);
 
-    if (!flag->retired && flag->def == nullptr) {
+    if (!flag->IsRetired() && flag->def == nullptr) {
       // Need to initialize def and cur fields.
       flag->def = (*flag->make_init_value)();
       flag->cur = Clone(flag->op, flag->def);
@@ -94,16 +94,6 @@ absl::Mutex* CommandLineFlag::InitFlagIfNecessary() const
   return &this->locks->primary_mu;
 }
 
-void CommandLineFlag::Destroy() const {
-  // Values are heap allocated for retired and Abseil Flags.
-  if (IsRetired() || IsAbseilFlag()) {
-    if (this->cur) Delete(this->op, this->cur);
-    if (this->def) Delete(this->op, this->def);
-  }
-
-  delete this->locks;
-}
-
 bool CommandLineFlag::IsModified() const {
   absl::MutexLock l(InitFlagIfNecessary());
   return modified;
@@ -159,87 +149,6 @@ std::string CommandLineFlag::CurrentValue() const {
   return Unparse(this->marshalling_op, this->cur);
 }
 
-bool CommandLineFlag::HasValidatorFn() const {
-  absl::MutexLock l(InitFlagIfNecessary());
-
-  return this->validator != nullptr;
-}
-
-bool CommandLineFlag::SetValidatorFn(FlagValidator fn) {
-  absl::MutexLock l(InitFlagIfNecessary());
-
-  // ok to register the same function over and over again
-  if (fn == this->validator) return true;
-
-  // Can't set validator to a different function, unless reset first.
-  if (fn != nullptr && this->validator != nullptr) {
-    ABSL_INTERNAL_LOG(
-        WARNING, absl::StrCat("Ignoring SetValidatorFn() for flag '", Name(),
-                              "': validate-fn already registered"));
-
-    return false;
-  }
-
-  this->validator = fn;
-  return true;
-}
-
-bool CommandLineFlag::InvokeValidator(const void* value) const
-    EXCLUSIVE_LOCKS_REQUIRED(this->locks->primary_mu) {
-  if (!this->validator) {
-    return true;
-  }
-
-  (void)value;
-
-  ABSL_INTERNAL_LOG(
-      FATAL,
-      absl::StrCat("Flag '", Name(),
-                   "' of encapsulated type should not have a validator"));
-
-  return false;
-}
-
-void CommandLineFlag::SetCallback(
-    const flags_internal::FlagCallback mutation_callback) {
-  absl::MutexLock l(InitFlagIfNecessary());
-
-  callback = mutation_callback;
-
-  InvokeCallback();
-}
-
-// If the flag has a mutation callback this function invokes it. While the
-// 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 CommandLineFlag::InvokeCallback()
-    EXCLUSIVE_LOCKS_REQUIRED(this->locks->primary_mu) {
-  if (!this->callback) return;
-
-  // The callback lock is guaranteed initialized, because *locks->primary_mu
-  // exists.
-  absl::Mutex* callback_mu = &this->locks->callback_mu;
-
-  // When executing the callback we need the primary flag's mutex to be unlocked
-  // so that callback can retrieve the flag's value.
-  this->locks->primary_mu.Unlock();
-
-  {
-    absl::MutexLock lock(callback_mu);
-    this->callback();
-  }
-
-  this->locks->primary_mu.Lock();
-}
-
 // 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 d82443a7760e..235125762b27 100644
--- a/absl/flags/internal/commandlineflag.h
+++ b/absl/flags/internal/commandlineflag.h
@@ -71,13 +71,6 @@ using InitialValGenFunc = void* (*)();
 
 struct CommandLineFlagInfo;
 
-// 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 (*)();
-
-using FlagValidator = bool (*)();
-
 extern const char kStrippedFlagHelp[];
 
 // The per-type function
@@ -201,53 +194,55 @@ struct 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)
+      const flags_internal::InitialValGenFunc initial_value_gen, 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) {}
 
-  // Revert the init routine.
-  void Destroy() const;
+  // Virtual destructor
+  virtual void Destroy() const = 0;
 
   // Not copyable/assignable.
   CommandLineFlag(const CommandLineFlag&) = delete;
   CommandLineFlag& operator=(const CommandLineFlag&) = delete;
 
+  // Access methods.
+
+  // Returns true iff this object corresponds to retired flag
+  virtual bool IsRetired() const { return false; }
+  // Returns true iff this is a handle to an Abseil Flag.
+  virtual bool IsAbseilFlag() const { return true; }
+
   absl::string_view Name() const { return name; }
   std::string Help() const { return help.GetHelpText(); }
-  bool IsRetired() const { return this->retired; }
   bool IsModified() const;
   void SetModified(bool is_modified);
   bool IsSpecifiedOnCommandLine() const;
-  // 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;
 
-  bool HasValidatorFn() const;
-  bool SetValidatorFn(FlagValidator fn);
-  bool InvokeValidator(const void* value) const;
+  // 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
+  bool ValidateDefaultValue() const;
+  bool ValidateInputValue(absl::string_view value) const;
 
   // Return true iff flag has type T.
   template <typename T>
@@ -268,8 +263,8 @@ struct CommandLineFlag {
     return res;
   }
 
-  void SetCallback(const flags_internal::FlagCallback mutation_callback);
-  void InvokeCallback();
+  // Interfaces to overate on callbacks.
+  virtual void InvokeCallback() {}
 
   // 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`
@@ -286,29 +281,23 @@ struct CommandLineFlag {
   void StoreAtomic(size_t size);
 
   void CheckDefaultValueParsingRoundtrip() const;
-  // Invoke the flag validators for old flags.
-  // TODO(rogeeff): implement proper validators for Abseil Flags
-  bool ValidateDefaultValue() const;
-  bool ValidateInputValue(absl::string_view value) const;
 
   // Constant configuration for a particular flag.
- private:
+ protected:
+  ~CommandLineFlag() = default;
+
   const char* const name;
   const HelpText help;
   const char* const filename;
 
- protected:
   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.
-  FlagValidator 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
@@ -326,7 +315,7 @@ struct CommandLineFlag {
 
   // Ensure that the lazily initialized fields of *flag have been initialized,
   // and return the lock which should be locked when flag's state is mutated.
-  absl::Mutex* InitFlagIfNecessary() const;
+  absl::Mutex* InitFlagIfNecessary() const LOCK_RETURNED(locks->primary_mu);
 
   // copy construct new value of flag's type in a memory referenced by dst
   // based on current flag's value
@@ -342,6 +331,11 @@ struct 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 void SetValidator(void*) {}
 };
 
 // Update any copy of the flag value that is stored in an atomic word.
diff --git a/absl/flags/internal/flag.cc b/absl/flags/internal/flag.cc
new file mode 100644
index 000000000000..34629b304aa1
--- /dev/null
+++ b/absl/flags/internal/flag.cc
@@ -0,0 +1,51 @@
+//
+// 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.
+
+#include "absl/flags/internal/flag.h"
+
+#include "absl/synchronization/mutex.h"
+
+namespace absl {
+namespace flags_internal {
+
+// If the flag has a mutation callback this function invokes it. While the
+// 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 InvokeCallback(absl::Mutex* primary_mu, absl::Mutex* callback_mu,
+                    FlagCallback cb) EXCLUSIVE_LOCKS_REQUIRED(primary_mu) {
+  if (!cb) return;
+
+  // When executing the callback we need the primary flag's mutex to be
+  // unlocked so that callback can retrieve the flag's value.
+  primary_mu->Unlock();
+
+  {
+    absl::MutexLock lock(callback_mu);
+    cb();
+  }
+
+  primary_mu->Lock();
+}
+
+}  // namespace flags_internal
+}  // namespace absl
diff --git a/absl/flags/internal/flag.h b/absl/flags/internal/flag.h
index bc50de5486bc..fad36877f74e 100644
--- a/absl/flags/internal/flag.h
+++ b/absl/flags/internal/flag.h
@@ -22,20 +22,30 @@
 namespace absl {
 namespace flags_internal {
 
+// 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 (*)();
+
+void InvokeCallback(absl::Mutex* primary_mu, absl::Mutex* callback_mu,
+                    FlagCallback cb) EXCLUSIVE_LOCKS_REQUIRED(primary_mu);
+
 // This is "unspecified" implementation of absl::Flag<T> type.
 template <typename T>
-class Flag : public flags_internal::CommandLineFlag {
+class Flag final : public flags_internal::CommandLineFlag {
  public:
-  constexpr Flag(const char* name, const flags_internal::HelpGenFunc help_gen,
-                 const char* filename,
+  constexpr Flag(const char* name_arg,
+                 const flags_internal::HelpGenFunc help_gen,
+                 const char* filename_arg,
                  const flags_internal::FlagMarshallingOpFn marshalling_op_arg,
                  const flags_internal::InitialValGenFunc initial_value_gen)
       : flags_internal::CommandLineFlag(
-            name, flags_internal::HelpText::FromFunctionPointer(help_gen),
-            filename, &flags_internal::FlagOps<T>, marshalling_op_arg,
+            name_arg, flags_internal::HelpText::FromFunctionPointer(help_gen),
+            filename_arg, &flags_internal::FlagOps<T>, marshalling_op_arg,
             initial_value_gen,
-            /*retired_arg=*/false, /*def_arg=*/nullptr,
-            /*cur_arg=*/nullptr) {}
+            /*def_arg=*/nullptr,
+            /*cur_arg=*/nullptr),
+        callback_(nullptr) {}
 
   T Get() const {
     // Implementation notes:
@@ -76,6 +86,31 @@ class Flag : public flags_internal::CommandLineFlag {
   }
 
   void Set(const T& v) { this->Write(&v, &flags_internal::FlagOps<T>); }
+
+  void SetCallback(const flags_internal::FlagCallback mutation_callback) {
+    absl::MutexLock l(InitFlagIfNecessary());
+
+    callback_ = mutation_callback;
+
+    InvokeCallback();
+  }
+  void InvokeCallback() override
+      EXCLUSIVE_LOCKS_REQUIRED(this->locks->primary_mu) {
+    flags_internal::InvokeCallback(&this->locks->primary_mu,
+                                   &this->locks->callback_mu, callback_);
+  }
+
+ private:
+  void Destroy() const override {
+    // Values are heap allocated Abseil Flags.
+    if (this->cur) Delete(this->op, this->cur);
+    if (this->def) Delete(this->op, this->def);
+
+    delete this->locks;
+  }
+
+  // Flag's data
+  FlagCallback callback_;  // Mutation callback
 };
 
 // This class facilitates Flag object registration and tail expression-based
diff --git a/absl/flags/internal/registry.cc b/absl/flags/internal/registry.cc
index 4e5d1106b93c..0d4eec982ba7 100644
--- a/absl/flags/internal/registry.cc
+++ b/absl/flags/internal/registry.cc
@@ -31,18 +31,6 @@
 
 namespace absl {
 namespace flags_internal {
-namespace {
-
-void DestroyFlag(CommandLineFlag* flag) NO_THREAD_SAFETY_ANALYSIS {
-  flag->Destroy();
-
-  // CommandLineFlag handle object is heap allocated for non Abseil Flags.
-  if (!flag->IsAbseilFlag()) {
-    delete flag;
-  }
-}
-
-}  // namespace
 
 // --------------------------------------------------------------------
 // FlagRegistry
@@ -59,7 +47,7 @@ class FlagRegistry {
   FlagRegistry() = default;
   ~FlagRegistry() {
     for (auto& p : flags_) {
-      DestroyFlag(p.second);
+      p.second->Destroy();
     }
   }
 
@@ -141,7 +129,7 @@ void FlagRegistry::RegisterFlag(CommandLineFlag* flag) {
           true);
     } else if (old_flag->IsRetired()) {
       // Retired definitions are idempotent. Just keep the old one.
-      DestroyFlag(flag);
+      flag->Destroy();
       return;
     } else if (old_flag->Filename() != flag->Filename()) {
       flags_internal::ReportUsageError(
@@ -224,7 +212,7 @@ class FlagSaverImpl {
       saved.marshalling_op = flag->marshalling_op;
       {
         absl::MutexLock l(flag->InitFlagIfNecessary());
-        saved.validator = flag->validator;
+        saved.validator = flag->GetValidator();
         saved.modified = flag->modified;
         saved.on_command_line = flag->on_command_line;
         saved.current = Clone(saved.op, flag->cur);
@@ -250,7 +238,7 @@ class FlagSaverImpl {
       bool restored = false;
       {
         absl::MutexLock l(flag->InitFlagIfNecessary());
-        flag->validator = src.validator;
+        flag->SetValidator(src.validator);
         flag->modified = src.modified;
         flag->on_command_line = src.on_command_line;
         if (flag->counter != src.counter ||
@@ -290,9 +278,9 @@ class FlagSaverImpl {
     FlagOpFn op;
     FlagMarshallingOpFn marshalling_op;
     int64_t counter;
+    void* validator;
     bool modified;
     bool on_command_line;
-    bool (*validator)();
     const void* current;        // nullptr after restore
     const void* default_value;  // nullptr after restore
   };
@@ -414,14 +402,38 @@ bool RegisterCommandLineFlag(CommandLineFlag* flag) {
 
 // --------------------------------------------------------------------
 
-bool Retire(FlagOpFn ops, FlagMarshallingOpFn marshalling_ops,
-            const char* name) {
-  auto* flag = new CommandLineFlag(
-      name,
-      /*help_text=*/absl::flags_internal::HelpText::FromStaticCString(nullptr),
-      /*filename_arg=*/"RETIRED", ops, marshalling_ops,
-      /*initial_value_gen=*/nullptr,
-      /*retired_arg=*/true, nullptr, nullptr);
+namespace {
+
+class RetiredFlagObj final : public flags_internal::CommandLineFlag {
+ public:
+  constexpr RetiredFlagObj(const char* name_arg, FlagOpFn ops,
+                           FlagMarshallingOpFn marshalling_ops)
+      : flags_internal::CommandLineFlag(
+            name_arg, flags_internal::HelpText::FromStaticCString(nullptr),
+            /*filename_arg=*/"RETIRED", ops, marshalling_ops,
+            /*initial_value_gen=*/nullptr,
+            /*def_arg=*/nullptr,
+            /*cur_arg=*/nullptr) {}
+
+ private:
+  bool IsRetired() const override { return true; }
+
+  void Destroy() const override {
+    // Values are heap allocated for Retired Flags.
+    if (this->cur) Delete(this->op, this->cur);
+    if (this->def) Delete(this->op, this->def);
+
+    if (this->locks) delete this->locks;
+
+    delete this;
+  }
+};
+
+}  // namespace
+
+bool Retire(const char* name, FlagOpFn ops,
+            FlagMarshallingOpFn marshalling_ops) {
+  auto* flag = new flags_internal::RetiredFlagObj(name, ops, marshalling_ops);
   FlagRegistry::GlobalRegistry()->RegisterFlag(flag);
   return true;
 }
diff --git a/absl/flags/internal/registry.h b/absl/flags/internal/registry.h
index 27566fbf4ab7..884a8db542fa 100644
--- a/absl/flags/internal/registry.h
+++ b/absl/flags/internal/registry.h
@@ -106,29 +106,16 @@ bool RegisterCommandLineFlag(CommandLineFlag*);
 //   4: Remove the old_lib 'retired' registration.
 //   5: Eventually delete the graveyard registration entirely.
 //
-// Returns bool to enable use in namespace-scope initializers.
-// For example:
-//
-//   static const bool dummy = base::RetiredFlag<int32_t>("myflag");
-//
-// Or to declare several at once:
-//
-//   static bool dummies[] = {
-//       base::RetiredFlag<std::string>("some_string_flag"),
-//       base::RetiredFlag<double>("some_double_flag"),
-//       base::RetiredFlag<int32_t>("some_int32_flag")
-//   };
 
 // Retire flag with name "name" and type indicated by ops.
-bool Retire(FlagOpFn ops, FlagMarshallingOpFn marshalling_ops,
-            const char* name);
+bool Retire(const char* name, FlagOpFn ops,
+            FlagMarshallingOpFn marshalling_ops);
 
 // Registered a retired flag with name 'flag_name' and type 'T'.
 template <typename T>
 inline bool RetiredFlag(const char* flag_name) {
-  return flags_internal::Retire(flags_internal::FlagOps<T>,
-                                flags_internal::FlagMarshallingOps<T>,
-                                flag_name);
+  return flags_internal::Retire(flag_name, flags_internal::FlagOps<T>,
+                                flags_internal::FlagMarshallingOps<T>);
 }
 
 // If the flag is retired, returns true and indicates in |*type_is_bool|
diff --git a/absl/flags/internal/usage_test.cc b/absl/flags/internal/usage_test.cc
index 781e1d2b16ab..8538b2b60884 100644
--- a/absl/flags/internal/usage_test.cc
+++ b/absl/flags/internal/usage_test.cc
@@ -395,7 +395,7 @@ TEST_F(UsageReportingTest, TestUsageFlag_helpon) {
 }  // namespace
 
 int main(int argc, char* argv[]) {
-  absl::GetFlag(FLAGS_undefok);  // Force linking of parse.cc
+  (void)absl::GetFlag(FLAGS_undefok);  // Force linking of parse.cc
   flags::SetProgramInvocationName("usage_test");
   absl::SetProgramUsageMessage(kTestUsageMessage);
   ::testing::InitGoogleTest(&argc, argv);