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/flag.h"
17
18 #include <assert.h>
19 #include <stddef.h>
20 #include <stdint.h>
21 #include <string.h>
22
23 #include <array>
24 #include <atomic>
25 #include <memory>
26 #include <new>
27 #include <string>
28 #include <typeinfo>
29
30 #include "absl/base/call_once.h"
31 #include "absl/base/casts.h"
32 #include "absl/base/config.h"
33 #include "absl/base/optimization.h"
34 #include "absl/flags/config.h"
35 #include "absl/flags/internal/commandlineflag.h"
36 #include "absl/flags/usage_config.h"
37 #include "absl/memory/memory.h"
38 #include "absl/strings/str_cat.h"
39 #include "absl/strings/string_view.h"
40 #include "absl/synchronization/mutex.h"
41
42 namespace absl {
43 ABSL_NAMESPACE_BEGIN
44 namespace flags_internal {
45
46 // The help message indicating that the commandline flag has been
47 // 'stripped'. It will not show up when doing "-help" and its
48 // variants. The flag is stripped if ABSL_FLAGS_STRIP_HELP is set to 1
49 // before including absl/flags/flag.h
50 const char kStrippedFlagHelp[] = "\001\002\003\004 (unknown) \004\003\002\001";
51
52 namespace {
53
54 // Currently we only validate flag values for user-defined flag types.
ShouldValidateFlagValue(FlagFastTypeId flag_type_id)55 bool ShouldValidateFlagValue(FlagFastTypeId flag_type_id) {
56 #define DONT_VALIDATE(T, _) \
57 if (flag_type_id == base_internal::FastTypeId<T>()) return false;
58 ABSL_FLAGS_INTERNAL_SUPPORTED_TYPES(DONT_VALIDATE)
59 #undef DONT_VALIDATE
60
61 return true;
62 }
63
64 // RAII helper used to temporarily unlock and relock `absl::Mutex`.
65 // This is used when we need to ensure that locks are released while
66 // invoking user supplied callbacks and then reacquired, since callbacks may
67 // need to acquire these locks themselves.
68 class MutexRelock {
69 public:
MutexRelock(absl::Mutex & mu)70 explicit MutexRelock(absl::Mutex& mu) : mu_(mu) { mu_.Unlock(); }
~MutexRelock()71 ~MutexRelock() { mu_.Lock(); }
72
73 MutexRelock(const MutexRelock&) = delete;
74 MutexRelock& operator=(const MutexRelock&) = delete;
75
76 private:
77 absl::Mutex& mu_;
78 };
79
80 } // namespace
81
82 ///////////////////////////////////////////////////////////////////////////////
83 // Persistent state of the flag data.
84
85 class FlagImpl;
86
87 class FlagState : public flags_internal::FlagStateInterface {
88 public:
89 template <typename V>
FlagState(FlagImpl & flag_impl,const V & v,bool modified,bool on_command_line,int64_t counter)90 FlagState(FlagImpl& flag_impl, const V& v, bool modified,
91 bool on_command_line, int64_t counter)
92 : flag_impl_(flag_impl),
93 value_(v),
94 modified_(modified),
95 on_command_line_(on_command_line),
96 counter_(counter) {}
97
~FlagState()98 ~FlagState() override {
99 if (flag_impl_.ValueStorageKind() != FlagValueStorageKind::kAlignedBuffer)
100 return;
101 flags_internal::Delete(flag_impl_.op_, value_.heap_allocated);
102 }
103
104 private:
105 friend class FlagImpl;
106
107 // Restores the flag to the saved state.
Restore() const108 void Restore() const override {
109 if (!flag_impl_.RestoreState(*this)) return;
110
111 ABSL_INTERNAL_LOG(INFO,
112 absl::StrCat("Restore saved value of ", flag_impl_.Name(),
113 " to: ", flag_impl_.CurrentValue()));
114 }
115
116 // Flag and saved flag data.
117 FlagImpl& flag_impl_;
118 union SavedValue {
SavedValue(void * v)119 explicit SavedValue(void* v) : heap_allocated(v) {}
SavedValue(int64_t v)120 explicit SavedValue(int64_t v) : one_word(v) {}
SavedValue(flags_internal::AlignedTwoWords v)121 explicit SavedValue(flags_internal::AlignedTwoWords v) : two_words(v) {}
122
123 void* heap_allocated;
124 int64_t one_word;
125 flags_internal::AlignedTwoWords two_words;
126 } value_;
127 bool modified_;
128 bool on_command_line_;
129 int64_t counter_;
130 };
131
132 ///////////////////////////////////////////////////////////////////////////////
133 // Flag implementation, which does not depend on flag value type.
134
DynValueDeleter(FlagOpFn op_arg)135 DynValueDeleter::DynValueDeleter(FlagOpFn op_arg) : op(op_arg) {}
136
operator ()(void * ptr) const137 void DynValueDeleter::operator()(void* ptr) const {
138 if (op == nullptr) return;
139
140 Delete(op, ptr);
141 }
142
Init()143 void FlagImpl::Init() {
144 new (&data_guard_) absl::Mutex;
145
146 auto def_kind = static_cast<FlagDefaultKind>(def_kind_);
147
148 switch (ValueStorageKind()) {
149 case FlagValueStorageKind::kAlignedBuffer:
150 // For this storage kind the default_value_ always points to gen_func
151 // during initialization.
152 assert(def_kind == FlagDefaultKind::kGenFunc);
153 (*default_value_.gen_func)(AlignedBufferValue());
154 break;
155 case FlagValueStorageKind::kOneWordAtomic: {
156 alignas(int64_t) std::array<char, sizeof(int64_t)> buf{};
157 if (def_kind == FlagDefaultKind::kGenFunc) {
158 (*default_value_.gen_func)(buf.data());
159 } else {
160 assert(def_kind != FlagDefaultKind::kDynamicValue);
161 std::memcpy(buf.data(), &default_value_, Sizeof(op_));
162 }
163 OneWordValue().store(absl::bit_cast<int64_t>(buf),
164 std::memory_order_release);
165 break;
166 }
167 case FlagValueStorageKind::kTwoWordsAtomic: {
168 // For this storage kind the default_value_ always points to gen_func
169 // during initialization.
170 assert(def_kind == FlagDefaultKind::kGenFunc);
171 alignas(AlignedTwoWords) std::array<char, sizeof(AlignedTwoWords)> buf{};
172 (*default_value_.gen_func)(buf.data());
173 auto atomic_value = absl::bit_cast<AlignedTwoWords>(buf);
174 TwoWordsValue().store(atomic_value, std::memory_order_release);
175 break;
176 }
177 }
178 }
179
DataGuard() const180 absl::Mutex* FlagImpl::DataGuard() const {
181 absl::call_once(const_cast<FlagImpl*>(this)->init_control_, &FlagImpl::Init,
182 const_cast<FlagImpl*>(this));
183
184 // data_guard_ is initialized inside Init.
185 return reinterpret_cast<absl::Mutex*>(&data_guard_);
186 }
187
AssertValidType(FlagFastTypeId rhs_type_id,const std::type_info * (* gen_rtti)()) const188 void FlagImpl::AssertValidType(FlagFastTypeId rhs_type_id,
189 const std::type_info* (*gen_rtti)()) const {
190 FlagFastTypeId lhs_type_id = flags_internal::FastTypeId(op_);
191
192 // `rhs_type_id` is the fast type id corresponding to the declaration
193 // visibile at the call site. `lhs_type_id` is the fast type id
194 // corresponding to the type specified in flag definition. They must match
195 // for this operation to be well-defined.
196 if (ABSL_PREDICT_TRUE(lhs_type_id == rhs_type_id)) return;
197
198 const std::type_info* lhs_runtime_type_id =
199 flags_internal::RuntimeTypeId(op_);
200 const std::type_info* rhs_runtime_type_id = (*gen_rtti)();
201
202 if (lhs_runtime_type_id == rhs_runtime_type_id) return;
203
204 #if defined(ABSL_FLAGS_INTERNAL_HAS_RTTI)
205 if (*lhs_runtime_type_id == *rhs_runtime_type_id) return;
206 #endif
207
208 ABSL_INTERNAL_LOG(
209 FATAL, absl::StrCat("Flag '", Name(),
210 "' is defined as one type and declared as another"));
211 }
212
MakeInitValue() const213 std::unique_ptr<void, DynValueDeleter> FlagImpl::MakeInitValue() const {
214 void* res = nullptr;
215 switch (DefaultKind()) {
216 case FlagDefaultKind::kDynamicValue:
217 res = flags_internal::Clone(op_, default_value_.dynamic_value);
218 break;
219 case FlagDefaultKind::kGenFunc:
220 res = flags_internal::Alloc(op_);
221 (*default_value_.gen_func)(res);
222 break;
223 default:
224 res = flags_internal::Clone(op_, &default_value_);
225 break;
226 }
227 return {res, DynValueDeleter{op_}};
228 }
229
StoreValue(const void * src)230 void FlagImpl::StoreValue(const void* src) {
231 switch (ValueStorageKind()) {
232 case FlagValueStorageKind::kAlignedBuffer:
233 Copy(op_, src, AlignedBufferValue());
234 break;
235 case FlagValueStorageKind::kOneWordAtomic: {
236 int64_t one_word_val = 0;
237 std::memcpy(&one_word_val, src, Sizeof(op_));
238 OneWordValue().store(one_word_val, std::memory_order_release);
239 break;
240 }
241 case FlagValueStorageKind::kTwoWordsAtomic: {
242 AlignedTwoWords two_words_val{0, 0};
243 std::memcpy(&two_words_val, src, Sizeof(op_));
244 TwoWordsValue().store(two_words_val, std::memory_order_release);
245 break;
246 }
247 }
248
249 modified_ = true;
250 ++counter_;
251 InvokeCallback();
252 }
253
Name() const254 absl::string_view FlagImpl::Name() const { return name_; }
255
Filename() const256 std::string FlagImpl::Filename() const {
257 return flags_internal::GetUsageConfig().normalize_filename(filename_);
258 }
259
Help() const260 std::string FlagImpl::Help() const {
261 return HelpSourceKind() == FlagHelpKind::kLiteral ? help_.literal
262 : help_.gen_func();
263 }
264
TypeId() const265 FlagFastTypeId FlagImpl::TypeId() const {
266 return flags_internal::FastTypeId(op_);
267 }
268
IsSpecifiedOnCommandLine() const269 bool FlagImpl::IsSpecifiedOnCommandLine() const {
270 absl::MutexLock l(DataGuard());
271 return on_command_line_;
272 }
273
DefaultValue() const274 std::string FlagImpl::DefaultValue() const {
275 absl::MutexLock l(DataGuard());
276
277 auto obj = MakeInitValue();
278 return flags_internal::Unparse(op_, obj.get());
279 }
280
CurrentValue() const281 std::string FlagImpl::CurrentValue() const {
282 auto* guard = DataGuard(); // Make sure flag initialized
283 switch (ValueStorageKind()) {
284 case FlagValueStorageKind::kAlignedBuffer: {
285 absl::MutexLock l(guard);
286 return flags_internal::Unparse(op_, AlignedBufferValue());
287 }
288 case FlagValueStorageKind::kOneWordAtomic: {
289 const auto one_word_val =
290 absl::bit_cast<std::array<char, sizeof(int64_t)>>(
291 OneWordValue().load(std::memory_order_acquire));
292 return flags_internal::Unparse(op_, one_word_val.data());
293 }
294 case FlagValueStorageKind::kTwoWordsAtomic: {
295 const auto two_words_val =
296 absl::bit_cast<std::array<char, sizeof(AlignedTwoWords)>>(
297 TwoWordsValue().load(std::memory_order_acquire));
298 return flags_internal::Unparse(op_, two_words_val.data());
299 }
300 }
301
302 return "";
303 }
304
SetCallback(const FlagCallbackFunc mutation_callback)305 void FlagImpl::SetCallback(const FlagCallbackFunc mutation_callback) {
306 absl::MutexLock l(DataGuard());
307
308 if (callback_ == nullptr) {
309 callback_ = new FlagCallback;
310 }
311 callback_->func = mutation_callback;
312
313 InvokeCallback();
314 }
315
InvokeCallback() const316 void FlagImpl::InvokeCallback() const {
317 if (!callback_) return;
318
319 // Make a copy of the C-style function pointer that we are about to invoke
320 // before we release the lock guarding it.
321 FlagCallbackFunc cb = callback_->func;
322
323 // If the flag has a mutation callback this function invokes it. While the
324 // callback is being invoked the primary flag's mutex is unlocked and it is
325 // re-locked back after call to callback is completed. Callback invocation is
326 // guarded by flag's secondary mutex instead which prevents concurrent
327 // callback invocation. Note that it is possible for other thread to grab the
328 // primary lock and update flag's value at any time during the callback
329 // invocation. This is by design. Callback can get a value of the flag if
330 // necessary, but it might be different from the value initiated the callback
331 // and it also can be different by the time the callback invocation is
332 // completed. Requires that *primary_lock be held in exclusive mode; it may be
333 // released and reacquired by the implementation.
334 MutexRelock relock(*DataGuard());
335 absl::MutexLock lock(&callback_->guard);
336 cb();
337 }
338
SaveState()339 std::unique_ptr<FlagStateInterface> FlagImpl::SaveState() {
340 absl::MutexLock l(DataGuard());
341
342 bool modified = modified_;
343 bool on_command_line = on_command_line_;
344 switch (ValueStorageKind()) {
345 case FlagValueStorageKind::kAlignedBuffer: {
346 return absl::make_unique<FlagState>(
347 *this, flags_internal::Clone(op_, AlignedBufferValue()), modified,
348 on_command_line, counter_);
349 }
350 case FlagValueStorageKind::kOneWordAtomic: {
351 return absl::make_unique<FlagState>(
352 *this, OneWordValue().load(std::memory_order_acquire), modified,
353 on_command_line, counter_);
354 }
355 case FlagValueStorageKind::kTwoWordsAtomic: {
356 return absl::make_unique<FlagState>(
357 *this, TwoWordsValue().load(std::memory_order_acquire), modified,
358 on_command_line, counter_);
359 }
360 }
361 return nullptr;
362 }
363
RestoreState(const FlagState & flag_state)364 bool FlagImpl::RestoreState(const FlagState& flag_state) {
365 absl::MutexLock l(DataGuard());
366
367 if (flag_state.counter_ == counter_) {
368 return false;
369 }
370
371 switch (ValueStorageKind()) {
372 case FlagValueStorageKind::kAlignedBuffer:
373 StoreValue(flag_state.value_.heap_allocated);
374 break;
375 case FlagValueStorageKind::kOneWordAtomic:
376 StoreValue(&flag_state.value_.one_word);
377 break;
378 case FlagValueStorageKind::kTwoWordsAtomic:
379 StoreValue(&flag_state.value_.two_words);
380 break;
381 }
382
383 modified_ = flag_state.modified_;
384 on_command_line_ = flag_state.on_command_line_;
385
386 return true;
387 }
388
389 template <typename StorageT>
OffsetValue() const390 StorageT* FlagImpl::OffsetValue() const {
391 char* p = reinterpret_cast<char*>(const_cast<FlagImpl*>(this));
392 // The offset is deduced via Flag value type specific op_.
393 size_t offset = flags_internal::ValueOffset(op_);
394
395 return reinterpret_cast<StorageT*>(p + offset);
396 }
397
AlignedBufferValue() const398 void* FlagImpl::AlignedBufferValue() const {
399 assert(ValueStorageKind() == FlagValueStorageKind::kAlignedBuffer);
400 return OffsetValue<void>();
401 }
402
OneWordValue() const403 std::atomic<int64_t>& FlagImpl::OneWordValue() const {
404 assert(ValueStorageKind() == FlagValueStorageKind::kOneWordAtomic);
405 return OffsetValue<FlagOneWordValue>()->value;
406 }
407
TwoWordsValue() const408 std::atomic<AlignedTwoWords>& FlagImpl::TwoWordsValue() const {
409 assert(ValueStorageKind() == FlagValueStorageKind::kTwoWordsAtomic);
410 return OffsetValue<FlagTwoWordsValue>()->value;
411 }
412
413 // Attempts to parse supplied `value` string using parsing routine in the `flag`
414 // argument. If parsing successful, this function replaces the dst with newly
415 // parsed value. In case if any error is encountered in either step, the error
416 // message is stored in 'err'
TryParse(absl::string_view value,std::string & err) const417 std::unique_ptr<void, DynValueDeleter> FlagImpl::TryParse(
418 absl::string_view value, std::string& err) const {
419 std::unique_ptr<void, DynValueDeleter> tentative_value = MakeInitValue();
420
421 std::string parse_err;
422 if (!flags_internal::Parse(op_, value, tentative_value.get(), &parse_err)) {
423 absl::string_view err_sep = parse_err.empty() ? "" : "; ";
424 err = absl::StrCat("Illegal value '", value, "' specified for flag '",
425 Name(), "'", err_sep, parse_err);
426 return nullptr;
427 }
428
429 return tentative_value;
430 }
431
Read(void * dst) const432 void FlagImpl::Read(void* dst) const {
433 auto* guard = DataGuard(); // Make sure flag initialized
434 switch (ValueStorageKind()) {
435 case FlagValueStorageKind::kAlignedBuffer: {
436 absl::MutexLock l(guard);
437 flags_internal::CopyConstruct(op_, AlignedBufferValue(), dst);
438 break;
439 }
440 case FlagValueStorageKind::kOneWordAtomic: {
441 const int64_t one_word_val =
442 OneWordValue().load(std::memory_order_acquire);
443 std::memcpy(dst, &one_word_val, Sizeof(op_));
444 break;
445 }
446 case FlagValueStorageKind::kTwoWordsAtomic: {
447 const AlignedTwoWords two_words_val =
448 TwoWordsValue().load(std::memory_order_acquire);
449 std::memcpy(dst, &two_words_val, Sizeof(op_));
450 break;
451 }
452 }
453 }
454
Write(const void * src)455 void FlagImpl::Write(const void* src) {
456 absl::MutexLock l(DataGuard());
457
458 if (ShouldValidateFlagValue(flags_internal::FastTypeId(op_))) {
459 std::unique_ptr<void, DynValueDeleter> obj{flags_internal::Clone(op_, src),
460 DynValueDeleter{op_}};
461 std::string ignored_error;
462 std::string src_as_str = flags_internal::Unparse(op_, src);
463 if (!flags_internal::Parse(op_, src_as_str, obj.get(), &ignored_error)) {
464 ABSL_INTERNAL_LOG(ERROR, absl::StrCat("Attempt to set flag '", Name(),
465 "' to invalid value ", src_as_str));
466 }
467 }
468
469 StoreValue(src);
470 }
471
472 // Sets the value of the flag based on specified string `value`. If the flag
473 // was successfully set to new value, it returns true. Otherwise, sets `err`
474 // to indicate the error, leaves the flag unchanged, and returns false. There
475 // are three ways to set the flag's value:
476 // * Update the current flag value
477 // * Update the flag's default value
478 // * Update the current flag value if it was never set before
479 // The mode is selected based on 'set_mode' parameter.
ParseFrom(absl::string_view value,FlagSettingMode set_mode,ValueSource source,std::string & err)480 bool FlagImpl::ParseFrom(absl::string_view value, FlagSettingMode set_mode,
481 ValueSource source, std::string& err) {
482 absl::MutexLock l(DataGuard());
483
484 switch (set_mode) {
485 case SET_FLAGS_VALUE: {
486 // set or modify the flag's value
487 auto tentative_value = TryParse(value, err);
488 if (!tentative_value) return false;
489
490 StoreValue(tentative_value.get());
491
492 if (source == kCommandLine) {
493 on_command_line_ = true;
494 }
495 break;
496 }
497 case SET_FLAG_IF_DEFAULT: {
498 // set the flag's value, but only if it hasn't been set by someone else
499 if (modified_) {
500 // TODO(rogeeff): review and fix this semantic. Currently we do not fail
501 // in this case if flag is modified. This is misleading since the flag's
502 // value is not updated even though we return true.
503 // *err = absl::StrCat(Name(), " is already set to ",
504 // CurrentValue(), "\n");
505 // return false;
506 return true;
507 }
508 auto tentative_value = TryParse(value, err);
509 if (!tentative_value) return false;
510
511 StoreValue(tentative_value.get());
512 break;
513 }
514 case SET_FLAGS_DEFAULT: {
515 auto tentative_value = TryParse(value, err);
516 if (!tentative_value) return false;
517
518 if (DefaultKind() == FlagDefaultKind::kDynamicValue) {
519 void* old_value = default_value_.dynamic_value;
520 default_value_.dynamic_value = tentative_value.release();
521 tentative_value.reset(old_value);
522 } else {
523 default_value_.dynamic_value = tentative_value.release();
524 def_kind_ = static_cast<uint8_t>(FlagDefaultKind::kDynamicValue);
525 }
526
527 if (!modified_) {
528 // Need to set both default value *and* current, in this case.
529 StoreValue(default_value_.dynamic_value);
530 modified_ = false;
531 }
532 break;
533 }
534 }
535
536 return true;
537 }
538
CheckDefaultValueParsingRoundtrip() const539 void FlagImpl::CheckDefaultValueParsingRoundtrip() const {
540 std::string v = DefaultValue();
541
542 absl::MutexLock lock(DataGuard());
543
544 auto dst = MakeInitValue();
545 std::string error;
546 if (!flags_internal::Parse(op_, v, dst.get(), &error)) {
547 ABSL_INTERNAL_LOG(
548 FATAL,
549 absl::StrCat("Flag ", Name(), " (from ", Filename(),
550 "): string form of default value '", v,
551 "' could not be parsed; error=", error));
552 }
553
554 // We do not compare dst to def since parsing/unparsing may make
555 // small changes, e.g., precision loss for floating point types.
556 }
557
ValidateInputValue(absl::string_view value) const558 bool FlagImpl::ValidateInputValue(absl::string_view value) const {
559 absl::MutexLock l(DataGuard());
560
561 auto obj = MakeInitValue();
562 std::string ignored_error;
563 return flags_internal::Parse(op_, value, obj.get(), &ignored_error);
564 }
565
566 } // namespace flags_internal
567 ABSL_NAMESPACE_END
568 } // namespace absl
569