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