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
27namespace absl {
28namespace 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
36const char kStrippedFlagHelp[] = "\001\002\003\004 (unknown) \004\003\002\001";
37
38namespace {
39
40void 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.
58void 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.
79bool 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.
94void 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.
111absl::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.
139bool 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.
160void 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
169bool Validate(CommandLineFlag*, const void*) {
170 return true;
171}
172
173std::string HelpText::GetHelpText() const {
174 if (help_function_) return help_function_();
175 if (help_message_) return help_message_;
176
177 return {};
178}
179
180const int64_t CommandLineFlag::kAtomicInit;
181
182void 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
199void 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
233absl::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
257std::string CommandLineFlag::Filename() const {
258 return flags_internal::GetUsageConfig().normalize_filename(this->filename);
259}
260
261std::string CommandLineFlag::DefaultValue() const {
262 return Unparse(this->marshalling_op, this->def);
263}
264
265std::string CommandLineFlag::CurrentValue() const {
266 return Unparse(this->marshalling_op, this->cur);
267}
268
269void 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'
285static 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.
322bool 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