commandlineflag.cc
Go to the documentation of this file.
00001 //
00002 // Copyright 2019 The Abseil Authors.
00003 //
00004 // Licensed under the Apache License, Version 2.0 (the "License");
00005 // you may not use this file except in compliance with the License.
00006 // You may obtain a copy of the License at
00007 //
00008 //      https://www.apache.org/licenses/LICENSE-2.0
00009 //
00010 // Unless required by applicable law or agreed to in writing, software
00011 // distributed under the License is distributed on an "AS IS" BASIS,
00012 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013 // See the License for the specific language governing permissions and
00014 // limitations under the License.
00015 
00016 #include "absl/flags/internal/commandlineflag.h"
00017 
00018 #include <cassert>
00019 
00020 #include "absl/base/internal/raw_logging.h"
00021 #include "absl/base/optimization.h"
00022 #include "absl/flags/config.h"
00023 #include "absl/flags/usage_config.h"
00024 #include "absl/strings/str_cat.h"
00025 #include "absl/synchronization/mutex.h"
00026 
00027 namespace absl {
00028 namespace flags_internal {
00029 
00030 // The help message indicating that the commandline flag has been
00031 // 'stripped'. It will not show up when doing "-help" and its
00032 // variants. The flag is stripped if ABSL_FLAGS_STRIP_HELP is set to 1
00033 // before including absl/flags/flag.h
00034 
00035 // This is used by this file, and also in commandlineflags_reporting.cc
00036 const char kStrippedFlagHelp[] = "\001\002\003\004 (unknown) \004\003\002\001";
00037 
00038 namespace {
00039 
00040 void StoreAtomic(CommandLineFlag* flag, const void* data, size_t size) {
00041   int64_t t = 0;
00042   assert(size <= sizeof(int64_t));
00043   memcpy(&t, data, size);
00044   flag->atomic.store(t, std::memory_order_release);
00045 }
00046 
00047 // If the flag has a mutation callback this function invokes it. While the
00048 // callback is being invoked the primary flag's mutex is unlocked and it is
00049 // re-locked back after call to callback is completed. Callback invocation is
00050 // guarded by flag's secondary mutex instead which prevents concurrent callback
00051 // invocation. Note that it is possible for other thread to grab the primary
00052 // lock and update flag's value at any time during the callback invocation.
00053 // This is by design. Callback can get a value of the flag if necessary, but it
00054 // might be different from the value initiated the callback and it also can be
00055 // different by the time the callback invocation is completed.
00056 // Requires that *primary_lock be held in exclusive mode; it may be released
00057 // and reacquired by the implementation.
00058 void InvokeCallback(CommandLineFlag* flag, absl::Mutex* primary_lock)
00059     EXCLUSIVE_LOCKS_REQUIRED(primary_lock) {
00060   if (!flag->callback) return;
00061 
00062   // The callback lock is guaranteed initialized, because *primary_lock exists.
00063   absl::Mutex* callback_mu = &flag->locks->callback_mu;
00064 
00065   // When executing the callback we need the primary flag's mutex to be unlocked
00066   // so that callback can retrieve the flag's value.
00067   primary_lock->Unlock();
00068 
00069   {
00070     absl::MutexLock lock(callback_mu);
00071 
00072     flag->callback();
00073   }
00074 
00075   primary_lock->Lock();
00076 }
00077 
00078 // Currently we only validate flag values for user-defined flag types.
00079 bool ShouldValidateFlagValue(const CommandLineFlag& flag) {
00080 #define DONT_VALIDATE(T) \
00081   if (flag.IsOfType<T>()) return false;
00082   ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(DONT_VALIDATE)
00083   DONT_VALIDATE(std::string)
00084   DONT_VALIDATE(std::vector<std::string>)
00085 #undef DONT_VALIDATE
00086 
00087   return true;
00088 }
00089 
00090 }  // namespace
00091 
00092 // Update any copy of the flag value that is stored in an atomic word.
00093 // In addition if flag has a mutation callback this function invokes it.
00094 void UpdateCopy(CommandLineFlag* flag, absl::Mutex* primary_lock)
00095     EXCLUSIVE_LOCKS_REQUIRED(primary_lock) {
00096 #define STORE_ATOMIC(T)                      \
00097   else if (flag->IsOfType<T>()) {            \
00098     StoreAtomic(flag, flag->cur, sizeof(T)); \
00099   }
00100 
00101   if (false) {
00102   }
00103   ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(STORE_ATOMIC)
00104 #undef STORE_ATOMIC
00105 
00106   InvokeCallback(flag, primary_lock);
00107 }
00108 
00109 // Ensure that the lazily initialized fields of *flag have been initialized,
00110 // and return &flag->locks->primary_mu.
00111 absl::Mutex* InitFlagIfNecessary(CommandLineFlag* flag)
00112     LOCK_RETURNED(flag->locks->primary_mu) {
00113   absl::Mutex* mu;
00114   if (!flag->inited.load(std::memory_order_acquire)) {
00115     // Need to initialize lazily initialized fields.
00116     ABSL_CONST_INIT static absl::Mutex init_lock(absl::kConstInit);
00117     init_lock.Lock();
00118     if (flag->locks == nullptr) {  // Must initialize Mutexes for this flag.
00119       flag->locks = new flags_internal::CommandLineFlagLocks;
00120     }
00121     mu = &flag->locks->primary_mu;
00122     init_lock.Unlock();
00123     mu->Lock();
00124     if (!flag->retired &&
00125         flag->def == nullptr) {  // Need to initialize def and cur fields.
00126       flag->def = (*flag->make_init_value)();
00127       flag->cur = Clone(flag->op, flag->def);
00128       UpdateCopy(flag, mu);
00129     }
00130     mu->Unlock();
00131     flag->inited.store(true, std::memory_order_release);
00132   } else {  // All fields initialized; flag->locks is therefore safe to read.
00133     mu = &flag->locks->primary_mu;
00134   }
00135   return mu;
00136 }
00137 
00138 // Return true iff flag value was changed via direct-access.
00139 bool ChangedDirectly(CommandLineFlag* flag, const void* a, const void* b) {
00140   if (!flag->IsAbseilFlag()) {
00141     // Need to compare values for direct-access flags.
00142 #define CHANGED_FOR_TYPE(T)                                                  \
00143   if (flag->IsOfType<T>()) {                                                 \
00144     return *reinterpret_cast<const T*>(a) != *reinterpret_cast<const T*>(b); \
00145   }
00146 
00147     CHANGED_FOR_TYPE(bool);
00148     CHANGED_FOR_TYPE(int32_t);
00149     CHANGED_FOR_TYPE(int64_t);
00150     CHANGED_FOR_TYPE(uint64_t);
00151     CHANGED_FOR_TYPE(double);
00152     CHANGED_FOR_TYPE(std::string);
00153 #undef CHANGED_FOR_TYPE
00154   }
00155   return false;
00156 }
00157 
00158 // Direct-access flags can be modified without going through the
00159 // flag API. Detect such changes and updated the modified bit.
00160 void UpdateModifiedBit(CommandLineFlag* flag) {
00161   if (!flag->IsAbseilFlag()) {
00162     absl::MutexLock l(InitFlagIfNecessary(flag));
00163     if (!flag->modified && ChangedDirectly(flag, flag->cur, flag->def)) {
00164       flag->modified = true;
00165     }
00166   }
00167 }
00168 
00169 bool Validate(CommandLineFlag*, const void*) {
00170   return true;
00171 }
00172 
00173 std::string HelpText::GetHelpText() const {
00174   if (help_function_) return help_function_();
00175   if (help_message_) return help_message_;
00176 
00177   return {};
00178 }
00179 
00180 const int64_t CommandLineFlag::kAtomicInit;
00181 
00182 void CommandLineFlag::Read(void* dst,
00183                            const flags_internal::FlagOpFn dst_op) const {
00184   absl::ReaderMutexLock l(
00185       InitFlagIfNecessary(const_cast<CommandLineFlag*>(this)));
00186 
00187   // `dst_op` is the unmarshaling operation corresponding to the declaration
00188   // visibile at the call site. `op` is the Flag's defined unmarshalling
00189   // operation. They must match for this operation to be well-defined.
00190   if (ABSL_PREDICT_FALSE(dst_op != op)) {
00191     ABSL_INTERNAL_LOG(
00192         ERROR,
00193         absl::StrCat("Flag '", name,
00194                      "' is defined as one type and declared as another"));
00195   }
00196   CopyConstruct(op, cur, dst);
00197 }
00198 
00199 void CommandLineFlag::Write(const void* src,
00200                             const flags_internal::FlagOpFn src_op) {
00201   absl::Mutex* mu = InitFlagIfNecessary(this);
00202   absl::MutexLock l(mu);
00203 
00204   // `src_op` is the marshalling operation corresponding to the declaration
00205   // visible at the call site. `op` is the Flag's defined marshalling operation.
00206   // They must match for this operation to be well-defined.
00207   if (ABSL_PREDICT_FALSE(src_op != op)) {
00208     ABSL_INTERNAL_LOG(
00209         ERROR,
00210         absl::StrCat("Flag '", name,
00211                      "' is defined as one type and declared as another"));
00212   }
00213 
00214   if (ShouldValidateFlagValue(*this)) {
00215     void* obj = Clone(op, src);
00216     std::string ignored_error;
00217     std::string src_as_str = Unparse(marshalling_op, src);
00218     if (!Parse(marshalling_op, src_as_str, obj, &ignored_error) ||
00219         !Validate(this, obj)) {
00220       ABSL_INTERNAL_LOG(ERROR, absl::StrCat("Attempt to set flag '", name,
00221                                             "' to invalid value ", src_as_str));
00222     }
00223     Delete(op, obj);
00224   }
00225 
00226   modified = true;
00227   counter++;
00228   Copy(op, src, cur);
00229 
00230   UpdateCopy(this, mu);
00231 }
00232 
00233 absl::string_view CommandLineFlag::Typename() const {
00234   // We do not store/report type in Abseil Flags, so that user do not rely on in
00235   // at runtime
00236   if (IsAbseilFlag() || IsRetired()) return "";
00237 
00238 #define HANDLE_V1_BUILTIN_TYPE(t) \
00239   if (IsOfType<t>()) {            \
00240     return #t;                    \
00241   }
00242 
00243   HANDLE_V1_BUILTIN_TYPE(bool);
00244   HANDLE_V1_BUILTIN_TYPE(int32_t);
00245   HANDLE_V1_BUILTIN_TYPE(int64_t);
00246   HANDLE_V1_BUILTIN_TYPE(uint64_t);
00247   HANDLE_V1_BUILTIN_TYPE(double);
00248 #undef HANDLE_V1_BUILTIN_TYPE
00249 
00250   if (IsOfType<std::string>()) {
00251     return "string";
00252   }
00253 
00254   return "";
00255 }
00256 
00257 std::string CommandLineFlag::Filename() const {
00258   return flags_internal::GetUsageConfig().normalize_filename(this->filename);
00259 }
00260 
00261 std::string CommandLineFlag::DefaultValue() const {
00262   return Unparse(this->marshalling_op, this->def);
00263 }
00264 
00265 std::string CommandLineFlag::CurrentValue() const {
00266   return Unparse(this->marshalling_op, this->cur);
00267 }
00268 
00269 void CommandLineFlag::SetCallback(
00270     const flags_internal::FlagCallback mutation_callback) {
00271   absl::Mutex* mu = InitFlagIfNecessary(this);
00272   absl::MutexLock l(mu);
00273 
00274   callback = mutation_callback;
00275 
00276   InvokeCallback(this, mu);
00277 }
00278 
00279 // Attempts to parse supplied `value` string using parsing routine in the `flag`
00280 // argument. If parsing is successful, it will try to validate that the parsed
00281 // value is valid for the specified 'flag'. Finally this function stores the
00282 // parsed value in 'dst' assuming it is a pointer to the flag's value type. In
00283 // case if any error is encountered in either step, the error message is stored
00284 // in 'err'
00285 static bool TryParseLocked(CommandLineFlag* flag, void* dst,
00286                            absl::string_view value, std::string* err) {
00287   void* tentative_value = Clone(flag->op, flag->def);
00288   std::string parse_err;
00289   if (!Parse(flag->marshalling_op, value, tentative_value, &parse_err)) {
00290     auto type_name = flag->Typename();
00291     absl::string_view err_sep = parse_err.empty() ? "" : "; ";
00292     absl::string_view typename_sep = type_name.empty() ? "" : " ";
00293     *err = absl::StrCat("Illegal value '", value, "' specified for",
00294                         typename_sep, type_name, " flag '", flag->Name(), "'",
00295                         err_sep, parse_err);
00296     Delete(flag->op, tentative_value);
00297     return false;
00298   }
00299 
00300   if (!Validate(flag, tentative_value)) {
00301     *err = absl::StrCat("Failed validation of new value '",
00302                         Unparse(flag->marshalling_op, tentative_value),
00303                         "' for flag '", flag->Name(), "'");
00304     Delete(flag->op, tentative_value);
00305     return false;
00306   }
00307 
00308   flag->counter++;
00309   Copy(flag->op, tentative_value, dst);
00310   Delete(flag->op, tentative_value);
00311   return true;
00312 }
00313 
00314 // Sets the value of the flag based on specified string `value`. If the flag
00315 // was successfully set to new value, it returns true. Otherwise, sets `err`
00316 // to indicate the error, leaves the flag unchanged, and returns false. There
00317 // are three ways to set the flag's value:
00318 //  * Update the current flag value
00319 //  * Update the flag's default value
00320 //  * Update the current flag value if it was never set before
00321 // The mode is selected based on 'set_mode' parameter.
00322 bool CommandLineFlag::SetFromString(absl::string_view value,
00323                                     FlagSettingMode set_mode,
00324                                     ValueSource source, std::string* err) {
00325   if (IsRetired()) return false;
00326 
00327   UpdateModifiedBit(this);
00328 
00329   absl::Mutex* mu = InitFlagIfNecessary(this);
00330   absl::MutexLock l(mu);
00331 
00332   switch (set_mode) {
00333     case SET_FLAGS_VALUE: {
00334       // set or modify the flag's value
00335       if (!TryParseLocked(this, this->cur, value, err)) return false;
00336       this->modified = true;
00337       UpdateCopy(this, mu);
00338 
00339       if (source == kCommandLine) {
00340         this->on_command_line = true;
00341       }
00342       break;
00343     }
00344     case SET_FLAG_IF_DEFAULT: {
00345       // set the flag's value, but only if it hasn't been set by someone else
00346       if (!this->modified) {
00347         if (!TryParseLocked(this, this->cur, value, err)) return false;
00348         this->modified = true;
00349         UpdateCopy(this, mu);
00350       } else {
00351         // TODO(rogeeff): review and fix this semantic. Currently we do not fail
00352         // in this case if flag is modified. This is misleading since the flag's
00353         // value is not updated even though we return true.
00354         // *err = absl::StrCat(this->Name(), " is already set to ",
00355         //                     CurrentValue(), "\n");
00356         // return false;
00357         return true;
00358       }
00359       break;
00360     }
00361     case SET_FLAGS_DEFAULT: {
00362       // modify the flag's default-value
00363       if (!TryParseLocked(this, this->def, value, err)) return false;
00364 
00365       if (!this->modified) {
00366         // Need to set both defvalue *and* current, in this case
00367         Copy(this->op, this->def, this->cur);
00368         UpdateCopy(this, mu);
00369       }
00370       break;
00371     }
00372     default: {
00373       // unknown set_mode
00374       assert(false);
00375       return false;
00376     }
00377   }
00378 
00379   return true;
00380 }
00381 
00382 }  // namespace flags_internal
00383 }  // namespace absl


abseil_cpp
Author(s):
autogenerated on Wed Jun 19 2019 19:42:14