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 <stddef.h>
19 #include <stdint.h>
20 #include <string.h>
21
22 #include <atomic>
23 #include <memory>
24 #include <string>
25 #include <vector>
26
27 #include "absl/base/attributes.h"
28 #include "absl/base/config.h"
29 #include "absl/base/const_init.h"
30 #include "absl/base/optimization.h"
31 #include "absl/flags/internal/commandlineflag.h"
32 #include "absl/flags/usage_config.h"
33 #include "absl/strings/str_cat.h"
34 #include "absl/strings/string_view.h"
35 #include "absl/synchronization/mutex.h"
36
37 namespace absl {
38 ABSL_NAMESPACE_BEGIN
39 namespace flags_internal {
40
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 ABSL_FLAGS_STRIP_HELP is set to 1
44 // before including absl/flags/flag.h
45 const char kStrippedFlagHelp[] = "\001\002\003\004 (unknown) \004\003\002\001";
46
47 namespace {
48
49 // Currently we only validate flag values for user-defined flag types.
ShouldValidateFlagValue(FlagStaticTypeId flag_type_id)50 bool ShouldValidateFlagValue(FlagStaticTypeId flag_type_id) {
51 #define DONT_VALIDATE(T) \
52 if (flag_type_id == &FlagStaticTypeIdGen<T>) return false;
53 ABSL_FLAGS_INTERNAL_BUILTIN_TYPES(DONT_VALIDATE)
54 #undef DONT_VALIDATE
55
56 return true;
57 }
58
59 // RAII helper used to temporarily unlock and relock `absl::Mutex`.
60 // This is used when we need to ensure that locks are released while
61 // invoking user supplied callbacks and then reacquired, since callbacks may
62 // need to acquire these locks themselves.
63 class MutexRelock {
64 public:
MutexRelock(absl::Mutex * mu)65 explicit MutexRelock(absl::Mutex* mu) : mu_(mu) { mu_->Unlock(); }
~MutexRelock()66 ~MutexRelock() { mu_->Lock(); }
67
68 MutexRelock(const MutexRelock&) = delete;
69 MutexRelock& operator=(const MutexRelock&) = delete;
70
71 private:
72 absl::Mutex* mu_;
73 };
74
75 } // namespace
76
Init()77 void FlagImpl::Init() {
78 new (&data_guard_) absl::Mutex;
79
80 absl::MutexLock lock(reinterpret_cast<absl::Mutex*>(&data_guard_));
81
82 value_.dynamic = MakeInitValue().release();
83 StoreAtomic();
84 }
85
86 // Ensures that the lazily initialized data is initialized,
87 // and returns pointer to the mutex guarding flags data.
DataGuard() const88 absl::Mutex* FlagImpl::DataGuard() const {
89 absl::call_once(const_cast<FlagImpl*>(this)->init_control_, &FlagImpl::Init,
90 const_cast<FlagImpl*>(this));
91
92 // data_guard_ is initialized.
93 return reinterpret_cast<absl::Mutex*>(&data_guard_);
94 }
95
AssertValidType(FlagStaticTypeId type_id) const96 void FlagImpl::AssertValidType(FlagStaticTypeId type_id) const {
97 FlagStaticTypeId this_type_id = flags_internal::StaticTypeId(op_);
98
99 // `type_id` is the type id corresponding to the declaration visibile at the
100 // call site. `this_type_id` is the type id corresponding to the type stored
101 // during flag definition. They must match for this operation to be
102 // well-defined.
103 if (ABSL_PREDICT_TRUE(type_id == this_type_id)) return;
104
105 void* lhs_runtime_type_id = type_id();
106 void* rhs_runtime_type_id = this_type_id();
107
108 if (lhs_runtime_type_id == rhs_runtime_type_id) return;
109
110 #if defined(ABSL_FLAGS_INTERNAL_HAS_RTTI)
111 if (*reinterpret_cast<std::type_info*>(lhs_runtime_type_id) ==
112 *reinterpret_cast<std::type_info*>(rhs_runtime_type_id))
113 return;
114 #endif
115
116 ABSL_INTERNAL_LOG(
117 FATAL, absl::StrCat("Flag '", Name(),
118 "' is defined as one type and declared as another"));
119 }
120
MakeInitValue() const121 std::unique_ptr<void, DynValueDeleter> FlagImpl::MakeInitValue() const {
122 void* res = nullptr;
123 if (DefaultKind() == FlagDefaultKind::kDynamicValue) {
124 res = flags_internal::Clone(op_, default_value_.dynamic_value);
125 } else {
126 res = (*default_value_.gen_func)();
127 }
128 return {res, DynValueDeleter{op_}};
129 }
130
StoreValue(const void * src)131 void FlagImpl::StoreValue(const void* src) {
132 flags_internal::Copy(op_, src, value_.dynamic);
133 StoreAtomic();
134 modified_ = true;
135 ++counter_;
136 InvokeCallback();
137 }
138
Name() const139 absl::string_view FlagImpl::Name() const { return name_; }
140
Filename() const141 std::string FlagImpl::Filename() const {
142 return flags_internal::GetUsageConfig().normalize_filename(filename_);
143 }
144
Help() const145 std::string FlagImpl::Help() const {
146 return HelpSourceKind() == FlagHelpKind::kLiteral ? help_.literal
147 : help_.gen_func();
148 }
149
IsModified() const150 bool FlagImpl::IsModified() const {
151 absl::MutexLock l(DataGuard());
152 return modified_;
153 }
154
IsSpecifiedOnCommandLine() const155 bool FlagImpl::IsSpecifiedOnCommandLine() const {
156 absl::MutexLock l(DataGuard());
157 return on_command_line_;
158 }
159
DefaultValue() const160 std::string FlagImpl::DefaultValue() const {
161 absl::MutexLock l(DataGuard());
162
163 auto obj = MakeInitValue();
164 return flags_internal::Unparse(op_, obj.get());
165 }
166
CurrentValue() const167 std::string FlagImpl::CurrentValue() const {
168 absl::MutexLock l(DataGuard());
169
170 return flags_internal::Unparse(op_, value_.dynamic);
171 }
172
SetCallback(const FlagCallbackFunc mutation_callback)173 void FlagImpl::SetCallback(const FlagCallbackFunc mutation_callback) {
174 absl::MutexLock l(DataGuard());
175
176 if (callback_ == nullptr) {
177 callback_ = new FlagCallback;
178 }
179 callback_->func = mutation_callback;
180
181 InvokeCallback();
182 }
183
InvokeCallback() const184 void FlagImpl::InvokeCallback() const {
185 if (!callback_) return;
186
187 // Make a copy of the C-style function pointer that we are about to invoke
188 // before we release the lock guarding it.
189 FlagCallbackFunc cb = callback_->func;
190
191 // If the flag has a mutation callback this function invokes it. While the
192 // callback is being invoked the primary flag's mutex is unlocked and it is
193 // re-locked back after call to callback is completed. Callback invocation is
194 // guarded by flag's secondary mutex instead which prevents concurrent
195 // callback invocation. Note that it is possible for other thread to grab the
196 // primary lock and update flag's value at any time during the callback
197 // invocation. This is by design. Callback can get a value of the flag if
198 // necessary, but it might be different from the value initiated the callback
199 // and it also can be different by the time the callback invocation is
200 // completed. Requires that *primary_lock be held in exclusive mode; it may be
201 // released and reacquired by the implementation.
202 MutexRelock relock(DataGuard());
203 absl::MutexLock lock(&callback_->guard);
204 cb();
205 }
206
RestoreState(const void * value,bool modified,bool on_command_line,int64_t counter)207 bool FlagImpl::RestoreState(const void* value, bool modified,
208 bool on_command_line, int64_t counter) {
209 {
210 absl::MutexLock l(DataGuard());
211
212 if (counter_ == counter) return false;
213 }
214
215 Write(value);
216
217 {
218 absl::MutexLock l(DataGuard());
219
220 modified_ = modified;
221 on_command_line_ = on_command_line;
222 }
223
224 return true;
225 }
226
227 // Attempts to parse supplied `value` string using parsing routine in the `flag`
228 // argument. If parsing successful, this function replaces the dst with newly
229 // parsed value. In case if any error is encountered in either step, the error
230 // message is stored in 'err'
TryParse(absl::string_view value,std::string * err) const231 std::unique_ptr<void, DynValueDeleter> FlagImpl::TryParse(
232 absl::string_view value, std::string* err) const {
233 std::unique_ptr<void, DynValueDeleter> tentative_value = MakeInitValue();
234
235 std::string parse_err;
236 if (!flags_internal::Parse(op_, value, tentative_value.get(), &parse_err)) {
237 absl::string_view err_sep = parse_err.empty() ? "" : "; ";
238 *err = absl::StrCat("Illegal value '", value, "' specified for flag '",
239 Name(), "'", err_sep, parse_err);
240 return nullptr;
241 }
242
243 return tentative_value;
244 }
245
Read(void * dst) const246 void FlagImpl::Read(void* dst) const {
247 absl::ReaderMutexLock l(DataGuard());
248
249 flags_internal::CopyConstruct(op_, value_.dynamic, dst);
250 }
251
StoreAtomic()252 void FlagImpl::StoreAtomic() {
253 size_t data_size = flags_internal::Sizeof(op_);
254
255 if (data_size <= sizeof(int64_t)) {
256 int64_t t = 0;
257 std::memcpy(&t, value_.dynamic, data_size);
258 value_.atomics.small_atomic.store(t, std::memory_order_release);
259 }
260 #if defined(ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD)
261 else if (data_size <= sizeof(FlagsInternalTwoWordsType)) {
262 FlagsInternalTwoWordsType t{0, 0};
263 std::memcpy(&t, value_.dynamic, data_size);
264 value_.atomics.big_atomic.store(t, std::memory_order_release);
265 }
266 #endif
267 }
268
Write(const void * src)269 void FlagImpl::Write(const void* src) {
270 absl::MutexLock l(DataGuard());
271
272 if (ShouldValidateFlagValue(flags_internal::StaticTypeId(op_))) {
273 std::unique_ptr<void, DynValueDeleter> obj{flags_internal::Clone(op_, src),
274 DynValueDeleter{op_}};
275 std::string ignored_error;
276 std::string src_as_str = flags_internal::Unparse(op_, src);
277 if (!flags_internal::Parse(op_, src_as_str, obj.get(), &ignored_error)) {
278 ABSL_INTERNAL_LOG(ERROR, absl::StrCat("Attempt to set flag '", Name(),
279 "' to invalid value ", src_as_str));
280 }
281 }
282
283 StoreValue(src);
284 }
285
286 // Sets the value of the flag based on specified string `value`. If the flag
287 // was successfully set to new value, it returns true. Otherwise, sets `err`
288 // to indicate the error, leaves the flag unchanged, and returns false. There
289 // are three ways to set the flag's value:
290 // * Update the current flag value
291 // * Update the flag's default value
292 // * Update the current flag value if it was never set before
293 // The mode is selected based on 'set_mode' parameter.
SetFromString(absl::string_view value,FlagSettingMode set_mode,ValueSource source,std::string * err)294 bool FlagImpl::SetFromString(absl::string_view value, FlagSettingMode set_mode,
295 ValueSource source, std::string* err) {
296 absl::MutexLock l(DataGuard());
297
298 switch (set_mode) {
299 case SET_FLAGS_VALUE: {
300 // set or modify the flag's value
301 auto tentative_value = TryParse(value, err);
302 if (!tentative_value) return false;
303
304 StoreValue(tentative_value.get());
305
306 if (source == kCommandLine) {
307 on_command_line_ = true;
308 }
309 break;
310 }
311 case SET_FLAG_IF_DEFAULT: {
312 // set the flag's value, but only if it hasn't been set by someone else
313 if (modified_) {
314 // TODO(rogeeff): review and fix this semantic. Currently we do not fail
315 // in this case if flag is modified. This is misleading since the flag's
316 // value is not updated even though we return true.
317 // *err = absl::StrCat(Name(), " is already set to ",
318 // CurrentValue(), "\n");
319 // return false;
320 return true;
321 }
322 auto tentative_value = TryParse(value, err);
323 if (!tentative_value) return false;
324
325 StoreValue(tentative_value.get());
326 break;
327 }
328 case SET_FLAGS_DEFAULT: {
329 auto tentative_value = TryParse(value, err);
330 if (!tentative_value) return false;
331
332 if (DefaultKind() == FlagDefaultKind::kDynamicValue) {
333 void* old_value = default_value_.dynamic_value;
334 default_value_.dynamic_value = tentative_value.release();
335 tentative_value.reset(old_value);
336 } else {
337 default_value_.dynamic_value = tentative_value.release();
338 def_kind_ = static_cast<uint8_t>(FlagDefaultKind::kDynamicValue);
339 }
340
341 if (!modified_) {
342 // Need to set both default value *and* current, in this case
343 StoreValue(default_value_.dynamic_value);
344 modified_ = false;
345 }
346 break;
347 }
348 }
349
350 return true;
351 }
352
CheckDefaultValueParsingRoundtrip() const353 void FlagImpl::CheckDefaultValueParsingRoundtrip() const {
354 std::string v = DefaultValue();
355
356 absl::MutexLock lock(DataGuard());
357
358 auto dst = MakeInitValue();
359 std::string error;
360 if (!flags_internal::Parse(op_, v, dst.get(), &error)) {
361 ABSL_INTERNAL_LOG(
362 FATAL,
363 absl::StrCat("Flag ", Name(), " (from ", Filename(),
364 "): std::string form of default value '", v,
365 "' could not be parsed; error=", error));
366 }
367
368 // We do not compare dst to def since parsing/unparsing may make
369 // small changes, e.g., precision loss for floating point types.
370 }
371
ValidateInputValue(absl::string_view value) const372 bool FlagImpl::ValidateInputValue(absl::string_view value) const {
373 absl::MutexLock l(DataGuard());
374
375 auto obj = MakeInitValue();
376 std::string ignored_error;
377 return flags_internal::Parse(op_, value, obj.get(), &ignored_error);
378 }
379
380 } // namespace flags_internal
381 ABSL_NAMESPACE_END
382 } // namespace absl
383