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