• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
16 #include "absl/flags/internal/registry.h"
17 
18 #include <assert.h>
19 #include <stdlib.h>
20 
21 #include <functional>
22 #include <map>
23 #include <memory>
24 #include <string>
25 #include <utility>
26 #include <vector>
27 
28 #include "absl/base/config.h"
29 #include "absl/base/internal/raw_logging.h"
30 #include "absl/base/thread_annotations.h"
31 #include "absl/flags/internal/commandlineflag.h"
32 #include "absl/flags/usage_config.h"
33 #include "absl/strings/str_cat.h"
34 #include "absl/strings/string_view.h"
35 #include "absl/synchronization/mutex.h"
36 
37 // --------------------------------------------------------------------
38 // FlagRegistry implementation
39 //    A FlagRegistry holds all flag objects indexed
40 //    by their names so that if you know a flag's name you can access or
41 //    set it.
42 
43 namespace absl {
44 ABSL_NAMESPACE_BEGIN
45 namespace flags_internal {
46 
47 // --------------------------------------------------------------------
48 // FlagRegistry
49 //    A FlagRegistry singleton object holds all flag objects indexed
50 //    by their names so that if you know a flag's name (as a C
51 //    string), you can access or set it.  If the function is named
52 //    FooLocked(), you must own the registry lock before calling
53 //    the function; otherwise, you should *not* hold the lock, and
54 //    the function will acquire it itself if needed.
55 // --------------------------------------------------------------------
56 
57 class FlagRegistry {
58  public:
59   FlagRegistry() = default;
60   ~FlagRegistry() = default;
61 
62   // Store a flag in this registry.  Takes ownership of *flag.
63   void RegisterFlag(CommandLineFlag* flag);
64 
Lock()65   void Lock() ABSL_EXCLUSIVE_LOCK_FUNCTION(lock_) { lock_.Lock(); }
Unlock()66   void Unlock() ABSL_UNLOCK_FUNCTION(lock_) { lock_.Unlock(); }
67 
68   // Returns the flag object for the specified name, or nullptr if not found.
69   // Will emit a warning if a 'retired' flag is specified.
70   CommandLineFlag* FindFlagLocked(absl::string_view name);
71 
72   // Returns the retired flag object for the specified name, or nullptr if not
73   // found or not retired.  Does not emit a warning.
74   CommandLineFlag* FindRetiredFlagLocked(absl::string_view name);
75 
76   static FlagRegistry* GlobalRegistry();  // returns a singleton registry
77 
78  private:
79   friend class FlagSaverImpl;  // reads all the flags in order to copy them
80   friend void ForEachFlagUnlocked(
81       std::function<void(CommandLineFlag*)> visitor);
82 
83   // The map from name to flag, for FindFlagLocked().
84   using FlagMap = std::map<absl::string_view, CommandLineFlag*>;
85   using FlagIterator = FlagMap::iterator;
86   using FlagConstIterator = FlagMap::const_iterator;
87   FlagMap flags_;
88 
89   absl::Mutex lock_;
90 
91   // Disallow
92   FlagRegistry(const FlagRegistry&);
93   FlagRegistry& operator=(const FlagRegistry&);
94 };
95 
GlobalRegistry()96 FlagRegistry* FlagRegistry::GlobalRegistry() {
97   static FlagRegistry* global_registry = new FlagRegistry;
98   return global_registry;
99 }
100 
101 namespace {
102 
103 class FlagRegistryLock {
104  public:
FlagRegistryLock(FlagRegistry * fr)105   explicit FlagRegistryLock(FlagRegistry* fr) : fr_(fr) { fr_->Lock(); }
~FlagRegistryLock()106   ~FlagRegistryLock() { fr_->Unlock(); }
107 
108  private:
109   FlagRegistry* const fr_;
110 };
111 
112 void DestroyRetiredFlag(CommandLineFlag* flag);
113 }  // namespace
114 
RegisterFlag(CommandLineFlag * flag)115 void FlagRegistry::RegisterFlag(CommandLineFlag* flag) {
116   FlagRegistryLock registry_lock(this);
117   std::pair<FlagIterator, bool> ins =
118       flags_.insert(FlagMap::value_type(flag->Name(), flag));
119   if (ins.second == false) {  // means the name was already in the map
120     CommandLineFlag* old_flag = ins.first->second;
121     if (flag->IsRetired() != old_flag->IsRetired()) {
122       // All registrations must agree on the 'retired' flag.
123       flags_internal::ReportUsageError(
124           absl::StrCat(
125               "Retired flag '", flag->Name(),
126               "' was defined normally in file '",
127               (flag->IsRetired() ? old_flag->Filename() : flag->Filename()),
128               "'."),
129           true);
130     } else if (flag->TypeId() != old_flag->TypeId()) {
131       flags_internal::ReportUsageError(
132           absl::StrCat("Flag '", flag->Name(),
133                        "' was defined more than once but with "
134                        "differing types. Defined in files '",
135                        old_flag->Filename(), "' and '", flag->Filename(),
136                        "' with types '", old_flag->Typename(), "' and '",
137                        flag->Typename(), "', respectively."),
138           true);
139     } else if (old_flag->IsRetired()) {
140       // Retired flag can just be deleted.
141       DestroyRetiredFlag(flag);
142       return;
143     } else if (old_flag->Filename() != flag->Filename()) {
144       flags_internal::ReportUsageError(
145           absl::StrCat("Flag '", flag->Name(),
146                        "' was defined more than once (in files '",
147                        old_flag->Filename(), "' and '", flag->Filename(),
148                        "')."),
149           true);
150     } else {
151       flags_internal::ReportUsageError(
152           absl::StrCat(
153               "Something wrong with flag '", flag->Name(), "' in file '",
154               flag->Filename(), "'. One possibility: file '", flag->Filename(),
155               "' is being linked both statically and dynamically into this "
156               "executable. e.g. some files listed as srcs to a test and also "
157               "listed as srcs of some shared lib deps of the same test."),
158           true);
159     }
160     // All cases above are fatal, except for the retired flags.
161     std::exit(1);
162   }
163 }
164 
FindFlagLocked(absl::string_view name)165 CommandLineFlag* FlagRegistry::FindFlagLocked(absl::string_view name) {
166   FlagConstIterator i = flags_.find(name);
167   if (i == flags_.end()) {
168     return nullptr;
169   }
170 
171   if (i->second->IsRetired()) {
172     flags_internal::ReportUsageError(
173         absl::StrCat("Accessing retired flag '", name, "'"), false);
174   }
175 
176   return i->second;
177 }
178 
FindRetiredFlagLocked(absl::string_view name)179 CommandLineFlag* FlagRegistry::FindRetiredFlagLocked(absl::string_view name) {
180   FlagConstIterator i = flags_.find(name);
181   if (i == flags_.end() || !i->second->IsRetired()) {
182     return nullptr;
183   }
184 
185   return i->second;
186 }
187 
188 // --------------------------------------------------------------------
189 // FlagSaver
190 // FlagSaverImpl
191 //    This class stores the states of all flags at construct time,
192 //    and restores all flags to that state at destruct time.
193 //    Its major implementation challenge is that it never modifies
194 //    pointers in the 'main' registry, so global FLAG_* vars always
195 //    point to the right place.
196 // --------------------------------------------------------------------
197 
198 class FlagSaverImpl {
199  public:
200   FlagSaverImpl() = default;
201   FlagSaverImpl(const FlagSaverImpl&) = delete;
202   void operator=(const FlagSaverImpl&) = delete;
203 
204   // Saves the flag states from the flag registry into this object.
205   // It's an error to call this more than once.
SaveFromRegistry()206   void SaveFromRegistry() {
207     assert(backup_registry_.empty());  // call only once!
208     flags_internal::ForEachFlag([&](flags_internal::CommandLineFlag* flag) {
209       if (auto flag_state = flag->SaveState()) {
210         backup_registry_.emplace_back(std::move(flag_state));
211       }
212     });
213   }
214 
215   // Restores the saved flag states into the flag registry.
RestoreToRegistry()216   void RestoreToRegistry() {
217     for (const auto& flag_state : backup_registry_) {
218       flag_state->Restore();
219     }
220   }
221 
222  private:
223   std::vector<std::unique_ptr<flags_internal::FlagStateInterface>>
224       backup_registry_;
225 };
226 
FlagSaver()227 FlagSaver::FlagSaver() : impl_(new FlagSaverImpl) { impl_->SaveFromRegistry(); }
228 
Ignore()229 void FlagSaver::Ignore() {
230   delete impl_;
231   impl_ = nullptr;
232 }
233 
~FlagSaver()234 FlagSaver::~FlagSaver() {
235   if (!impl_) return;
236 
237   impl_->RestoreToRegistry();
238   delete impl_;
239 }
240 
241 // --------------------------------------------------------------------
242 
FindCommandLineFlag(absl::string_view name)243 CommandLineFlag* FindCommandLineFlag(absl::string_view name) {
244   if (name.empty()) return nullptr;
245   FlagRegistry* const registry = FlagRegistry::GlobalRegistry();
246   FlagRegistryLock frl(registry);
247 
248   return registry->FindFlagLocked(name);
249 }
250 
FindRetiredFlag(absl::string_view name)251 CommandLineFlag* FindRetiredFlag(absl::string_view name) {
252   FlagRegistry* const registry = FlagRegistry::GlobalRegistry();
253   FlagRegistryLock frl(registry);
254 
255   return registry->FindRetiredFlagLocked(name);
256 }
257 
258 // --------------------------------------------------------------------
259 
ForEachFlagUnlocked(std::function<void (CommandLineFlag *)> visitor)260 void ForEachFlagUnlocked(std::function<void(CommandLineFlag*)> visitor) {
261   FlagRegistry* const registry = FlagRegistry::GlobalRegistry();
262   for (FlagRegistry::FlagConstIterator i = registry->flags_.begin();
263        i != registry->flags_.end(); ++i) {
264     visitor(i->second);
265   }
266 }
267 
ForEachFlag(std::function<void (CommandLineFlag *)> visitor)268 void ForEachFlag(std::function<void(CommandLineFlag*)> visitor) {
269   FlagRegistry* const registry = FlagRegistry::GlobalRegistry();
270   FlagRegistryLock frl(registry);
271   ForEachFlagUnlocked(visitor);
272 }
273 
274 // --------------------------------------------------------------------
275 
RegisterCommandLineFlag(CommandLineFlag * flag)276 bool RegisterCommandLineFlag(CommandLineFlag* flag) {
277   FlagRegistry::GlobalRegistry()->RegisterFlag(flag);
278   return true;
279 }
280 
281 // --------------------------------------------------------------------
282 
283 namespace {
284 
285 class RetiredFlagObj final : public flags_internal::CommandLineFlag {
286  public:
RetiredFlagObj(const char * name,FlagStaticTypeId type_id)287   constexpr RetiredFlagObj(const char* name, FlagStaticTypeId type_id)
288       : name_(name), type_id_(type_id) {}
289 
290  private:
Name() const291   absl::string_view Name() const override { return name_; }
Filename() const292   std::string Filename() const override { return "RETIRED"; }
Typename() const293   absl::string_view Typename() const override { return ""; }
TypeId() const294   FlagStaticTypeId TypeId() const override { return type_id_; }
Help() const295   std::string Help() const override { return ""; }
IsRetired() const296   bool IsRetired() const override { return true; }
IsModified() const297   bool IsModified() const override { return false; }
IsSpecifiedOnCommandLine() const298   bool IsSpecifiedOnCommandLine() const override { return false; }
DefaultValue() const299   std::string DefaultValue() const override { return ""; }
CurrentValue() const300   std::string CurrentValue() const override { return ""; }
301 
302   // Any input is valid
ValidateInputValue(absl::string_view) const303   bool ValidateInputValue(absl::string_view) const override { return true; }
304 
SaveState()305   std::unique_ptr<flags_internal::FlagStateInterface> SaveState() override {
306     return nullptr;
307   }
308 
SetFromString(absl::string_view,flags_internal::FlagSettingMode,flags_internal::ValueSource,std::string *)309   bool SetFromString(absl::string_view, flags_internal::FlagSettingMode,
310                      flags_internal::ValueSource, std::string*) override {
311     return false;
312   }
313 
CheckDefaultValueParsingRoundtrip() const314   void CheckDefaultValueParsingRoundtrip() const override {}
315 
Read(void *) const316   void Read(void*) const override {}
317 
318   // Data members
319   const char* const name_;
320   const FlagStaticTypeId type_id_;
321 };
322 
DestroyRetiredFlag(flags_internal::CommandLineFlag * flag)323 void DestroyRetiredFlag(flags_internal::CommandLineFlag* flag) {
324   assert(flag->IsRetired());
325   delete static_cast<RetiredFlagObj*>(flag);
326 }
327 
328 }  // namespace
329 
Retire(const char * name,FlagStaticTypeId type_id)330 bool Retire(const char* name, FlagStaticTypeId type_id) {
331   auto* flag = new flags_internal::RetiredFlagObj(name, type_id);
332   FlagRegistry::GlobalRegistry()->RegisterFlag(flag);
333   return true;
334 }
335 
336 // --------------------------------------------------------------------
337 
IsRetiredFlag(absl::string_view name,bool * type_is_bool)338 bool IsRetiredFlag(absl::string_view name, bool* type_is_bool) {
339   assert(!name.empty());
340   CommandLineFlag* flag = flags_internal::FindRetiredFlag(name);
341   if (flag == nullptr) {
342     return false;
343   }
344   assert(type_is_bool);
345   *type_is_bool = flag->IsOfType<bool>();
346   return true;
347 }
348 
349 }  // namespace flags_internal
350 ABSL_NAMESPACE_END
351 }  // namespace absl
352