00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
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
00028
00029
00030
00031
00032 namespace absl {
00033 namespace flags_internal {
00034 namespace {
00035
00036 void DestroyFlag(CommandLineFlag* flag) NO_THREAD_SAFETY_ANALYSIS {
00037
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
00046 if (!flag->IsAbseilFlag()) {
00047 delete flag;
00048 }
00049 }
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063 class FlagPtrMap {
00064 public:
00065 void Register(CommandLineFlag* flag) {
00066 auto& vec = buckets_[BucketForFlag(flag->cur)];
00067 if (vec.size() == vec.capacity()) {
00068
00069
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
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097 static constexpr size_t kNumBuckets = 163;
00098 std::vector<CommandLineFlag*> buckets_[kNumBuckets];
00099
00100 static int BucketForFlag(const void* ptr) {
00101
00102
00103 return reinterpret_cast<uintptr_t>(ptr) % kNumBuckets;
00104 }
00105 };
00106 constexpr size_t FlagPtrMap::kNumBuckets;
00107
00108 }
00109
00110 class FlagRegistry {
00111 public:
00112 FlagRegistry() = default;
00113 ~FlagRegistry() {
00114 for (auto& p : flags_) {
00115 DestroyFlag(p.second);
00116 }
00117 }
00118
00119
00120
00121
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
00128
00129 CommandLineFlag* FindFlagLocked(absl::string_view name);
00130
00131
00132
00133 CommandLineFlag* FindRetiredFlagLocked(absl::string_view name);
00134
00135
00136 CommandLineFlag* FindFlagViaPtrLocked(const void* flag_ptr);
00137
00138 static FlagRegistry* GlobalRegistry();
00139
00140 private:
00141 friend class FlagSaverImpl;
00142 friend void ForEachFlagUnlocked(
00143 std::function<void(CommandLineFlag*)> visitor);
00144
00145
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
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 }
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) {
00183 CommandLineFlag* old_flag = ins.first->second;
00184 if (flag->IsRetired() != old_flag->IsRetired()) {
00185
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
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
00224 std::exit(1);
00225 }
00226
00227 if (ptr != nullptr) {
00228
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
00262
00263
00264
00265
00266
00267
00268
00269
00270 class FlagSaverImpl {
00271 public:
00272
00273 FlagSaverImpl() {}
00274 ~FlagSaverImpl() {
00275
00276 for (const SavedFlag& src : backup_registry_) {
00277 Delete(src.op, src.current);
00278 Delete(src.op, src.default_value);
00279 }
00280 }
00281
00282
00283
00284
00285 void SaveFromRegistry() {
00286 assert(backup_registry_.empty());
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
00308
00309
00310
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
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
00339
00340
00341
00342
00343 Validate(flag, flag->cur);
00344 }
00345 }
00346
00347
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;
00366 const void* default_value;
00367 };
00368
00369 std::vector<SavedFlag> backup_registry_;
00370
00371 FlagSaverImpl(const FlagSaverImpl&);
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
00393
00394
00395
00396
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
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 absl::flags_internal::HelpText::FromStaticCString(nullptr),
00494 "RETIRED", ops, marshalling_ops,
00495 nullptr,
00496 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 }
00515 }