/src/abseil-cpp/absl/flags/internal/flag.cc
Line | Count | Source (jump to first uncovered line) |
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 <cstring> |
26 | | #include <memory> |
27 | | #include <string> |
28 | | #include <typeinfo> |
29 | | #include <vector> |
30 | | |
31 | | #include "absl/base/attributes.h" |
32 | | #include "absl/base/call_once.h" |
33 | | #include "absl/base/casts.h" |
34 | | #include "absl/base/config.h" |
35 | | #include "absl/base/const_init.h" |
36 | | #include "absl/base/dynamic_annotations.h" |
37 | | #include "absl/base/optimization.h" |
38 | | #include "absl/flags/config.h" |
39 | | #include "absl/flags/internal/commandlineflag.h" |
40 | | #include "absl/flags/usage_config.h" |
41 | | #include "absl/memory/memory.h" |
42 | | #include "absl/strings/str_cat.h" |
43 | | #include "absl/strings/string_view.h" |
44 | | #include "absl/synchronization/mutex.h" |
45 | | |
46 | | namespace absl { |
47 | | ABSL_NAMESPACE_BEGIN |
48 | | namespace flags_internal { |
49 | | |
50 | | // The help message indicating that the commandline flag has been stripped. It |
51 | | // will not show up when doing "-help" and its variants. The flag is stripped |
52 | | // if ABSL_FLAGS_STRIP_HELP is set to 1 before including absl/flags/flag.h |
53 | | const char kStrippedFlagHelp[] = "\001\002\003\004 (unknown) \004\003\002\001"; |
54 | | |
55 | | namespace { |
56 | | |
57 | | // Currently we only validate flag values for user-defined flag types. |
58 | 0 | bool ShouldValidateFlagValue(FlagFastTypeId flag_type_id) { |
59 | 0 | #define DONT_VALIDATE(T, _) \ |
60 | 0 | if (flag_type_id == base_internal::FastTypeId<T>()) return false; |
61 | 0 | ABSL_FLAGS_INTERNAL_SUPPORTED_TYPES(DONT_VALIDATE) |
62 | 0 | #undef DONT_VALIDATE |
63 | | |
64 | 0 | return true; |
65 | 0 | } |
66 | | |
67 | | // RAII helper used to temporarily unlock and relock `absl::Mutex`. |
68 | | // This is used when we need to ensure that locks are released while |
69 | | // invoking user supplied callbacks and then reacquired, since callbacks may |
70 | | // need to acquire these locks themselves. |
71 | | class MutexRelock { |
72 | | public: |
73 | 0 | explicit MutexRelock(absl::Mutex& mu) : mu_(mu) { mu_.Unlock(); } |
74 | 0 | ~MutexRelock() { mu_.Lock(); } |
75 | | |
76 | | MutexRelock(const MutexRelock&) = delete; |
77 | | MutexRelock& operator=(const MutexRelock&) = delete; |
78 | | |
79 | | private: |
80 | | absl::Mutex& mu_; |
81 | | }; |
82 | | |
83 | | // This is a freelist of leaked flag values and guard for its access. |
84 | | // When we can't guarantee it is safe to reuse the memory for flag values, |
85 | | // we move the memory to the freelist where it lives indefinitely, so it can |
86 | | // still be safely accessed. This also prevents leak checkers from complaining |
87 | | // about the leaked memory that can no longer be accessed through any pointer. |
88 | | ABSL_CONST_INIT absl::Mutex s_freelist_guard(absl::kConstInit); |
89 | | ABSL_CONST_INIT std::vector<void*>* s_freelist = nullptr; |
90 | | |
91 | 0 | void AddToFreelist(void* p) { |
92 | 0 | absl::MutexLock l(&s_freelist_guard); |
93 | 0 | if (!s_freelist) { |
94 | 0 | s_freelist = new std::vector<void*>; |
95 | 0 | } |
96 | 0 | s_freelist->push_back(p); |
97 | 0 | } |
98 | | |
99 | | } // namespace |
100 | | |
101 | | /////////////////////////////////////////////////////////////////////////////// |
102 | | |
103 | 0 | uint64_t NumLeakedFlagValues() { |
104 | 0 | absl::MutexLock l(&s_freelist_guard); |
105 | 0 | return s_freelist == nullptr ? 0u : s_freelist->size(); |
106 | 0 | } |
107 | | |
108 | | /////////////////////////////////////////////////////////////////////////////// |
109 | | // Persistent state of the flag data. |
110 | | |
111 | | class FlagImpl; |
112 | | |
113 | | class FlagState : public flags_internal::FlagStateInterface { |
114 | | public: |
115 | | template <typename V> |
116 | | FlagState(FlagImpl& flag_impl, const V& v, bool modified, |
117 | | bool on_command_line, int64_t counter) |
118 | | : flag_impl_(flag_impl), |
119 | | value_(v), |
120 | | modified_(modified), |
121 | | on_command_line_(on_command_line), |
122 | 0 | counter_(counter) {} Unexecuted instantiation: absl::flags_internal::FlagState::FlagState<long>(absl::flags_internal::FlagImpl&, long const&, bool, bool, long) Unexecuted instantiation: absl::flags_internal::FlagState::FlagState<void*>(absl::flags_internal::FlagImpl&, void* const&, bool, bool, long) |
123 | | |
124 | 0 | ~FlagState() override { |
125 | 0 | if (flag_impl_.ValueStorageKind() != FlagValueStorageKind::kHeapAllocated && |
126 | 0 | flag_impl_.ValueStorageKind() != FlagValueStorageKind::kSequenceLocked) |
127 | 0 | return; |
128 | 0 | flags_internal::Delete(flag_impl_.op_, value_.heap_allocated); |
129 | 0 | } |
130 | | |
131 | | private: |
132 | | friend class FlagImpl; |
133 | | |
134 | | // Restores the flag to the saved state. |
135 | 0 | void Restore() const override { |
136 | 0 | if (!flag_impl_.RestoreState(*this)) return; |
137 | | |
138 | 0 | ABSL_INTERNAL_LOG(INFO, |
139 | 0 | absl::StrCat("Restore saved value of ", flag_impl_.Name(), |
140 | 0 | " to: ", flag_impl_.CurrentValue())); |
141 | 0 | } |
142 | | |
143 | | // Flag and saved flag data. |
144 | | FlagImpl& flag_impl_; |
145 | | union SavedValue { |
146 | 0 | explicit SavedValue(void* v) : heap_allocated(v) {} |
147 | 0 | explicit SavedValue(int64_t v) : one_word(v) {} |
148 | | |
149 | | void* heap_allocated; |
150 | | int64_t one_word; |
151 | | } value_; |
152 | | bool modified_; |
153 | | bool on_command_line_; |
154 | | int64_t counter_; |
155 | | }; |
156 | | |
157 | | /////////////////////////////////////////////////////////////////////////////// |
158 | | // Flag implementation, which does not depend on flag value type. |
159 | | |
160 | 0 | DynValueDeleter::DynValueDeleter(FlagOpFn op_arg) : op(op_arg) {} |
161 | | |
162 | 0 | void DynValueDeleter::operator()(void* ptr) const { |
163 | 0 | if (op == nullptr) return; |
164 | | |
165 | 0 | Delete(op, ptr); |
166 | 0 | } |
167 | | |
168 | 0 | MaskedPointer::MaskedPointer(ptr_t rhs, bool is_candidate) : ptr_(rhs) { |
169 | 0 | if (is_candidate) { |
170 | 0 | ApplyMask(kUnprotectedReadCandidate); |
171 | 0 | } |
172 | 0 | } |
173 | | |
174 | 0 | bool MaskedPointer::IsUnprotectedReadCandidate() const { |
175 | 0 | return CheckMask(kUnprotectedReadCandidate); |
176 | 0 | } |
177 | | |
178 | 0 | bool MaskedPointer::HasBeenRead() const { return CheckMask(kHasBeenRead); } |
179 | | |
180 | 0 | void MaskedPointer::Set(FlagOpFn op, const void* src, bool is_candidate) { |
181 | 0 | flags_internal::Copy(op, src, Ptr()); |
182 | 0 | if (is_candidate) { |
183 | 0 | ApplyMask(kUnprotectedReadCandidate); |
184 | 0 | } |
185 | 0 | } |
186 | 0 | void MaskedPointer::MarkAsRead() { ApplyMask(kHasBeenRead); } |
187 | | |
188 | 0 | void MaskedPointer::ApplyMask(mask_t mask) { |
189 | 0 | ptr_ = reinterpret_cast<ptr_t>(reinterpret_cast<mask_t>(ptr_) | mask); |
190 | 0 | } |
191 | 0 | bool MaskedPointer::CheckMask(mask_t mask) const { |
192 | 0 | return (reinterpret_cast<mask_t>(ptr_) & mask) != 0; |
193 | 0 | } |
194 | | |
195 | 1 | void FlagImpl::Init() { |
196 | 1 | new (&data_guard_) absl::Mutex; |
197 | | |
198 | 1 | auto def_kind = static_cast<FlagDefaultKind>(def_kind_); |
199 | | |
200 | 1 | switch (ValueStorageKind()) { |
201 | 1 | case FlagValueStorageKind::kValueAndInitBit: |
202 | 1 | case FlagValueStorageKind::kOneWordAtomic: { |
203 | 1 | alignas(int64_t) std::array<char, sizeof(int64_t)> buf{}; |
204 | 1 | if (def_kind == FlagDefaultKind::kGenFunc) { |
205 | 0 | (*default_value_.gen_func)(buf.data()); |
206 | 1 | } else { |
207 | 1 | assert(def_kind != FlagDefaultKind::kDynamicValue); |
208 | 1 | std::memcpy(buf.data(), &default_value_, Sizeof(op_)); |
209 | 1 | } |
210 | 1 | if (ValueStorageKind() == FlagValueStorageKind::kValueAndInitBit) { |
211 | | // We presume here the memory layout of FlagValueAndInitBit struct. |
212 | 1 | uint8_t initialized = 1; |
213 | 1 | std::memcpy(buf.data() + Sizeof(op_), &initialized, |
214 | 1 | sizeof(initialized)); |
215 | 1 | } |
216 | | // Type can contain valid uninitialized bits, e.g. padding. |
217 | 1 | ABSL_ANNOTATE_MEMORY_IS_INITIALIZED(buf.data(), buf.size()); |
218 | 1 | OneWordValue().store(absl::bit_cast<int64_t>(buf), |
219 | 1 | std::memory_order_release); |
220 | 1 | break; |
221 | 1 | } |
222 | 0 | case FlagValueStorageKind::kSequenceLocked: { |
223 | | // For this storage kind the default_value_ always points to gen_func |
224 | | // during initialization. |
225 | 0 | assert(def_kind == FlagDefaultKind::kGenFunc); |
226 | 0 | (*default_value_.gen_func)(AtomicBufferValue()); |
227 | 0 | break; |
228 | 0 | } |
229 | 0 | case FlagValueStorageKind::kHeapAllocated: |
230 | | // For this storage kind the default_value_ always points to gen_func |
231 | | // during initialization. |
232 | 0 | assert(def_kind == FlagDefaultKind::kGenFunc); |
233 | | // Flag value initially points to the internal buffer. |
234 | 0 | MaskedPointer ptr_value = PtrStorage().load(std::memory_order_acquire); |
235 | 0 | (*default_value_.gen_func)(ptr_value.Ptr()); |
236 | | // Default value is a candidate for an unprotected read. |
237 | 0 | PtrStorage().store(MaskedPointer(ptr_value.Ptr(), true), |
238 | 0 | std::memory_order_release); |
239 | 0 | break; |
240 | 1 | } |
241 | 1 | seq_lock_.MarkInitialized(); |
242 | 1 | } |
243 | | |
244 | 1 | absl::Mutex* FlagImpl::DataGuard() const { |
245 | 1 | absl::call_once(const_cast<FlagImpl*>(this)->init_control_, &FlagImpl::Init, |
246 | 1 | const_cast<FlagImpl*>(this)); |
247 | | |
248 | | // data_guard_ is initialized inside Init. |
249 | 1 | return reinterpret_cast<absl::Mutex*>(&data_guard_); |
250 | 1 | } |
251 | | |
252 | | void FlagImpl::AssertValidType(FlagFastTypeId rhs_type_id, |
253 | 7.21k | const std::type_info* (*gen_rtti)()) const { |
254 | 7.21k | FlagFastTypeId lhs_type_id = flags_internal::FastTypeId(op_); |
255 | | |
256 | | // `rhs_type_id` is the fast type id corresponding to the declaration |
257 | | // visible at the call site. `lhs_type_id` is the fast type id |
258 | | // corresponding to the type specified in flag definition. They must match |
259 | | // for this operation to be well-defined. |
260 | 7.21k | if (ABSL_PREDICT_TRUE(lhs_type_id == rhs_type_id)) return; |
261 | | |
262 | 0 | const std::type_info* lhs_runtime_type_id = |
263 | 0 | flags_internal::RuntimeTypeId(op_); |
264 | 0 | const std::type_info* rhs_runtime_type_id = (*gen_rtti)(); |
265 | |
|
266 | 0 | if (lhs_runtime_type_id == rhs_runtime_type_id) return; |
267 | | |
268 | 0 | #ifdef ABSL_INTERNAL_HAS_RTTI |
269 | 0 | if (*lhs_runtime_type_id == *rhs_runtime_type_id) return; |
270 | 0 | #endif |
271 | | |
272 | 0 | ABSL_INTERNAL_LOG( |
273 | 0 | FATAL, absl::StrCat("Flag '", Name(), |
274 | 0 | "' is defined as one type and declared as another")); |
275 | 0 | } |
276 | | |
277 | 0 | std::unique_ptr<void, DynValueDeleter> FlagImpl::MakeInitValue() const { |
278 | 0 | void* res = nullptr; |
279 | 0 | switch (DefaultKind()) { |
280 | 0 | case FlagDefaultKind::kDynamicValue: |
281 | 0 | res = flags_internal::Clone(op_, default_value_.dynamic_value); |
282 | 0 | break; |
283 | 0 | case FlagDefaultKind::kGenFunc: |
284 | 0 | res = flags_internal::Alloc(op_); |
285 | 0 | (*default_value_.gen_func)(res); |
286 | 0 | break; |
287 | 0 | default: |
288 | 0 | res = flags_internal::Clone(op_, &default_value_); |
289 | 0 | break; |
290 | 0 | } |
291 | 0 | return {res, DynValueDeleter{op_}}; |
292 | 0 | } |
293 | | |
294 | 0 | void FlagImpl::StoreValue(const void* src, ValueSource source) { |
295 | 0 | switch (ValueStorageKind()) { |
296 | 0 | case FlagValueStorageKind::kValueAndInitBit: |
297 | 0 | case FlagValueStorageKind::kOneWordAtomic: { |
298 | | // Load the current value to avoid setting 'init' bit manually. |
299 | 0 | int64_t one_word_val = OneWordValue().load(std::memory_order_acquire); |
300 | 0 | std::memcpy(&one_word_val, src, Sizeof(op_)); |
301 | 0 | OneWordValue().store(one_word_val, std::memory_order_release); |
302 | 0 | seq_lock_.IncrementModificationCount(); |
303 | 0 | break; |
304 | 0 | } |
305 | 0 | case FlagValueStorageKind::kSequenceLocked: { |
306 | 0 | seq_lock_.Write(AtomicBufferValue(), src, Sizeof(op_)); |
307 | 0 | break; |
308 | 0 | } |
309 | 0 | case FlagValueStorageKind::kHeapAllocated: |
310 | 0 | MaskedPointer ptr_value = PtrStorage().load(std::memory_order_acquire); |
311 | |
|
312 | 0 | if (ptr_value.IsUnprotectedReadCandidate() && ptr_value.HasBeenRead()) { |
313 | | // If current value is a candidate for an unprotected read and if it was |
314 | | // already read at least once, follow up reads (if any) are done without |
315 | | // mutex protection. We can't guarantee it is safe to reuse this memory |
316 | | // since it may have been accessed by another thread concurrently, so |
317 | | // instead we move the memory to a freelist so it can still be safely |
318 | | // accessed, and allocate a new one for the new value. |
319 | 0 | AddToFreelist(ptr_value.Ptr()); |
320 | 0 | ptr_value = MaskedPointer(Clone(op_, src), source == kCommandLine); |
321 | 0 | } else { |
322 | | // Current value either was set programmatically or was never read. |
323 | | // We can reuse the memory since all accesses to this value (if any) |
324 | | // were protected by mutex. That said, if a new value comes from command |
325 | | // line it now becomes a candidate for an unprotected read. |
326 | 0 | ptr_value.Set(op_, src, source == kCommandLine); |
327 | 0 | } |
328 | |
|
329 | 0 | PtrStorage().store(ptr_value, std::memory_order_release); |
330 | 0 | seq_lock_.IncrementModificationCount(); |
331 | 0 | break; |
332 | 0 | } |
333 | 0 | modified_ = true; |
334 | 0 | InvokeCallback(); |
335 | 0 | } |
336 | | |
337 | 16 | absl::string_view FlagImpl::Name() const { return name_; } |
338 | | |
339 | 16 | std::string FlagImpl::Filename() const { |
340 | 16 | return flags_internal::GetUsageConfig().normalize_filename(filename_); |
341 | 16 | } |
342 | | |
343 | 0 | std::string FlagImpl::Help() const { |
344 | 0 | return HelpSourceKind() == FlagHelpKind::kLiteral ? help_.literal |
345 | 0 | : help_.gen_func(); |
346 | 0 | } |
347 | | |
348 | 0 | FlagFastTypeId FlagImpl::TypeId() const { |
349 | 0 | return flags_internal::FastTypeId(op_); |
350 | 0 | } |
351 | | |
352 | 0 | int64_t FlagImpl::ModificationCount() const { |
353 | 0 | return seq_lock_.ModificationCount(); |
354 | 0 | } |
355 | | |
356 | 0 | bool FlagImpl::IsSpecifiedOnCommandLine() const { |
357 | 0 | absl::MutexLock l(DataGuard()); |
358 | 0 | return on_command_line_; |
359 | 0 | } |
360 | | |
361 | 0 | std::string FlagImpl::DefaultValue() const { |
362 | 0 | absl::MutexLock l(DataGuard()); |
363 | |
|
364 | 0 | auto obj = MakeInitValue(); |
365 | 0 | return flags_internal::Unparse(op_, obj.get()); |
366 | 0 | } |
367 | | |
368 | 0 | std::string FlagImpl::CurrentValue() const { |
369 | 0 | auto* guard = DataGuard(); // Make sure flag initialized |
370 | 0 | switch (ValueStorageKind()) { |
371 | 0 | case FlagValueStorageKind::kValueAndInitBit: |
372 | 0 | case FlagValueStorageKind::kOneWordAtomic: { |
373 | 0 | const auto one_word_val = |
374 | 0 | absl::bit_cast<std::array<char, sizeof(int64_t)>>( |
375 | 0 | OneWordValue().load(std::memory_order_acquire)); |
376 | 0 | return flags_internal::Unparse(op_, one_word_val.data()); |
377 | 0 | } |
378 | 0 | case FlagValueStorageKind::kSequenceLocked: { |
379 | 0 | std::unique_ptr<void, DynValueDeleter> cloned(flags_internal::Alloc(op_), |
380 | 0 | DynValueDeleter{op_}); |
381 | 0 | ReadSequenceLockedData(cloned.get()); |
382 | 0 | return flags_internal::Unparse(op_, cloned.get()); |
383 | 0 | } |
384 | 0 | case FlagValueStorageKind::kHeapAllocated: { |
385 | 0 | absl::MutexLock l(guard); |
386 | 0 | return flags_internal::Unparse( |
387 | 0 | op_, PtrStorage().load(std::memory_order_acquire).Ptr()); |
388 | 0 | } |
389 | 0 | } |
390 | | |
391 | 0 | return ""; |
392 | 0 | } |
393 | | |
394 | 0 | void FlagImpl::SetCallback(const FlagCallbackFunc mutation_callback) { |
395 | 0 | absl::MutexLock l(DataGuard()); |
396 | |
|
397 | 0 | if (callback_ == nullptr) { |
398 | 0 | callback_ = new FlagCallback; |
399 | 0 | } |
400 | 0 | callback_->func = mutation_callback; |
401 | |
|
402 | 0 | InvokeCallback(); |
403 | 0 | } |
404 | | |
405 | 0 | void FlagImpl::InvokeCallback() const { |
406 | 0 | if (!callback_) return; |
407 | | |
408 | | // Make a copy of the C-style function pointer that we are about to invoke |
409 | | // before we release the lock guarding it. |
410 | 0 | FlagCallbackFunc cb = callback_->func; |
411 | | |
412 | | // If the flag has a mutation callback this function invokes it. While the |
413 | | // callback is being invoked the primary flag's mutex is unlocked and it is |
414 | | // re-locked back after call to callback is completed. Callback invocation is |
415 | | // guarded by flag's secondary mutex instead which prevents concurrent |
416 | | // callback invocation. Note that it is possible for other thread to grab the |
417 | | // primary lock and update flag's value at any time during the callback |
418 | | // invocation. This is by design. Callback can get a value of the flag if |
419 | | // necessary, but it might be different from the value initiated the callback |
420 | | // and it also can be different by the time the callback invocation is |
421 | | // completed. Requires that *primary_lock be held in exclusive mode; it may be |
422 | | // released and reacquired by the implementation. |
423 | 0 | MutexRelock relock(*DataGuard()); |
424 | 0 | absl::MutexLock lock(&callback_->guard); |
425 | 0 | cb(); |
426 | 0 | } |
427 | | |
428 | 0 | std::unique_ptr<FlagStateInterface> FlagImpl::SaveState() { |
429 | 0 | absl::MutexLock l(DataGuard()); |
430 | |
|
431 | 0 | bool modified = modified_; |
432 | 0 | bool on_command_line = on_command_line_; |
433 | 0 | switch (ValueStorageKind()) { |
434 | 0 | case FlagValueStorageKind::kValueAndInitBit: |
435 | 0 | case FlagValueStorageKind::kOneWordAtomic: { |
436 | 0 | return absl::make_unique<FlagState>( |
437 | 0 | *this, OneWordValue().load(std::memory_order_acquire), modified, |
438 | 0 | on_command_line, ModificationCount()); |
439 | 0 | } |
440 | 0 | case FlagValueStorageKind::kSequenceLocked: { |
441 | 0 | void* cloned = flags_internal::Alloc(op_); |
442 | | // Read is guaranteed to be successful because we hold the lock. |
443 | 0 | bool success = |
444 | 0 | seq_lock_.TryRead(cloned, AtomicBufferValue(), Sizeof(op_)); |
445 | 0 | assert(success); |
446 | 0 | static_cast<void>(success); |
447 | 0 | return absl::make_unique<FlagState>(*this, cloned, modified, |
448 | 0 | on_command_line, ModificationCount()); |
449 | 0 | } |
450 | 0 | case FlagValueStorageKind::kHeapAllocated: { |
451 | 0 | return absl::make_unique<FlagState>( |
452 | 0 | *this, |
453 | 0 | flags_internal::Clone( |
454 | 0 | op_, PtrStorage().load(std::memory_order_acquire).Ptr()), |
455 | 0 | modified, on_command_line, ModificationCount()); |
456 | 0 | } |
457 | 0 | } |
458 | 0 | return nullptr; |
459 | 0 | } |
460 | | |
461 | 0 | bool FlagImpl::RestoreState(const FlagState& flag_state) { |
462 | 0 | absl::MutexLock l(DataGuard()); |
463 | 0 | if (flag_state.counter_ == ModificationCount()) { |
464 | 0 | return false; |
465 | 0 | } |
466 | | |
467 | 0 | switch (ValueStorageKind()) { |
468 | 0 | case FlagValueStorageKind::kValueAndInitBit: |
469 | 0 | case FlagValueStorageKind::kOneWordAtomic: |
470 | 0 | StoreValue(&flag_state.value_.one_word, kProgrammaticChange); |
471 | 0 | break; |
472 | 0 | case FlagValueStorageKind::kSequenceLocked: |
473 | 0 | case FlagValueStorageKind::kHeapAllocated: |
474 | 0 | StoreValue(flag_state.value_.heap_allocated, kProgrammaticChange); |
475 | 0 | break; |
476 | 0 | } |
477 | | |
478 | 0 | modified_ = flag_state.modified_; |
479 | 0 | on_command_line_ = flag_state.on_command_line_; |
480 | |
|
481 | 0 | return true; |
482 | 0 | } |
483 | | |
484 | | template <typename StorageT> |
485 | 2 | StorageT* FlagImpl::OffsetValue() const { |
486 | 2 | char* p = reinterpret_cast<char*>(const_cast<FlagImpl*>(this)); |
487 | | // The offset is deduced via Flag value type specific op_. |
488 | 2 | ptrdiff_t offset = flags_internal::ValueOffset(op_); |
489 | | |
490 | 2 | return reinterpret_cast<StorageT*>(p + offset); |
491 | 2 | } Unexecuted instantiation: std::__1::atomic<unsigned long>* absl::flags_internal::FlagImpl::OffsetValue<std::__1::atomic<unsigned long> >() const absl::flags_internal::FlagOneWordValue* absl::flags_internal::FlagImpl::OffsetValue<absl::flags_internal::FlagOneWordValue>() const Line | Count | Source | 485 | 2 | StorageT* FlagImpl::OffsetValue() const { | 486 | 2 | char* p = reinterpret_cast<char*>(const_cast<FlagImpl*>(this)); | 487 | | // The offset is deduced via Flag value type specific op_. | 488 | 2 | ptrdiff_t offset = flags_internal::ValueOffset(op_); | 489 | | | 490 | 2 | return reinterpret_cast<StorageT*>(p + offset); | 491 | 2 | } |
Unexecuted instantiation: absl::flags_internal::FlagMaskedPointerValue* absl::flags_internal::FlagImpl::OffsetValue<absl::flags_internal::FlagMaskedPointerValue>() const |
492 | | |
493 | 0 | std::atomic<uint64_t>* FlagImpl::AtomicBufferValue() const { |
494 | 0 | assert(ValueStorageKind() == FlagValueStorageKind::kSequenceLocked); |
495 | 0 | return OffsetValue<std::atomic<uint64_t>>(); |
496 | 0 | } |
497 | | |
498 | 2 | std::atomic<int64_t>& FlagImpl::OneWordValue() const { |
499 | 2 | assert(ValueStorageKind() == FlagValueStorageKind::kOneWordAtomic || |
500 | 2 | ValueStorageKind() == FlagValueStorageKind::kValueAndInitBit); |
501 | 2 | return OffsetValue<FlagOneWordValue>()->value; |
502 | 2 | } |
503 | | |
504 | 0 | std::atomic<MaskedPointer>& FlagImpl::PtrStorage() const { |
505 | 0 | assert(ValueStorageKind() == FlagValueStorageKind::kHeapAllocated); |
506 | 0 | return OffsetValue<FlagMaskedPointerValue>()->value; |
507 | 0 | } |
508 | | |
509 | | // Attempts to parse supplied `value` string using parsing routine in the `flag` |
510 | | // argument. If parsing successful, this function replaces the dst with newly |
511 | | // parsed value. In case if any error is encountered in either step, the error |
512 | | // message is stored in 'err' |
513 | | std::unique_ptr<void, DynValueDeleter> FlagImpl::TryParse( |
514 | 0 | absl::string_view value, std::string& err) const { |
515 | 0 | std::unique_ptr<void, DynValueDeleter> tentative_value = MakeInitValue(); |
516 | |
|
517 | 0 | std::string parse_err; |
518 | 0 | if (!flags_internal::Parse(op_, value, tentative_value.get(), &parse_err)) { |
519 | 0 | absl::string_view err_sep = parse_err.empty() ? "" : "; "; |
520 | 0 | err = absl::StrCat("Illegal value '", value, "' specified for flag '", |
521 | 0 | Name(), "'", err_sep, parse_err); |
522 | 0 | return nullptr; |
523 | 0 | } |
524 | | |
525 | 0 | return tentative_value; |
526 | 0 | } |
527 | | |
528 | 0 | void FlagImpl::Read(void* dst) const { |
529 | 0 | auto* guard = DataGuard(); // Make sure flag initialized |
530 | 0 | switch (ValueStorageKind()) { |
531 | 0 | case FlagValueStorageKind::kValueAndInitBit: |
532 | 0 | case FlagValueStorageKind::kOneWordAtomic: { |
533 | 0 | const int64_t one_word_val = |
534 | 0 | OneWordValue().load(std::memory_order_acquire); |
535 | 0 | std::memcpy(dst, &one_word_val, Sizeof(op_)); |
536 | 0 | break; |
537 | 0 | } |
538 | 0 | case FlagValueStorageKind::kSequenceLocked: { |
539 | 0 | ReadSequenceLockedData(dst); |
540 | 0 | break; |
541 | 0 | } |
542 | 0 | case FlagValueStorageKind::kHeapAllocated: { |
543 | 0 | absl::MutexLock l(guard); |
544 | 0 | MaskedPointer ptr_value = PtrStorage().load(std::memory_order_acquire); |
545 | |
|
546 | 0 | flags_internal::CopyConstruct(op_, ptr_value.Ptr(), dst); |
547 | | |
548 | | // For unprotected read candidates, mark that the value as has been read. |
549 | 0 | if (ptr_value.IsUnprotectedReadCandidate() && !ptr_value.HasBeenRead()) { |
550 | 0 | ptr_value.MarkAsRead(); |
551 | 0 | PtrStorage().store(ptr_value, std::memory_order_release); |
552 | 0 | } |
553 | 0 | break; |
554 | 0 | } |
555 | 0 | } |
556 | 0 | } |
557 | | |
558 | 1 | int64_t FlagImpl::ReadOneWord() const { |
559 | 1 | assert(ValueStorageKind() == FlagValueStorageKind::kOneWordAtomic || |
560 | 1 | ValueStorageKind() == FlagValueStorageKind::kValueAndInitBit); |
561 | 1 | auto* guard = DataGuard(); // Make sure flag initialized |
562 | 1 | (void)guard; |
563 | 1 | return OneWordValue().load(std::memory_order_acquire); |
564 | 1 | } |
565 | | |
566 | 0 | bool FlagImpl::ReadOneBool() const { |
567 | 0 | assert(ValueStorageKind() == FlagValueStorageKind::kValueAndInitBit); |
568 | 0 | auto* guard = DataGuard(); // Make sure flag initialized |
569 | 0 | (void)guard; |
570 | 0 | return absl::bit_cast<FlagValueAndInitBit<bool>>( |
571 | 0 | OneWordValue().load(std::memory_order_acquire)) |
572 | 0 | .value; |
573 | 0 | } |
574 | | |
575 | 0 | void FlagImpl::ReadSequenceLockedData(void* dst) const { |
576 | 0 | size_t size = Sizeof(op_); |
577 | | // Attempt to read using the sequence lock. |
578 | 0 | if (ABSL_PREDICT_TRUE(seq_lock_.TryRead(dst, AtomicBufferValue(), size))) { |
579 | 0 | return; |
580 | 0 | } |
581 | | // We failed due to contention. Acquire the lock to prevent contention |
582 | | // and try again. |
583 | 0 | absl::ReaderMutexLock l(DataGuard()); |
584 | 0 | bool success = seq_lock_.TryRead(dst, AtomicBufferValue(), size); |
585 | 0 | assert(success); |
586 | 0 | static_cast<void>(success); |
587 | 0 | } |
588 | | |
589 | 0 | void FlagImpl::Write(const void* src) { |
590 | 0 | absl::MutexLock l(DataGuard()); |
591 | |
|
592 | 0 | if (ShouldValidateFlagValue(flags_internal::FastTypeId(op_))) { |
593 | 0 | std::unique_ptr<void, DynValueDeleter> obj{flags_internal::Clone(op_, src), |
594 | 0 | DynValueDeleter{op_}}; |
595 | 0 | std::string ignored_error; |
596 | 0 | std::string src_as_str = flags_internal::Unparse(op_, src); |
597 | 0 | if (!flags_internal::Parse(op_, src_as_str, obj.get(), &ignored_error)) { |
598 | 0 | ABSL_INTERNAL_LOG(ERROR, absl::StrCat("Attempt to set flag '", Name(), |
599 | 0 | "' to invalid value ", src_as_str)); |
600 | 0 | } |
601 | 0 | } |
602 | |
|
603 | 0 | StoreValue(src, kProgrammaticChange); |
604 | 0 | } |
605 | | |
606 | | // Sets the value of the flag based on specified string `value`. If the flag |
607 | | // was successfully set to new value, it returns true. Otherwise, sets `err` |
608 | | // to indicate the error, leaves the flag unchanged, and returns false. There |
609 | | // are three ways to set the flag's value: |
610 | | // * Update the current flag value |
611 | | // * Update the flag's default value |
612 | | // * Update the current flag value if it was never set before |
613 | | // The mode is selected based on 'set_mode' parameter. |
614 | | bool FlagImpl::ParseFrom(absl::string_view value, FlagSettingMode set_mode, |
615 | 0 | ValueSource source, std::string& err) { |
616 | 0 | absl::MutexLock l(DataGuard()); |
617 | |
|
618 | 0 | switch (set_mode) { |
619 | 0 | case SET_FLAGS_VALUE: { |
620 | | // set or modify the flag's value |
621 | 0 | auto tentative_value = TryParse(value, err); |
622 | 0 | if (!tentative_value) return false; |
623 | | |
624 | 0 | StoreValue(tentative_value.get(), source); |
625 | |
|
626 | 0 | if (source == kCommandLine) { |
627 | 0 | on_command_line_ = true; |
628 | 0 | } |
629 | 0 | break; |
630 | 0 | } |
631 | 0 | case SET_FLAG_IF_DEFAULT: { |
632 | | // set the flag's value, but only if it hasn't been set by someone else |
633 | 0 | if (modified_) { |
634 | | // TODO(rogeeff): review and fix this semantic. Currently we do not fail |
635 | | // in this case if flag is modified. This is misleading since the flag's |
636 | | // value is not updated even though we return true. |
637 | | // *err = absl::StrCat(Name(), " is already set to ", |
638 | | // CurrentValue(), "\n"); |
639 | | // return false; |
640 | 0 | return true; |
641 | 0 | } |
642 | 0 | auto tentative_value = TryParse(value, err); |
643 | 0 | if (!tentative_value) return false; |
644 | | |
645 | 0 | StoreValue(tentative_value.get(), source); |
646 | 0 | break; |
647 | 0 | } |
648 | 0 | case SET_FLAGS_DEFAULT: { |
649 | 0 | auto tentative_value = TryParse(value, err); |
650 | 0 | if (!tentative_value) return false; |
651 | | |
652 | 0 | if (DefaultKind() == FlagDefaultKind::kDynamicValue) { |
653 | 0 | void* old_value = default_value_.dynamic_value; |
654 | 0 | default_value_.dynamic_value = tentative_value.release(); |
655 | 0 | tentative_value.reset(old_value); |
656 | 0 | } else { |
657 | 0 | default_value_.dynamic_value = tentative_value.release(); |
658 | 0 | def_kind_ = static_cast<uint8_t>(FlagDefaultKind::kDynamicValue); |
659 | 0 | } |
660 | |
|
661 | 0 | if (!modified_) { |
662 | | // Need to set both default value *and* current, in this case. |
663 | 0 | StoreValue(default_value_.dynamic_value, source); |
664 | 0 | modified_ = false; |
665 | 0 | } |
666 | 0 | break; |
667 | 0 | } |
668 | 0 | } |
669 | | |
670 | 0 | return true; |
671 | 0 | } |
672 | | |
673 | 0 | void FlagImpl::CheckDefaultValueParsingRoundtrip() const { |
674 | 0 | std::string v = DefaultValue(); |
675 | |
|
676 | 0 | absl::MutexLock lock(DataGuard()); |
677 | |
|
678 | 0 | auto dst = MakeInitValue(); |
679 | 0 | std::string error; |
680 | 0 | if (!flags_internal::Parse(op_, v, dst.get(), &error)) { |
681 | 0 | ABSL_INTERNAL_LOG( |
682 | 0 | FATAL, |
683 | 0 | absl::StrCat("Flag ", Name(), " (from ", Filename(), |
684 | 0 | "): string form of default value '", v, |
685 | 0 | "' could not be parsed; error=", error)); |
686 | 0 | } |
687 | | |
688 | | // We do not compare dst to def since parsing/unparsing may make |
689 | | // small changes, e.g., precision loss for floating point types. |
690 | 0 | } |
691 | | |
692 | 0 | bool FlagImpl::ValidateInputValue(absl::string_view value) const { |
693 | 0 | absl::MutexLock l(DataGuard()); |
694 | |
|
695 | 0 | auto obj = MakeInitValue(); |
696 | 0 | std::string ignored_error; |
697 | 0 | return flags_internal::Parse(op_, value, obj.get(), &ignored_error); |
698 | 0 | } |
699 | | |
700 | | } // namespace flags_internal |
701 | | ABSL_NAMESPACE_END |
702 | | } // namespace absl |