• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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