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