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/commandlineflag.h" |
17 | |
18 | #include <cassert> |
19 | |
20 | #include "absl/base/internal/raw_logging.h" |
21 | #include "absl/base/optimization.h" |
22 | #include "absl/flags/config.h" |
23 | #include "absl/flags/usage_config.h" |
24 | #include "absl/strings/str_cat.h" |
25 | #include "absl/synchronization/mutex.h" |
26 | |
27 | namespace absl { |
28 | namespace flags_internal { |
29 | |
30 | // The help message indicating that the commandline flag has been |
31 | // 'stripped'. It will not show up when doing "-help" and its |
32 | // variants. The flag is stripped if ABSL_FLAGS_STRIP_HELP is set to 1 |
33 | // before including absl/flags/flag.h |
34 | |
35 | // This is used by this file, and also in commandlineflags_reporting.cc |
36 | const char kStrippedFlagHelp[] = "\001\002\003\004 (unknown) \004\003\002\001" ; |
37 | |
38 | namespace { |
39 | |
40 | void StoreAtomic(CommandLineFlag* flag, const void* data, size_t size) { |
41 | int64_t t = 0; |
42 | assert(size <= sizeof(int64_t)); |
43 | memcpy(&t, data, size); |
44 | flag->atomic.store(t, std::memory_order_release); |
45 | } |
46 | |
47 | // If the flag has a mutation callback this function invokes it. While the |
48 | // callback is being invoked the primary flag's mutex is unlocked and it is |
49 | // re-locked back after call to callback is completed. Callback invocation is |
50 | // guarded by flag's secondary mutex instead which prevents concurrent callback |
51 | // invocation. Note that it is possible for other thread to grab the primary |
52 | // lock and update flag's value at any time during the callback invocation. |
53 | // This is by design. Callback can get a value of the flag if necessary, but it |
54 | // might be different from the value initiated the callback and it also can be |
55 | // different by the time the callback invocation is completed. |
56 | // Requires that *primary_lock be held in exclusive mode; it may be released |
57 | // and reacquired by the implementation. |
58 | void InvokeCallback(CommandLineFlag* flag, absl::Mutex* primary_lock) |
59 | EXCLUSIVE_LOCKS_REQUIRED(primary_lock) { |
60 | if (!flag->callback) return; |
61 | |
62 | // The callback lock is guaranteed initialized, because *primary_lock exists. |
63 | absl::Mutex* callback_mu = &flag->locks->callback_mu; |
64 | |
65 | // When executing the callback we need the primary flag's mutex to be unlocked |
66 | // so that callback can retrieve the flag's value. |
67 | primary_lock->Unlock(); |
68 | |
69 | { |
70 | absl::MutexLock lock(callback_mu); |
71 | |
72 | flag->callback(); |
73 | } |
74 | |
75 | primary_lock->Lock(); |
76 | } |
77 | |
78 | // Currently we only validate flag values for user-defined flag types. |
79 | bool ShouldValidateFlagValue(const CommandLineFlag& flag) { |
80 | #define DONT_VALIDATE(T) \ |
81 | if (flag.IsOfType<T>()) return false; |
82 | ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(DONT_VALIDATE) |
83 | DONT_VALIDATE(std::string) |
84 | DONT_VALIDATE(std::vector<std::string>) |
85 | #undef DONT_VALIDATE |
86 | |
87 | return true; |
88 | } |
89 | |
90 | } // namespace |
91 | |
92 | // Update any copy of the flag value that is stored in an atomic word. |
93 | // In addition if flag has a mutation callback this function invokes it. |
94 | void UpdateCopy(CommandLineFlag* flag, absl::Mutex* primary_lock) |
95 | EXCLUSIVE_LOCKS_REQUIRED(primary_lock) { |
96 | #define STORE_ATOMIC(T) \ |
97 | else if (flag->IsOfType<T>()) { \ |
98 | StoreAtomic(flag, flag->cur, sizeof(T)); \ |
99 | } |
100 | |
101 | if (false) { |
102 | } |
103 | ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(STORE_ATOMIC) |
104 | #undef STORE_ATOMIC |
105 | |
106 | InvokeCallback(flag, primary_lock); |
107 | } |
108 | |
109 | // Ensure that the lazily initialized fields of *flag have been initialized, |
110 | // and return &flag->locks->primary_mu. |
111 | absl::Mutex* InitFlagIfNecessary(CommandLineFlag* flag) |
112 | LOCK_RETURNED(flag->locks->primary_mu) { |
113 | absl::Mutex* mu; |
114 | if (!flag->inited.load(std::memory_order_acquire)) { |
115 | // Need to initialize lazily initialized fields. |
116 | ABSL_CONST_INIT static absl::Mutex init_lock(absl::kConstInit); |
117 | init_lock.Lock(); |
118 | if (flag->locks == nullptr) { // Must initialize Mutexes for this flag. |
119 | flag->locks = new flags_internal::CommandLineFlagLocks; |
120 | } |
121 | mu = &flag->locks->primary_mu; |
122 | init_lock.Unlock(); |
123 | mu->Lock(); |
124 | if (!flag->retired && |
125 | flag->def == nullptr) { // Need to initialize def and cur fields. |
126 | flag->def = (*flag->make_init_value)(); |
127 | flag->cur = Clone(flag->op, flag->def); |
128 | UpdateCopy(flag, mu); |
129 | } |
130 | mu->Unlock(); |
131 | flag->inited.store(true, std::memory_order_release); |
132 | } else { // All fields initialized; flag->locks is therefore safe to read. |
133 | mu = &flag->locks->primary_mu; |
134 | } |
135 | return mu; |
136 | } |
137 | |
138 | // Return true iff flag value was changed via direct-access. |
139 | bool ChangedDirectly(CommandLineFlag* flag, const void* a, const void* b) { |
140 | if (!flag->IsAbseilFlag()) { |
141 | // Need to compare values for direct-access flags. |
142 | #define CHANGED_FOR_TYPE(T) \ |
143 | if (flag->IsOfType<T>()) { \ |
144 | return *reinterpret_cast<const T*>(a) != *reinterpret_cast<const T*>(b); \ |
145 | } |
146 | |
147 | CHANGED_FOR_TYPE(bool); |
148 | CHANGED_FOR_TYPE(int32_t); |
149 | CHANGED_FOR_TYPE(int64_t); |
150 | CHANGED_FOR_TYPE(uint64_t); |
151 | CHANGED_FOR_TYPE(double); |
152 | CHANGED_FOR_TYPE(std::string); |
153 | #undef CHANGED_FOR_TYPE |
154 | } |
155 | return false; |
156 | } |
157 | |
158 | // Direct-access flags can be modified without going through the |
159 | // flag API. Detect such changes and updated the modified bit. |
160 | void UpdateModifiedBit(CommandLineFlag* flag) { |
161 | if (!flag->IsAbseilFlag()) { |
162 | absl::MutexLock l(InitFlagIfNecessary(flag)); |
163 | if (!flag->modified && ChangedDirectly(flag, flag->cur, flag->def)) { |
164 | flag->modified = true; |
165 | } |
166 | } |
167 | } |
168 | |
169 | bool Validate(CommandLineFlag*, const void*) { |
170 | return true; |
171 | } |
172 | |
173 | std::string HelpText::GetHelpText() const { |
174 | if (help_function_) return help_function_(); |
175 | if (help_message_) return help_message_; |
176 | |
177 | return {}; |
178 | } |
179 | |
180 | const int64_t CommandLineFlag::kAtomicInit; |
181 | |
182 | void CommandLineFlag::Read(void* dst, |
183 | const flags_internal::FlagOpFn dst_op) const { |
184 | absl::ReaderMutexLock l( |
185 | InitFlagIfNecessary(const_cast<CommandLineFlag*>(this))); |
186 | |
187 | // `dst_op` is the unmarshaling operation corresponding to the declaration |
188 | // visibile at the call site. `op` is the Flag's defined unmarshalling |
189 | // operation. They must match for this operation to be well-defined. |
190 | if (ABSL_PREDICT_FALSE(dst_op != op)) { |
191 | ABSL_INTERNAL_LOG( |
192 | ERROR, |
193 | absl::StrCat("Flag '" , name, |
194 | "' is defined as one type and declared as another" )); |
195 | } |
196 | CopyConstruct(op, cur, dst); |
197 | } |
198 | |
199 | void CommandLineFlag::Write(const void* src, |
200 | const flags_internal::FlagOpFn src_op) { |
201 | absl::Mutex* mu = InitFlagIfNecessary(this); |
202 | absl::MutexLock l(mu); |
203 | |
204 | // `src_op` is the marshalling operation corresponding to the declaration |
205 | // visible at the call site. `op` is the Flag's defined marshalling operation. |
206 | // They must match for this operation to be well-defined. |
207 | if (ABSL_PREDICT_FALSE(src_op != op)) { |
208 | ABSL_INTERNAL_LOG( |
209 | ERROR, |
210 | absl::StrCat("Flag '" , name, |
211 | "' is defined as one type and declared as another" )); |
212 | } |
213 | |
214 | if (ShouldValidateFlagValue(*this)) { |
215 | void* obj = Clone(op, src); |
216 | std::string ignored_error; |
217 | std::string src_as_str = Unparse(marshalling_op, src); |
218 | if (!Parse(marshalling_op, src_as_str, obj, &ignored_error) || |
219 | !Validate(this, obj)) { |
220 | ABSL_INTERNAL_LOG(ERROR, absl::StrCat("Attempt to set flag '" , name, |
221 | "' to invalid value " , src_as_str)); |
222 | } |
223 | Delete(op, obj); |
224 | } |
225 | |
226 | modified = true; |
227 | counter++; |
228 | Copy(op, src, cur); |
229 | |
230 | UpdateCopy(this, mu); |
231 | } |
232 | |
233 | absl::string_view CommandLineFlag::Typename() const { |
234 | // We do not store/report type in Abseil Flags, so that user do not rely on in |
235 | // at runtime |
236 | if (IsAbseilFlag() || IsRetired()) return "" ; |
237 | |
238 | #define HANDLE_V1_BUILTIN_TYPE(t) \ |
239 | if (IsOfType<t>()) { \ |
240 | return #t; \ |
241 | } |
242 | |
243 | HANDLE_V1_BUILTIN_TYPE(bool); |
244 | HANDLE_V1_BUILTIN_TYPE(int32_t); |
245 | HANDLE_V1_BUILTIN_TYPE(int64_t); |
246 | HANDLE_V1_BUILTIN_TYPE(uint64_t); |
247 | HANDLE_V1_BUILTIN_TYPE(double); |
248 | #undef HANDLE_V1_BUILTIN_TYPE |
249 | |
250 | if (IsOfType<std::string>()) { |
251 | return "string" ; |
252 | } |
253 | |
254 | return "" ; |
255 | } |
256 | |
257 | std::string CommandLineFlag::Filename() const { |
258 | return flags_internal::GetUsageConfig().normalize_filename(this->filename); |
259 | } |
260 | |
261 | std::string CommandLineFlag::DefaultValue() const { |
262 | return Unparse(this->marshalling_op, this->def); |
263 | } |
264 | |
265 | std::string CommandLineFlag::CurrentValue() const { |
266 | return Unparse(this->marshalling_op, this->cur); |
267 | } |
268 | |
269 | void CommandLineFlag::SetCallback( |
270 | const flags_internal::FlagCallback mutation_callback) { |
271 | absl::Mutex* mu = InitFlagIfNecessary(this); |
272 | absl::MutexLock l(mu); |
273 | |
274 | callback = mutation_callback; |
275 | |
276 | InvokeCallback(this, mu); |
277 | } |
278 | |
279 | // Attempts to parse supplied `value` string using parsing routine in the `flag` |
280 | // argument. If parsing is successful, it will try to validate that the parsed |
281 | // value is valid for the specified 'flag'. Finally this function stores the |
282 | // parsed value in 'dst' assuming it is a pointer to the flag's value type. In |
283 | // case if any error is encountered in either step, the error message is stored |
284 | // in 'err' |
285 | static bool TryParseLocked(CommandLineFlag* flag, void* dst, |
286 | absl::string_view value, std::string* err) { |
287 | void* tentative_value = Clone(flag->op, flag->def); |
288 | std::string parse_err; |
289 | if (!Parse(flag->marshalling_op, value, tentative_value, &parse_err)) { |
290 | auto type_name = flag->Typename(); |
291 | absl::string_view err_sep = parse_err.empty() ? "" : "; " ; |
292 | absl::string_view typename_sep = type_name.empty() ? "" : " " ; |
293 | *err = absl::StrCat("Illegal value '" , value, "' specified for" , |
294 | typename_sep, type_name, " flag '" , flag->Name(), "'" , |
295 | err_sep, parse_err); |
296 | Delete(flag->op, tentative_value); |
297 | return false; |
298 | } |
299 | |
300 | if (!Validate(flag, tentative_value)) { |
301 | *err = absl::StrCat("Failed validation of new value '" , |
302 | Unparse(flag->marshalling_op, tentative_value), |
303 | "' for flag '" , flag->Name(), "'" ); |
304 | Delete(flag->op, tentative_value); |
305 | return false; |
306 | } |
307 | |
308 | flag->counter++; |
309 | Copy(flag->op, tentative_value, dst); |
310 | Delete(flag->op, tentative_value); |
311 | return true; |
312 | } |
313 | |
314 | // Sets the value of the flag based on specified string `value`. If the flag |
315 | // was successfully set to new value, it returns true. Otherwise, sets `err` |
316 | // to indicate the error, leaves the flag unchanged, and returns false. There |
317 | // are three ways to set the flag's value: |
318 | // * Update the current flag value |
319 | // * Update the flag's default value |
320 | // * Update the current flag value if it was never set before |
321 | // The mode is selected based on 'set_mode' parameter. |
322 | bool CommandLineFlag::SetFromString(absl::string_view value, |
323 | FlagSettingMode set_mode, |
324 | ValueSource source, std::string* err) { |
325 | if (IsRetired()) return false; |
326 | |
327 | UpdateModifiedBit(this); |
328 | |
329 | absl::Mutex* mu = InitFlagIfNecessary(this); |
330 | absl::MutexLock l(mu); |
331 | |
332 | switch (set_mode) { |
333 | case SET_FLAGS_VALUE: { |
334 | // set or modify the flag's value |
335 | if (!TryParseLocked(this, this->cur, value, err)) return false; |
336 | this->modified = true; |
337 | UpdateCopy(this, mu); |
338 | |
339 | if (source == kCommandLine) { |
340 | this->on_command_line = true; |
341 | } |
342 | break; |
343 | } |
344 | case SET_FLAG_IF_DEFAULT: { |
345 | // set the flag's value, but only if it hasn't been set by someone else |
346 | if (!this->modified) { |
347 | if (!TryParseLocked(this, this->cur, value, err)) return false; |
348 | this->modified = true; |
349 | UpdateCopy(this, mu); |
350 | } else { |
351 | // TODO(rogeeff): review and fix this semantic. Currently we do not fail |
352 | // in this case if flag is modified. This is misleading since the flag's |
353 | // value is not updated even though we return true. |
354 | // *err = absl::StrCat(this->Name(), " is already set to ", |
355 | // CurrentValue(), "\n"); |
356 | // return false; |
357 | return true; |
358 | } |
359 | break; |
360 | } |
361 | case SET_FLAGS_DEFAULT: { |
362 | // modify the flag's default-value |
363 | if (!TryParseLocked(this, this->def, value, err)) return false; |
364 | |
365 | if (!this->modified) { |
366 | // Need to set both defvalue *and* current, in this case |
367 | Copy(this->op, this->def, this->cur); |
368 | UpdateCopy(this, mu); |
369 | } |
370 | break; |
371 | } |
372 | default: { |
373 | // unknown set_mode |
374 | assert(false); |
375 | return false; |
376 | } |
377 | } |
378 | |
379 | return true; |
380 | } |
381 | |
382 | } // namespace flags_internal |
383 | } // namespace absl |
384 | |