registry.cc
Go to the documentation of this file.
1 //
2 // Copyright 2019 The Abseil Authors.
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // https://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 
17 
20 #include "absl/flags/config.h"
22 #include "absl/strings/str_cat.h"
25 
26 // --------------------------------------------------------------------
27 // FlagRegistry implementation
28 // A FlagRegistry holds all flag objects indexed
29 // by their names so that if you know a flag's name you can access or
30 // set it.
31 
32 namespace absl {
33 namespace flags_internal {
34 namespace {
35 
36 void DestroyFlag(CommandLineFlag* flag) NO_THREAD_SAFETY_ANALYSIS {
37  // Values are heap allocated for retired and Abseil Flags.
38  if (flag->IsRetired() || flag->IsAbseilFlag()) {
39  if (flag->cur) Delete(flag->op, flag->cur);
40  if (flag->def) Delete(flag->op, flag->def);
41  }
42 
43  delete flag->locks;
44 
45  // CommandLineFlag handle object is heap allocated for non Abseil Flags.
46  if (!flag->IsAbseilFlag()) {
47  delete flag;
48  }
49 }
50 
51 // --------------------------------------------------------------------
52 // FlagRegistry
53 // A FlagRegistry singleton object holds all flag objects indexed
54 // by their names so that if you know a flag's name (as a C
55 // string), you can access or set it. If the function is named
56 // FooLocked(), you must own the registry lock before calling
57 // the function; otherwise, you should *not* hold the lock, and
58 // the function will acquire it itself if needed.
59 // --------------------------------------------------------------------
60 
61 // A map from flag pointer to CommandLineFlag*. Used when registering
62 // validators.
63 class FlagPtrMap {
64  public:
65  void Register(CommandLineFlag* flag) {
66  auto& vec = buckets_[BucketForFlag(flag->cur)];
67  if (vec.size() == vec.capacity()) {
68  // Bypass default 2x growth factor with 1.25 so we have fuller vectors.
69  // This saves 4% memory compared to default growth.
70  vec.reserve(vec.size() * 1.25 + 0.5);
71  }
72  vec.push_back(flag);
73  }
74 
75  CommandLineFlag* FindByPtr(const void* flag_ptr) {
76  const auto& flag_vector = buckets_[BucketForFlag(flag_ptr)];
77  for (CommandLineFlag* entry : flag_vector) {
78  if (entry->cur == flag_ptr) {
79  return entry;
80  }
81  }
82  return nullptr;
83  }
84 
85  private:
86  // Instead of std::map, we use a custom hash table where each bucket stores
87  // flags in a vector. This reduces memory usage 40% of the memory that would
88  // have been used by std::map.
89  //
90  // kNumBuckets was picked as a large enough prime. As of writing this code, a
91  // typical large binary has ~8k (old-style) flags, and this would gives
92  // buckets with roughly 50 elements each.
93  //
94  // Note that reads to this hash table are rare: exactly as many as we have
95  // flags with validators. As of writing, a typical binary only registers 52
96  // validated flags.
97  static constexpr size_t kNumBuckets = 163;
98  std::vector<CommandLineFlag*> buckets_[kNumBuckets];
99 
100  static int BucketForFlag(const void* ptr) {
101  // Modulo a prime is good enough here. On a real program, bucket size stddev
102  // after registering 8k flags is ~5 (mean size at 51).
103  return reinterpret_cast<uintptr_t>(ptr) % kNumBuckets;
104  }
105 };
106 constexpr size_t FlagPtrMap::kNumBuckets;
107 
108 } // namespace
109 
111  public:
112  FlagRegistry() = default;
114  for (auto& p : flags_) {
115  DestroyFlag(p.second);
116  }
117  }
118 
119  // Store a flag in this registry. Takes ownership of *flag.
120  // If ptr is non-null, the flag can later be found by calling
121  // FindFlagViaPtrLocked(ptr).
122  void RegisterFlag(CommandLineFlag* flag, const void* ptr);
123 
124  void Lock() EXCLUSIVE_LOCK_FUNCTION(lock_) { lock_.Lock(); }
125  void Unlock() UNLOCK_FUNCTION(lock_) { lock_.Unlock(); }
126 
127  // Returns the flag object for the specified name, or nullptr if not found.
128  // Will emit a warning if a 'retired' flag is specified.
129  CommandLineFlag* FindFlagLocked(absl::string_view name);
130 
131  // Returns the retired flag object for the specified name, or nullptr if not
132  // found or not retired. Does not emit a warning.
133  CommandLineFlag* FindRetiredFlagLocked(absl::string_view name);
134 
135  // Returns the flag object whose current-value is stored at flag_ptr.
136  CommandLineFlag* FindFlagViaPtrLocked(const void* flag_ptr);
137 
138  static FlagRegistry* GlobalRegistry(); // returns a singleton registry
139 
140  private:
141  friend class FlagSaverImpl; // reads all the flags in order to copy them
142  friend void ForEachFlagUnlocked(
143  std::function<void(CommandLineFlag*)> visitor);
144 
145  // The map from name to flag, for FindFlagLocked().
146  using FlagMap = std::map<absl::string_view, CommandLineFlag*>;
147  using FlagIterator = FlagMap::iterator;
148  using FlagConstIterator = FlagMap::const_iterator;
150 
151  FlagPtrMap flag_ptr_map_;
152 
154 
155  // Disallow
156  FlagRegistry(const FlagRegistry&);
157  FlagRegistry& operator=(const FlagRegistry&);
158 };
159 
161  static FlagRegistry* global_registry = new FlagRegistry;
162  return global_registry;
163 }
164 
165 namespace {
166 
167 class FlagRegistryLock {
168  public:
169  explicit FlagRegistryLock(FlagRegistry* fr) : fr_(fr) { fr_->Lock(); }
170  ~FlagRegistryLock() { fr_->Unlock(); }
171 
172  private:
173  FlagRegistry* const fr_;
174 };
175 
176 } // namespace
177 
178 void FlagRegistry::RegisterFlag(CommandLineFlag* flag, const void* ptr) {
179  FlagRegistryLock registry_lock(this);
180  std::pair<FlagIterator, bool> ins =
181  flags_.insert(FlagMap::value_type(flag->Name(), flag));
182  if (ins.second == false) { // means the name was already in the map
183  CommandLineFlag* old_flag = ins.first->second;
184  if (flag->IsRetired() != old_flag->IsRetired()) {
185  // All registrations must agree on the 'retired' flag.
187  absl::StrCat(
188  "Retired flag '", flag->Name(),
189  "' was defined normally in file '",
190  (flag->IsRetired() ? old_flag->Filename() : flag->Filename()),
191  "'."),
192  true);
193  } else if (flag->op != old_flag->op) {
195  absl::StrCat("Flag '", flag->Name(),
196  "' was defined more than once but with "
197  "differing types. Defined in files '",
198  old_flag->Filename(), "' and '", flag->Filename(),
199  "' with types '", old_flag->Typename(), "' and '",
200  flag->Typename(), "', respectively."),
201  true);
202  } else if (old_flag->IsRetired()) {
203  // Retired definitions are idempotent. Just keep the old one.
204  DestroyFlag(flag);
205  return;
206  } else if (old_flag->Filename() != flag->Filename()) {
208  absl::StrCat("Flag '", flag->Name(),
209  "' was defined more than once (in files '",
210  old_flag->Filename(), "' and '", flag->Filename(),
211  "')."),
212  true);
213  } else {
215  absl::StrCat(
216  "Something wrong with flag '", flag->Name(), "' in file '",
217  flag->Filename(), "'. One possibility: file '", flag->Filename(),
218  "' is being linked both statically and dynamically into this "
219  "executable. e.g. some files listed as srcs to a test and also "
220  "listed as srcs of some shared lib deps of the same test."),
221  true);
222  }
223  // All cases above are fatal, except for the retired flags.
224  std::exit(1);
225  }
226 
227  if (ptr != nullptr) {
228  // This must be the first time we're seeing this flag.
229  flag_ptr_map_.Register(flag);
230  }
231 }
232 
234  FlagConstIterator i = flags_.find(name);
235  if (i == flags_.end()) {
236  return nullptr;
237  }
238 
239  if (i->second->IsRetired()) {
241  absl::StrCat("Accessing retired flag '", name, "'"), false);
242  }
243 
244  return i->second;
245 }
246 
248  FlagConstIterator i = flags_.find(name);
249  if (i == flags_.end() || !i->second->IsRetired()) {
250  return nullptr;
251  }
252 
253  return i->second;
254 }
255 
257  return flag_ptr_map_.FindByPtr(flag_ptr);
258 }
259 
260 // --------------------------------------------------------------------
261 // FlagSaver
262 // FlagSaverImpl
263 // This class stores the states of all flags at construct time,
264 // and restores all flags to that state at destruct time.
265 // Its major implementation challenge is that it never modifies
266 // pointers in the 'main' registry, so global FLAG_* vars always
267 // point to the right place.
268 // --------------------------------------------------------------------
269 
271  public:
272  // Constructs an empty FlagSaverImpl object.
275  // reclaim memory from each of our CommandLineFlags
276  for (const SavedFlag& src : backup_registry_) {
277  Delete(src.op, src.current);
278  Delete(src.op, src.default_value);
279  }
280  }
281 
282  // Saves the flag states from the flag registry into this object.
283  // It's an error to call this more than once.
284  // Must be called when the registry mutex is not held.
286  assert(backup_registry_.empty()); // call only once!
287  SavedFlag saved;
289  if (flag->IsRetired()) return;
290 
291  saved.name = flag->Name();
292  saved.op = flag->op;
293  saved.marshalling_op = flag->marshalling_op;
294  {
296  saved.validator = flag->validator;
297  saved.modified = flag->modified;
298  saved.on_command_line = flag->IsSpecifiedOnCommandLine();
299  saved.current = Clone(saved.op, flag->cur);
300  saved.default_value = Clone(saved.op, flag->def);
301  saved.counter = flag->counter;
302  }
303  backup_registry_.push_back(saved);
304  });
305  }
306 
307  // Restores the saved flag states into the flag registry. We
308  // assume no flags were added or deleted from the registry since
309  // the SaveFromRegistry; if they were, that's trouble! Must be
310  // called when the registry mutex is not held.
312  FlagRegistry* const global_registry = FlagRegistry::GlobalRegistry();
313  FlagRegistryLock frl(global_registry);
314  for (const SavedFlag& src : backup_registry_) {
315  CommandLineFlag* flag = global_registry->FindFlagLocked(src.name);
316  // If null, flag got deleted from registry.
317  if (!flag) continue;
318 
319  bool restored = false;
320  {
321  absl::Mutex* mu = InitFlagIfNecessary(flag);
322  absl::MutexLock l(mu);
323  flag->validator = src.validator;
324  flag->modified = src.modified;
325  flag->on_command_line = src.on_command_line;
326  if (flag->counter != src.counter ||
327  ChangedDirectly(flag, src.default_value, flag->def)) {
328  flag->counter++;
329  Copy(src.op, src.default_value, flag->def);
330  }
331  if (flag->counter != src.counter ||
332  ChangedDirectly(flag, src.current, flag->cur)) {
333  restored = true;
334  flag->counter++;
335  Copy(src.op, src.current, flag->cur);
336  UpdateCopy(flag, mu);
337 
338  // Revalidate the flag because the validator might store state based
339  // on the flag's value, which just changed due to the restore.
340  // Failing validation is ignored because it's assumed that the flag
341  // was valid previously and there's little that can be done about it
342  // here, anyway.
343  Validate(flag, flag->cur);
344  }
345  }
346 
347  // Log statements must be done when no flag lock is held.
348  if (restored) {
350  INFO, absl::StrCat("Restore saved value of ", flag->Name(), ": ",
351  Unparse(src.marshalling_op, src.current)));
352  }
353  }
354  }
355 
356  private:
357  struct SavedFlag {
361  int64_t counter;
362  bool modified;
364  bool (*validator)();
365  const void* current; // nullptr after restore
366  const void* default_value; // nullptr after restore
367  };
368 
369  std::vector<SavedFlag> backup_registry_;
370 
371  FlagSaverImpl(const FlagSaverImpl&); // no copying!
372  void operator=(const FlagSaverImpl&);
373 };
374 
377 }
378 
380  delete impl_;
381  impl_ = nullptr;
382 }
383 
385  if (!impl_) return;
386 
388  delete impl_;
389 }
390 
391 // --------------------------------------------------------------------
392 // GetAllFlags()
393 // The main way the FlagRegistry class exposes its data. This
394 // returns, as strings, all the info about all the flags in
395 // the main registry, sorted first by filename they are defined
396 // in, and then by flagname.
397 // --------------------------------------------------------------------
398 
401  const CommandLineFlagInfo& b) const {
402  int cmp = absl::string_view(a.filename).compare(b.filename);
403  if (cmp != 0) return cmp < 0;
404  return a.name < b.name;
405  }
406 };
407 
409  CommandLineFlagInfo* result) {
410  result->name = std::string(flag->Name());
411  result->type = std::string(flag->Typename());
412  result->description = flag->Help();
413  result->filename = flag->Filename();
414 
415  UpdateModifiedBit(flag);
416 
418  result->current_value = flag->CurrentValue();
419  result->default_value = flag->DefaultValue();
420  result->is_default = !flag->modified;
421  result->has_validator_fn = (flag->validator != nullptr);
422  result->flag_ptr = flag->IsAbseilFlag() ? nullptr : flag->cur;
423 }
424 
425 // --------------------------------------------------------------------
426 
428  if (name.empty()) return nullptr;
429  FlagRegistry* const registry = FlagRegistry::GlobalRegistry();
430  FlagRegistryLock frl(registry);
431 
432  return registry->FindFlagLocked(name);
433 }
434 
435 CommandLineFlag* FindCommandLineV1Flag(const void* flag_ptr) {
436  FlagRegistry* const registry = FlagRegistry::GlobalRegistry();
437  FlagRegistryLock frl(registry);
438 
439  return registry->FindFlagViaPtrLocked(flag_ptr);
440 }
441 
443  FlagRegistry* const registry = FlagRegistry::GlobalRegistry();
444  FlagRegistryLock frl(registry);
445 
446  return registry->FindRetiredFlagLocked(name);
447 }
448 
449 // --------------------------------------------------------------------
450 
451 void ForEachFlagUnlocked(std::function<void(CommandLineFlag*)> visitor) {
452  FlagRegistry* const registry = FlagRegistry::GlobalRegistry();
453  for (FlagRegistry::FlagConstIterator i = registry->flags_.begin();
454  i != registry->flags_.end(); ++i) {
455  visitor(i->second);
456  }
457 }
458 
459 void ForEachFlag(std::function<void(CommandLineFlag*)> visitor) {
460  FlagRegistry* const registry = FlagRegistry::GlobalRegistry();
461  FlagRegistryLock frl(registry);
462  ForEachFlagUnlocked(visitor);
463 }
464 
465 // --------------------------------------------------------------------
466 
467 void GetAllFlags(std::vector<CommandLineFlagInfo>* OUTPUT) {
469  if (flag->IsRetired()) return;
470 
472  FillCommandLineFlagInfo(flag, &fi);
473  OUTPUT->push_back(fi);
474  });
475 
476  // Now sort the flags, first by filename they occur in, then alphabetically
477  std::sort(OUTPUT->begin(), OUTPUT->end(), FilenameFlagnameLess());
478 }
479 
480 // --------------------------------------------------------------------
481 
482 bool RegisterCommandLineFlag(CommandLineFlag* flag, const void* ptr) {
484  return true;
485 }
486 
487 // --------------------------------------------------------------------
488 
489 bool Retire(FlagOpFn ops, FlagMarshallingOpFn marshalling_ops,
490  const char* name) {
491  auto* flag = new CommandLineFlag(
492  name,
494  /*filename_arg=*/"RETIRED", ops, marshalling_ops,
495  /*initial_value_gen=*/nullptr,
496  /*retired_arg=*/true, nullptr, nullptr);
497  FlagRegistry::GlobalRegistry()->RegisterFlag(flag, nullptr);
498  return true;
499 }
500 
501 // --------------------------------------------------------------------
502 
503 bool IsRetiredFlag(absl::string_view name, bool* type_is_bool) {
504  assert(!name.empty());
506  if (flag == nullptr) {
507  return false;
508  }
509  assert(type_is_bool);
510  *type_is_bool = flag->IsOfType<bool>();
511  return true;
512 }
513 
514 } // namespace flags_internal
515 } // namespace absl
void Delete(FlagOpFn op, const void *obj)
static constexpr HelpText FromStaticCString(const char *msg)
#define EXCLUSIVE_LOCK_FUNCTION(...)
void UpdateModifiedBit(CommandLineFlag *flag)
static FlagRegistry * GlobalRegistry()
Definition: registry.cc:160
void ForEachFlagUnlocked(std::function< void(CommandLineFlag *)> visitor)
Definition: registry.cc:451
std::map< absl::string_view, CommandLineFlag * > FlagMap
Definition: registry.cc:146
bool Validate(CommandLineFlag *, const void *)
static constexpr size_t kNumBuckets
Definition: registry.cc:97
class FlagSaverImpl * impl_
Definition: registry.h:162
void * Clone(FlagOpFn op, const void *obj)
std::string StrCat(const AlphaNum &a, const AlphaNum &b)
Definition: str_cat.cc:98
void GetAllFlags(std::vector< CommandLineFlagInfo > *OUTPUT)
Definition: registry.cc:467
void Unlock() UNLOCK_FUNCTION(lock_)
Definition: registry.cc:125
FlagMap::iterator FlagIterator
Definition: registry.cc:147
FlagMap::const_iterator FlagConstIterator
Definition: registry.cc:148
void *(*)(FlagOp, const void *, void *) FlagOpFn
Definition: algorithm.h:29
void RegisterFlag(CommandLineFlag *flag, const void *ptr)
Definition: registry.cc:178
void *(*)(FlagOp, const void *, void *, void *) FlagMarshallingOpFn
void Lock() EXCLUSIVE_LOCK_FUNCTION(lock_)
Definition: registry.cc:124
#define ABSL_INTERNAL_LOG(severity, message)
Definition: raw_logging.h:73
char * ptr
std::vector< SavedFlag > backup_registry_
Definition: registry.cc:369
std::vector< CommandLineFlag * > buckets_[kNumBuckets]
Definition: registry.cc:98
CommandLineFlag * FindCommandLineFlag(absl::string_view name)
Definition: registry.cc:427
void Copy(FlagOpFn op, const void *src, void *dst)
char name[1]
Definition: mutex.cc:296
std::string Unparse(FlagMarshallingOpFn op, const void *val)
#define UNLOCK_FUNCTION(...)
CommandLineFlag * FindFlagViaPtrLocked(const void *flag_ptr)
Definition: registry.cc:256
#define NO_THREAD_SAFETY_ANALYSIS
constexpr bool empty() const noexcept
Definition: string_view.h:277
bool operator()(const CommandLineFlagInfo &a, const CommandLineFlagInfo &b) const
Definition: registry.cc:400
void FillCommandLineFlagInfo(CommandLineFlag *flag, CommandLineFlagInfo *result)
Definition: registry.cc:408
void ReportUsageError(absl::string_view msg, bool is_fatal)
CommandLineFlag * FindRetiredFlagLocked(absl::string_view name)
Definition: registry.cc:247
absl::string_view Typename() const
CommandLineFlag * FindFlagLocked(absl::string_view name)
Definition: registry.cc:233
CommandLineFlag * FindCommandLineV1Flag(const void *flag_ptr)
Definition: registry.cc:435
FlagRegistry *const fr_
Definition: registry.cc:173
uint64_t b
Definition: layout_test.cc:50
bool Retire(FlagOpFn ops, FlagMarshallingOpFn marshalling_ops, const char *name)
Definition: registry.cc:489
CommandLineFlag * FindRetiredFlag(absl::string_view name)
Definition: registry.cc:442
bool IsRetiredFlag(absl::string_view name, bool *type_is_bool)
Definition: registry.cc:503
void UpdateCopy(CommandLineFlag *flag, absl::Mutex *primary_lock) EXCLUSIVE_LOCKS_REQUIRED(primary_lock)
const FlagMarshallingOpFn marshalling_op
absl::Mutex * InitFlagIfNecessary(CommandLineFlag *flag) LOCK_RETURNED(flag -> locks->primary_mu)
int compare(string_view x) const noexcept
Definition: string_view.h:369
bool ChangedDirectly(CommandLineFlag *flag, const void *a, const void *b)
bool RegisterCommandLineFlag(CommandLineFlag *flag, const void *ptr)
Definition: registry.cc:482
void ForEachFlag(std::function< void(CommandLineFlag *)> visitor)
Definition: registry.cc:459


abseil_cpp
Author(s):
autogenerated on Mon Feb 28 2022 21:31:20