registry.cc
Go to the documentation of this file.
00001 //
00002 // Copyright 2019 The Abseil Authors.
00003 //
00004 // Licensed under the Apache License, Version 2.0 (the "License");
00005 // you may not use this file except in compliance with the License.
00006 // You may obtain a copy of the License at
00007 //
00008 //      https://www.apache.org/licenses/LICENSE-2.0
00009 //
00010 // Unless required by applicable law or agreed to in writing, software
00011 // distributed under the License is distributed on an "AS IS" BASIS,
00012 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013 // See the License for the specific language governing permissions and
00014 // limitations under the License.
00015 
00016 #include "absl/flags/internal/registry.h"
00017 
00018 #include "absl/base/dynamic_annotations.h"
00019 #include "absl/base/internal/raw_logging.h"
00020 #include "absl/flags/config.h"
00021 #include "absl/flags/usage_config.h"
00022 #include "absl/strings/str_cat.h"
00023 #include "absl/strings/string_view.h"
00024 #include "absl/synchronization/mutex.h"
00025 
00026 // --------------------------------------------------------------------
00027 // FlagRegistry implementation
00028 //    A FlagRegistry holds all flag objects indexed
00029 //    by their names so that if you know a flag's name you can access or
00030 //    set it.
00031 
00032 namespace absl {
00033 namespace flags_internal {
00034 namespace {
00035 
00036 void DestroyFlag(CommandLineFlag* flag) NO_THREAD_SAFETY_ANALYSIS {
00037   // Values are heap allocated for retired and Abseil Flags.
00038   if (flag->IsRetired() || flag->IsAbseilFlag()) {
00039     if (flag->cur) Delete(flag->op, flag->cur);
00040     if (flag->def) Delete(flag->op, flag->def);
00041   }
00042 
00043   delete flag->locks;
00044 
00045   // CommandLineFlag handle object is heap allocated for non Abseil Flags.
00046   if (!flag->IsAbseilFlag()) {
00047     delete flag;
00048   }
00049 }
00050 
00051 // --------------------------------------------------------------------
00052 // FlagRegistry
00053 //    A FlagRegistry singleton object holds all flag objects indexed
00054 //    by their names so that if you know a flag's name (as a C
00055 //    string), you can access or set it.  If the function is named
00056 //    FooLocked(), you must own the registry lock before calling
00057 //    the function; otherwise, you should *not* hold the lock, and
00058 //    the function will acquire it itself if needed.
00059 // --------------------------------------------------------------------
00060 
00061 // A map from flag pointer to CommandLineFlag*. Used when registering
00062 // validators.
00063 class FlagPtrMap {
00064  public:
00065   void Register(CommandLineFlag* flag) {
00066     auto& vec = buckets_[BucketForFlag(flag->cur)];
00067     if (vec.size() == vec.capacity()) {
00068       // Bypass default 2x growth factor with 1.25 so we have fuller vectors.
00069       // This saves 4% memory compared to default growth.
00070       vec.reserve(vec.size() * 1.25 + 0.5);
00071     }
00072     vec.push_back(flag);
00073   }
00074 
00075   CommandLineFlag* FindByPtr(const void* flag_ptr) {
00076     const auto& flag_vector = buckets_[BucketForFlag(flag_ptr)];
00077     for (CommandLineFlag* entry : flag_vector) {
00078       if (entry->cur == flag_ptr) {
00079         return entry;
00080       }
00081     }
00082     return nullptr;
00083   }
00084 
00085  private:
00086   // Instead of std::map, we use a custom hash table where each bucket stores
00087   // flags in a vector. This reduces memory usage 40% of the memory that would
00088   // have been used by std::map.
00089   //
00090   // kNumBuckets was picked as a large enough prime. As of writing this code, a
00091   // typical large binary has ~8k (old-style) flags, and this would gives
00092   // buckets with roughly 50 elements each.
00093   //
00094   // Note that reads to this hash table are rare: exactly as many as we have
00095   // flags with validators. As of writing, a typical binary only registers 52
00096   // validated flags.
00097   static constexpr size_t kNumBuckets = 163;
00098   std::vector<CommandLineFlag*> buckets_[kNumBuckets];
00099 
00100   static int BucketForFlag(const void* ptr) {
00101     // Modulo a prime is good enough here. On a real program, bucket size stddev
00102     // after registering 8k flags is ~5 (mean size at 51).
00103     return reinterpret_cast<uintptr_t>(ptr) % kNumBuckets;
00104   }
00105 };
00106 constexpr size_t FlagPtrMap::kNumBuckets;
00107 
00108 }  // namespace
00109 
00110 class FlagRegistry {
00111  public:
00112   FlagRegistry() = default;
00113   ~FlagRegistry() {
00114     for (auto& p : flags_) {
00115       DestroyFlag(p.second);
00116     }
00117   }
00118 
00119   // Store a flag in this registry.  Takes ownership of *flag.
00120   // If ptr is non-null, the flag can later be found by calling
00121   // FindFlagViaPtrLocked(ptr).
00122   void RegisterFlag(CommandLineFlag* flag, const void* ptr);
00123 
00124   void Lock() EXCLUSIVE_LOCK_FUNCTION(lock_) { lock_.Lock(); }
00125   void Unlock() UNLOCK_FUNCTION(lock_) { lock_.Unlock(); }
00126 
00127   // Returns the flag object for the specified name, or nullptr if not found.
00128   // Will emit a warning if a 'retired' flag is specified.
00129   CommandLineFlag* FindFlagLocked(absl::string_view name);
00130 
00131   // Returns the retired flag object for the specified name, or nullptr if not
00132   // found or not retired.  Does not emit a warning.
00133   CommandLineFlag* FindRetiredFlagLocked(absl::string_view name);
00134 
00135   // Returns the flag object whose current-value is stored at flag_ptr.
00136   CommandLineFlag* FindFlagViaPtrLocked(const void* flag_ptr);
00137 
00138   static FlagRegistry* GlobalRegistry();  // returns a singleton registry
00139 
00140  private:
00141   friend class FlagSaverImpl;  // reads all the flags in order to copy them
00142   friend void ForEachFlagUnlocked(
00143       std::function<void(CommandLineFlag*)> visitor);
00144 
00145   // The map from name to flag, for FindFlagLocked().
00146   using FlagMap = std::map<absl::string_view, CommandLineFlag*>;
00147   using FlagIterator = FlagMap::iterator;
00148   using FlagConstIterator = FlagMap::const_iterator;
00149   FlagMap flags_;
00150 
00151   FlagPtrMap flag_ptr_map_;
00152 
00153   absl::Mutex lock_;
00154 
00155   // Disallow
00156   FlagRegistry(const FlagRegistry&);
00157   FlagRegistry& operator=(const FlagRegistry&);
00158 };
00159 
00160 FlagRegistry* FlagRegistry::GlobalRegistry() {
00161   static FlagRegistry* global_registry = new FlagRegistry;
00162   return global_registry;
00163 }
00164 
00165 namespace {
00166 
00167 class FlagRegistryLock {
00168  public:
00169   explicit FlagRegistryLock(FlagRegistry* fr) : fr_(fr) { fr_->Lock(); }
00170   ~FlagRegistryLock() { fr_->Unlock(); }
00171 
00172  private:
00173   FlagRegistry* const fr_;
00174 };
00175 
00176 }  // namespace
00177 
00178 void FlagRegistry::RegisterFlag(CommandLineFlag* flag, const void* ptr) {
00179   FlagRegistryLock registry_lock(this);
00180   std::pair<FlagIterator, bool> ins =
00181       flags_.insert(FlagMap::value_type(flag->Name(), flag));
00182   if (ins.second == false) {  // means the name was already in the map
00183     CommandLineFlag* old_flag = ins.first->second;
00184     if (flag->IsRetired() != old_flag->IsRetired()) {
00185       // All registrations must agree on the 'retired' flag.
00186       flags_internal::ReportUsageError(
00187           absl::StrCat(
00188               "Retired flag '", flag->Name(),
00189               "' was defined normally in file '",
00190               (flag->IsRetired() ? old_flag->Filename() : flag->Filename()),
00191               "'."),
00192           true);
00193     } else if (flag->op != old_flag->op) {
00194       flags_internal::ReportUsageError(
00195           absl::StrCat("Flag '", flag->Name(),
00196                        "' was defined more than once but with "
00197                        "differing types. Defined in files '",
00198                        old_flag->Filename(), "' and '", flag->Filename(),
00199                        "' with types '", old_flag->Typename(), "' and '",
00200                        flag->Typename(), "', respectively."),
00201           true);
00202     } else if (old_flag->IsRetired()) {
00203       // Retired definitions are idempotent. Just keep the old one.
00204       DestroyFlag(flag);
00205       return;
00206     } else if (old_flag->Filename() != flag->Filename()) {
00207       flags_internal::ReportUsageError(
00208           absl::StrCat("Flag '", flag->Name(),
00209                        "' was defined more than once (in files '",
00210                        old_flag->Filename(), "' and '", flag->Filename(),
00211                        "')."),
00212           true);
00213     } else {
00214       flags_internal::ReportUsageError(
00215           absl::StrCat(
00216               "Something wrong with flag '", flag->Name(), "' in file '",
00217               flag->Filename(), "'. One possibility: file '", flag->Filename(),
00218               "' is being linked both statically and dynamically into this "
00219               "executable. e.g. some files listed as srcs to a test and also "
00220               "listed as srcs of some shared lib deps of the same test."),
00221           true);
00222     }
00223     // All cases above are fatal, except for the retired flags.
00224     std::exit(1);
00225   }
00226 
00227   if (ptr != nullptr) {
00228     // This must be the first time we're seeing this flag.
00229     flag_ptr_map_.Register(flag);
00230   }
00231 }
00232 
00233 CommandLineFlag* FlagRegistry::FindFlagLocked(absl::string_view name) {
00234   FlagConstIterator i = flags_.find(name);
00235   if (i == flags_.end()) {
00236     return nullptr;
00237   }
00238 
00239   if (i->second->IsRetired()) {
00240     flags_internal::ReportUsageError(
00241         absl::StrCat("Accessing retired flag '", name, "'"), false);
00242   }
00243 
00244   return i->second;
00245 }
00246 
00247 CommandLineFlag* FlagRegistry::FindRetiredFlagLocked(absl::string_view name) {
00248   FlagConstIterator i = flags_.find(name);
00249   if (i == flags_.end() || !i->second->IsRetired()) {
00250     return nullptr;
00251   }
00252 
00253   return i->second;
00254 }
00255 
00256 CommandLineFlag* FlagRegistry::FindFlagViaPtrLocked(const void* flag_ptr) {
00257   return flag_ptr_map_.FindByPtr(flag_ptr);
00258 }
00259 
00260 // --------------------------------------------------------------------
00261 // FlagSaver
00262 // FlagSaverImpl
00263 //    This class stores the states of all flags at construct time,
00264 //    and restores all flags to that state at destruct time.
00265 //    Its major implementation challenge is that it never modifies
00266 //    pointers in the 'main' registry, so global FLAG_* vars always
00267 //    point to the right place.
00268 // --------------------------------------------------------------------
00269 
00270 class FlagSaverImpl {
00271  public:
00272   // Constructs an empty FlagSaverImpl object.
00273   FlagSaverImpl() {}
00274   ~FlagSaverImpl() {
00275     // reclaim memory from each of our CommandLineFlags
00276     for (const SavedFlag& src : backup_registry_) {
00277       Delete(src.op, src.current);
00278       Delete(src.op, src.default_value);
00279     }
00280   }
00281 
00282   // Saves the flag states from the flag registry into this object.
00283   // It's an error to call this more than once.
00284   // Must be called when the registry mutex is not held.
00285   void SaveFromRegistry() {
00286     assert(backup_registry_.empty());  // call only once!
00287     SavedFlag saved;
00288     flags_internal::ForEachFlag([&](flags_internal::CommandLineFlag* flag) {
00289       if (flag->IsRetired()) return;
00290 
00291       saved.name = flag->Name();
00292       saved.op = flag->op;
00293       saved.marshalling_op = flag->marshalling_op;
00294       {
00295         absl::MutexLock l(InitFlagIfNecessary(flag));
00296         saved.validator = flag->validator;
00297         saved.modified = flag->modified;
00298         saved.on_command_line = flag->IsSpecifiedOnCommandLine();
00299         saved.current = Clone(saved.op, flag->cur);
00300         saved.default_value = Clone(saved.op, flag->def);
00301         saved.counter = flag->counter;
00302       }
00303       backup_registry_.push_back(saved);
00304     });
00305   }
00306 
00307   // Restores the saved flag states into the flag registry.  We
00308   // assume no flags were added or deleted from the registry since
00309   // the SaveFromRegistry; if they were, that's trouble!  Must be
00310   // called when the registry mutex is not held.
00311   void RestoreToRegistry() {
00312     FlagRegistry* const global_registry = FlagRegistry::GlobalRegistry();
00313     FlagRegistryLock frl(global_registry);
00314     for (const SavedFlag& src : backup_registry_) {
00315       CommandLineFlag* flag = global_registry->FindFlagLocked(src.name);
00316       // If null, flag got deleted from registry.
00317       if (!flag) continue;
00318 
00319       bool restored = false;
00320       {
00321         absl::Mutex* mu = InitFlagIfNecessary(flag);
00322         absl::MutexLock l(mu);
00323         flag->validator = src.validator;
00324         flag->modified = src.modified;
00325         flag->on_command_line = src.on_command_line;
00326         if (flag->counter != src.counter ||
00327             ChangedDirectly(flag, src.default_value, flag->def)) {
00328           flag->counter++;
00329           Copy(src.op, src.default_value, flag->def);
00330         }
00331         if (flag->counter != src.counter ||
00332             ChangedDirectly(flag, src.current, flag->cur)) {
00333           restored = true;
00334           flag->counter++;
00335           Copy(src.op, src.current, flag->cur);
00336           UpdateCopy(flag, mu);
00337 
00338           // Revalidate the flag because the validator might store state based
00339           // on the flag's value, which just changed due to the restore.
00340           // Failing validation is ignored because it's assumed that the flag
00341           // was valid previously and there's little that can be done about it
00342           // here, anyway.
00343           Validate(flag, flag->cur);
00344         }
00345       }
00346 
00347       // Log statements must be done when no flag lock is held.
00348       if (restored) {
00349         ABSL_INTERNAL_LOG(
00350             INFO, absl::StrCat("Restore saved value of ", flag->Name(), ": ",
00351                                Unparse(src.marshalling_op, src.current)));
00352       }
00353     }
00354   }
00355 
00356  private:
00357   struct SavedFlag {
00358     absl::string_view name;
00359     FlagOpFn op;
00360     FlagMarshallingOpFn marshalling_op;
00361     int64_t counter;
00362     bool modified;
00363     bool on_command_line;
00364     bool (*validator)();
00365     const void* current;        // nullptr after restore
00366     const void* default_value;  // nullptr after restore
00367   };
00368 
00369   std::vector<SavedFlag> backup_registry_;
00370 
00371   FlagSaverImpl(const FlagSaverImpl&);  // no copying!
00372   void operator=(const FlagSaverImpl&);
00373 };
00374 
00375 FlagSaver::FlagSaver() : impl_(new FlagSaverImpl()) {
00376   impl_->SaveFromRegistry();
00377 }
00378 
00379 void FlagSaver::Ignore() {
00380   delete impl_;
00381   impl_ = nullptr;
00382 }
00383 
00384 FlagSaver::~FlagSaver() {
00385   if (!impl_) return;
00386 
00387   impl_->RestoreToRegistry();
00388   delete impl_;
00389 }
00390 
00391 // --------------------------------------------------------------------
00392 // GetAllFlags()
00393 //    The main way the FlagRegistry class exposes its data.  This
00394 //    returns, as strings, all the info about all the flags in
00395 //    the main registry, sorted first by filename they are defined
00396 //    in, and then by flagname.
00397 // --------------------------------------------------------------------
00398 
00399 struct FilenameFlagnameLess {
00400   bool operator()(const CommandLineFlagInfo& a,
00401                   const CommandLineFlagInfo& b) const {
00402     int cmp = absl::string_view(a.filename).compare(b.filename);
00403     if (cmp != 0) return cmp < 0;
00404     return a.name < b.name;
00405   }
00406 };
00407 
00408 void FillCommandLineFlagInfo(CommandLineFlag* flag,
00409                              CommandLineFlagInfo* result) {
00410   result->name = std::string(flag->Name());
00411   result->type = std::string(flag->Typename());
00412   result->description = flag->Help();
00413   result->filename = flag->Filename();
00414 
00415   UpdateModifiedBit(flag);
00416 
00417   absl::MutexLock l(InitFlagIfNecessary(flag));
00418   result->current_value = flag->CurrentValue();
00419   result->default_value = flag->DefaultValue();
00420   result->is_default = !flag->modified;
00421   result->has_validator_fn = (flag->validator != nullptr);
00422   result->flag_ptr = flag->IsAbseilFlag() ? nullptr : flag->cur;
00423 }
00424 
00425 // --------------------------------------------------------------------
00426 
00427 CommandLineFlag* FindCommandLineFlag(absl::string_view name) {
00428   if (name.empty()) return nullptr;
00429   FlagRegistry* const registry = FlagRegistry::GlobalRegistry();
00430   FlagRegistryLock frl(registry);
00431 
00432   return registry->FindFlagLocked(name);
00433 }
00434 
00435 CommandLineFlag* FindCommandLineV1Flag(const void* flag_ptr) {
00436   FlagRegistry* const registry = FlagRegistry::GlobalRegistry();
00437   FlagRegistryLock frl(registry);
00438 
00439   return registry->FindFlagViaPtrLocked(flag_ptr);
00440 }
00441 
00442 CommandLineFlag* FindRetiredFlag(absl::string_view name) {
00443   FlagRegistry* const registry = FlagRegistry::GlobalRegistry();
00444   FlagRegistryLock frl(registry);
00445 
00446   return registry->FindRetiredFlagLocked(name);
00447 }
00448 
00449 // --------------------------------------------------------------------
00450 
00451 void ForEachFlagUnlocked(std::function<void(CommandLineFlag*)> visitor) {
00452   FlagRegistry* const registry = FlagRegistry::GlobalRegistry();
00453   for (FlagRegistry::FlagConstIterator i = registry->flags_.begin();
00454        i != registry->flags_.end(); ++i) {
00455     visitor(i->second);
00456   }
00457 }
00458 
00459 void ForEachFlag(std::function<void(CommandLineFlag*)> visitor) {
00460   FlagRegistry* const registry = FlagRegistry::GlobalRegistry();
00461   FlagRegistryLock frl(registry);
00462   ForEachFlagUnlocked(visitor);
00463 }
00464 
00465 // --------------------------------------------------------------------
00466 
00467 void GetAllFlags(std::vector<CommandLineFlagInfo>* OUTPUT) {
00468   flags_internal::ForEachFlag([&](CommandLineFlag* flag) {
00469     if (flag->IsRetired()) return;
00470 
00471     CommandLineFlagInfo fi;
00472     FillCommandLineFlagInfo(flag, &fi);
00473     OUTPUT->push_back(fi);
00474   });
00475 
00476   // Now sort the flags, first by filename they occur in, then alphabetically
00477   std::sort(OUTPUT->begin(), OUTPUT->end(), FilenameFlagnameLess());
00478 }
00479 
00480 // --------------------------------------------------------------------
00481 
00482 bool RegisterCommandLineFlag(CommandLineFlag* flag, const void* ptr) {
00483   FlagRegistry::GlobalRegistry()->RegisterFlag(flag, ptr);
00484   return true;
00485 }
00486 
00487 // --------------------------------------------------------------------
00488 
00489 bool Retire(FlagOpFn ops, FlagMarshallingOpFn marshalling_ops,
00490             const char* name) {
00491   auto* flag = new CommandLineFlag(
00492       name,
00493       /*help_text=*/absl::flags_internal::HelpText::FromStaticCString(nullptr),
00494       /*filename_arg=*/"RETIRED", ops, marshalling_ops,
00495       /*initial_value_gen=*/nullptr,
00496       /*retired_arg=*/true, nullptr, nullptr);
00497   FlagRegistry::GlobalRegistry()->RegisterFlag(flag, nullptr);
00498   return true;
00499 }
00500 
00501 // --------------------------------------------------------------------
00502 
00503 bool IsRetiredFlag(absl::string_view name, bool* type_is_bool) {
00504   assert(!name.empty());
00505   CommandLineFlag* flag = flags_internal::FindRetiredFlag(name);
00506   if (flag == nullptr) {
00507     return false;
00508   }
00509   assert(type_is_bool);
00510   *type_is_bool = flag->IsOfType<bool>();
00511   return true;
00512 }
00513 
00514 }  // namespace flags_internal
00515 }  // namespace absl


abseil_cpp
Author(s):
autogenerated on Wed Jun 19 2019 19:42:15