• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 //  Copyright 2020 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 
16 #include "absl/flags/reflection.h"
17 
18 #include <assert.h>
19 
20 #include <map>
21 #include <string>
22 
23 #include "absl/base/config.h"
24 #include "absl/base/thread_annotations.h"
25 #include "absl/flags/commandlineflag.h"
26 #include "absl/flags/internal/private_handle_accessor.h"
27 #include "absl/flags/internal/registry.h"
28 #include "absl/flags/usage_config.h"
29 #include "absl/strings/str_cat.h"
30 #include "absl/strings/string_view.h"
31 #include "absl/synchronization/mutex.h"
32 
33 namespace absl {
34 ABSL_NAMESPACE_BEGIN
35 namespace flags_internal {
36 
37 // --------------------------------------------------------------------
38 // FlagRegistry
39 //    A FlagRegistry singleton object holds all flag objects indexed by their
40 //    names so that if you know a flag's name, you can access or set it. If the
41 //    function is named FooLocked(), you must own the registry lock before
42 //    calling the function; otherwise, you should *not* hold the lock, and the
43 //    function will acquire it itself if needed.
44 // --------------------------------------------------------------------
45 
46 class FlagRegistry {
47  public:
48   FlagRegistry() = default;
49   ~FlagRegistry() = default;
50 
51   // Store a flag in this registry. Takes ownership of *flag.
52   void RegisterFlag(CommandLineFlag& flag);
53 
Lock()54   void Lock() ABSL_EXCLUSIVE_LOCK_FUNCTION(lock_) { lock_.Lock(); }
Unlock()55   void Unlock() ABSL_UNLOCK_FUNCTION(lock_) { lock_.Unlock(); }
56 
57   // Returns the flag object for the specified name, or nullptr if not found.
58   // Will emit a warning if a 'retired' flag is specified.
59   CommandLineFlag* FindFlagLocked(absl::string_view name);
60 
61   static FlagRegistry& GlobalRegistry();  // returns a singleton registry
62 
63  private:
64   friend class flags_internal::FlagSaverImpl;  // reads all the flags in order
65                                                // to copy them
66   friend void ForEachFlagUnlocked(
67       std::function<void(CommandLineFlag&)> visitor);
68 
69   // The map from name to flag, for FindFlagLocked().
70   using FlagMap = std::map<absl::string_view, CommandLineFlag*>;
71   using FlagIterator = FlagMap::iterator;
72   using FlagConstIterator = FlagMap::const_iterator;
73   FlagMap flags_;
74 
75   absl::Mutex lock_;
76 
77   // Disallow
78   FlagRegistry(const FlagRegistry&);
79   FlagRegistry& operator=(const FlagRegistry&);
80 };
81 
FindFlagLocked(absl::string_view name)82 CommandLineFlag* FlagRegistry::FindFlagLocked(absl::string_view name) {
83   FlagConstIterator i = flags_.find(name);
84   if (i == flags_.end()) {
85     return nullptr;
86   }
87 
88   return i->second;
89 }
90 
91 namespace {
92 
93 class FlagRegistryLock {
94  public:
FlagRegistryLock(FlagRegistry & fr)95   explicit FlagRegistryLock(FlagRegistry& fr) : fr_(fr) { fr_.Lock(); }
~FlagRegistryLock()96   ~FlagRegistryLock() { fr_.Unlock(); }
97 
98  private:
99   FlagRegistry& fr_;
100 };
101 
102 }  // namespace
103 
RegisterFlag(CommandLineFlag & flag)104 void FlagRegistry::RegisterFlag(CommandLineFlag& flag) {
105   FlagRegistryLock registry_lock(*this);
106   std::pair<FlagIterator, bool> ins =
107       flags_.insert(FlagMap::value_type(flag.Name(), &flag));
108   if (ins.second == false) {  // means the name was already in the map
109     CommandLineFlag& old_flag = *ins.first->second;
110     if (flag.IsRetired() != old_flag.IsRetired()) {
111       // All registrations must agree on the 'retired' flag.
112       flags_internal::ReportUsageError(
113           absl::StrCat(
114               "Retired flag '", flag.Name(), "' was defined normally in file '",
115               (flag.IsRetired() ? old_flag.Filename() : flag.Filename()), "'."),
116           true);
117     } else if (flags_internal::PrivateHandleAccessor::TypeId(flag) !=
118                flags_internal::PrivateHandleAccessor::TypeId(old_flag)) {
119       flags_internal::ReportUsageError(
120           absl::StrCat("Flag '", flag.Name(),
121                        "' was defined more than once but with "
122                        "differing types. Defined in files '",
123                        old_flag.Filename(), "' and '", flag.Filename(), "'."),
124           true);
125     } else if (old_flag.IsRetired()) {
126       return;
127     } else if (old_flag.Filename() != flag.Filename()) {
128       flags_internal::ReportUsageError(
129           absl::StrCat("Flag '", flag.Name(),
130                        "' was defined more than once (in files '",
131                        old_flag.Filename(), "' and '", flag.Filename(), "')."),
132           true);
133     } else {
134       flags_internal::ReportUsageError(
135           absl::StrCat(
136               "Something is wrong with flag '", flag.Name(), "' in file '",
137               flag.Filename(), "'. One possibility: file '", flag.Filename(),
138               "' is being linked both statically and dynamically into this "
139               "executable. e.g. some files listed as srcs to a test and also "
140               "listed as srcs of some shared lib deps of the same test."),
141           true);
142     }
143     // All cases above are fatal, except for the retired flags.
144     std::exit(1);
145   }
146 }
147 
GlobalRegistry()148 FlagRegistry& FlagRegistry::GlobalRegistry() {
149   static FlagRegistry* global_registry = new FlagRegistry;
150   return *global_registry;
151 }
152 
153 // --------------------------------------------------------------------
154 
ForEachFlagUnlocked(std::function<void (CommandLineFlag &)> visitor)155 void ForEachFlagUnlocked(std::function<void(CommandLineFlag&)> visitor) {
156   FlagRegistry& registry = FlagRegistry::GlobalRegistry();
157   for (FlagRegistry::FlagConstIterator i = registry.flags_.begin();
158        i != registry.flags_.end(); ++i) {
159     visitor(*i->second);
160   }
161 }
162 
ForEachFlag(std::function<void (CommandLineFlag &)> visitor)163 void ForEachFlag(std::function<void(CommandLineFlag&)> visitor) {
164   FlagRegistry& registry = FlagRegistry::GlobalRegistry();
165   FlagRegistryLock frl(registry);
166   ForEachFlagUnlocked(visitor);
167 }
168 
169 // --------------------------------------------------------------------
170 
RegisterCommandLineFlag(CommandLineFlag & flag)171 bool RegisterCommandLineFlag(CommandLineFlag& flag) {
172   FlagRegistry::GlobalRegistry().RegisterFlag(flag);
173   return true;
174 }
175 
176 // --------------------------------------------------------------------
177 
178 namespace {
179 
180 class RetiredFlagObj final : public CommandLineFlag {
181  public:
RetiredFlagObj(const char * name,FlagFastTypeId type_id)182   constexpr RetiredFlagObj(const char* name, FlagFastTypeId type_id)
183       : name_(name), type_id_(type_id) {}
184 
185  private:
Name() const186   absl::string_view Name() const override { return name_; }
Filename() const187   std::string Filename() const override {
188     OnAccess();
189     return "RETIRED";
190   }
TypeId() const191   FlagFastTypeId TypeId() const override { return type_id_; }
Help() const192   std::string Help() const override {
193     OnAccess();
194     return "";
195   }
IsRetired() const196   bool IsRetired() const override { return true; }
IsSpecifiedOnCommandLine() const197   bool IsSpecifiedOnCommandLine() const override {
198     OnAccess();
199     return false;
200   }
DefaultValue() const201   std::string DefaultValue() const override {
202     OnAccess();
203     return "";
204   }
CurrentValue() const205   std::string CurrentValue() const override {
206     OnAccess();
207     return "";
208   }
209 
210   // Any input is valid
ValidateInputValue(absl::string_view) const211   bool ValidateInputValue(absl::string_view) const override {
212     OnAccess();
213     return true;
214   }
215 
SaveState()216   std::unique_ptr<flags_internal::FlagStateInterface> SaveState() override {
217     return nullptr;
218   }
219 
ParseFrom(absl::string_view,flags_internal::FlagSettingMode,flags_internal::ValueSource,std::string &)220   bool ParseFrom(absl::string_view, flags_internal::FlagSettingMode,
221                  flags_internal::ValueSource, std::string&) override {
222     OnAccess();
223     return false;
224   }
225 
CheckDefaultValueParsingRoundtrip() const226   void CheckDefaultValueParsingRoundtrip() const override { OnAccess(); }
227 
Read(void *) const228   void Read(void*) const override { OnAccess(); }
229 
OnAccess() const230   void OnAccess() const {
231     flags_internal::ReportUsageError(
232         absl::StrCat("Accessing retired flag '", name_, "'"), false);
233   }
234 
235   // Data members
236   const char* const name_;
237   const FlagFastTypeId type_id_;
238 };
239 
240 }  // namespace
241 
Retire(const char * name,FlagFastTypeId type_id,char * buf)242 void Retire(const char* name, FlagFastTypeId type_id, char* buf) {
243   static_assert(sizeof(RetiredFlagObj) == kRetiredFlagObjSize, "");
244   static_assert(alignof(RetiredFlagObj) == kRetiredFlagObjAlignment, "");
245   auto* flag = ::new (static_cast<void*>(buf))
246       flags_internal::RetiredFlagObj(name, type_id);
247   FlagRegistry::GlobalRegistry().RegisterFlag(*flag);
248 }
249 
250 // --------------------------------------------------------------------
251 
252 class FlagSaverImpl {
253  public:
254   FlagSaverImpl() = default;
255   FlagSaverImpl(const FlagSaverImpl&) = delete;
256   void operator=(const FlagSaverImpl&) = delete;
257 
258   // Saves the flag states from the flag registry into this object.
259   // It's an error to call this more than once.
SaveFromRegistry()260   void SaveFromRegistry() {
261     assert(backup_registry_.empty());  // call only once!
262     flags_internal::ForEachFlag([&](CommandLineFlag& flag) {
263       if (auto flag_state =
264               flags_internal::PrivateHandleAccessor::SaveState(flag)) {
265         backup_registry_.emplace_back(std::move(flag_state));
266       }
267     });
268   }
269 
270   // Restores the saved flag states into the flag registry.
RestoreToRegistry()271   void RestoreToRegistry() {
272     for (const auto& flag_state : backup_registry_) {
273       flag_state->Restore();
274     }
275   }
276 
277  private:
278   std::vector<std::unique_ptr<flags_internal::FlagStateInterface>>
279       backup_registry_;
280 };
281 
282 }  // namespace flags_internal
283 
FlagSaver()284 FlagSaver::FlagSaver() : impl_(new flags_internal::FlagSaverImpl) {
285   impl_->SaveFromRegistry();
286 }
287 
~FlagSaver()288 FlagSaver::~FlagSaver() {
289   if (!impl_) return;
290 
291   impl_->RestoreToRegistry();
292   delete impl_;
293 }
294 
295 // --------------------------------------------------------------------
296 
FindCommandLineFlag(absl::string_view name)297 CommandLineFlag* FindCommandLineFlag(absl::string_view name) {
298   if (name.empty()) return nullptr;
299   flags_internal::FlagRegistry& registry =
300       flags_internal::FlagRegistry::GlobalRegistry();
301   flags_internal::FlagRegistryLock frl(registry);
302 
303   return registry.FindFlagLocked(name);
304 }
305 
306 // --------------------------------------------------------------------
307 
GetAllFlags()308 absl::flat_hash_map<absl::string_view, absl::CommandLineFlag*> GetAllFlags() {
309   absl::flat_hash_map<absl::string_view, absl::CommandLineFlag*> res;
310   flags_internal::ForEachFlag([&](CommandLineFlag& flag) {
311     res.insert({flag.Name(), &flag});
312   });
313   return res;
314 }
315 
316 ABSL_NAMESPACE_END
317 }  // namespace absl
318