flag.cc 21 KB

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