commandlineflag.cc
Go to the documentation of this file.
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 
17 
18 #include <cassert>
19 
21 #include "absl/base/optimization.h"
22 #include "absl/flags/config.h"
24 #include "absl/strings/str_cat.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;
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  }
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.
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.
161  if (!flag->IsAbseilFlag()) {
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 {
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)) {
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)) {
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 
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 
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 
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.
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
void Delete(FlagOpFn op, const void *obj)
#define EXCLUSIVE_LOCKS_REQUIRED(...)
bool Parse(FlagMarshallingOpFn op, absl::string_view text, void *dst, std::string *error)
#define STORE_ATOMIC(T)
static bool TryParseLocked(CommandLineFlag *flag, void *dst, absl::string_view value, std::string *err)
struct CommandLineFlagLocks * locks
void UpdateModifiedBit(CommandLineFlag *flag)
#define ABSL_CONST_INIT
Definition: attributes.h:605
bool Validate(CommandLineFlag *, const void *)
void * Clone(FlagOpFn op, const void *obj)
std::string StrCat(const AlphaNum &a, const AlphaNum &b)
Definition: str_cat.cc:98
#define ABSL_PREDICT_FALSE(x)
Definition: optimization.h:177
#define LOCK_RETURNED(x)
void *(*)(FlagOp, const void *, void *) FlagOpFn
#define ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(A)
void CopyConstruct(FlagOpFn op, const void *src, void *dst)
int * counter
Definition: algorithm.h:29
#define DONT_VALIDATE(T)
bool SetFromString(absl::string_view value, flags_internal::FlagSettingMode set_mode, flags_internal::ValueSource source, std::string *error)
#define CHANGED_FOR_TYPE(T)
#define ABSL_INTERNAL_LOG(severity, message)
Definition: raw_logging.h:73
#define HANDLE_V1_BUILTIN_TYPE(t)
void SetCallback(const flags_internal::FlagCallback mutation_callback)
void Unlock() UNLOCK_FUNCTION()
size_t value
static char data[kDataSize]
Definition: city_test.cc:31
void Copy(FlagOpFn op, const void *src, void *dst)
const char kStrippedFlagHelp[]
char name[1]
Definition: mutex.cc:296
FlagsUsageConfig GetUsageConfig()
std::string Unparse(FlagMarshallingOpFn op, const void *val)
uintptr_t size
constexpr bool empty() const noexcept
Definition: string_view.h:277
void Lock() EXCLUSIVE_LOCK_FUNCTION()
absl::string_view Typename() const
uint64_t b
Definition: layout_test.cc:50
void Read(void *dst, const flags_internal::FlagOpFn dst_op) const
void UpdateCopy(CommandLineFlag *flag, absl::Mutex *primary_lock) EXCLUSIVE_LOCKS_REQUIRED(primary_lock)
const FlagMarshallingOpFn marshalling_op
absl::Mutex * InitFlagIfNecessary(CommandLineFlag *flag) LOCK_RETURNED(flag -> locks->primary_mu)
std::function< std::string(absl::string_view)> normalize_filename
Definition: usage_config.h:104
bool ChangedDirectly(CommandLineFlag *flag, const void *a, const void *b)
void Write(const void *src, const flags_internal::FlagOpFn src_op)


abseil_cpp
Author(s):
autogenerated on Mon Feb 28 2022 21:31:18