aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--absl/flags/parse.cc233
-rw-r--r--absl/flags/parse.h106
-rw-r--r--absl/flags/parse_test.cc159
-rw-r--r--absl/synchronization/internal/futex.h70
-rw-r--r--absl/synchronization/internal/waiter.cc106
-rw-r--r--absl/synchronization/internal/waiter.h3
-rw-r--r--absl/synchronization/mutex.cc45
7 files changed, 519 insertions, 203 deletions
diff --git a/absl/flags/parse.cc b/absl/flags/parse.cc
index 768652d8..fcd6a52d 100644
--- a/absl/flags/parse.cc
+++ b/absl/flags/parse.cc
@@ -19,6 +19,7 @@
#include <algorithm>
#include <cstdint>
+#include <cstdlib>
#include <fstream>
#include <iostream>
#include <iterator>
@@ -278,7 +279,7 @@ std::tuple<absl::string_view, absl::string_view, bool> SplitNameAndValue(
return std::make_tuple("", "", false);
}
- auto equal_sign_pos = arg.find("=");
+ auto equal_sign_pos = arg.find('=');
absl::string_view flag_name = arg.substr(0, equal_sign_pos);
@@ -664,40 +665,76 @@ std::vector<std::string> GetMisspellingHints(const absl::string_view flag) {
// --------------------------------------------------------------------
std::vector<char*> ParseCommandLineImpl(int argc, char* argv[],
- UsageFlagsAction usage_flag_act,
- OnUndefinedFlag on_undef_flag) {
- ABSL_INTERNAL_CHECK(argc > 0, "Missing argv[0]");
+ UsageFlagsAction usage_flag_action,
+ OnUndefinedFlag undef_flag_action) {
+ std::vector<char*> positional_args;
+ std::vector<UnrecognizedFlag> unrecognized_flags;
- // Once parsing has started we will not have more flag registrations.
- // If we did, they would be missing during parsing, which is a problem on
- // itself.
- flags_internal::FinalizeRegistry();
+ bool parse_successful = absl::ParseAbseilFlagsOnly(
+ argc, argv, positional_args, unrecognized_flags);
- // This routine does not return anything since we abort on failure.
- CheckDefaultValuesParsingRoundtrip();
+ if (undef_flag_action != OnUndefinedFlag::kIgnoreUndefined) {
+ ReportUnrecognizedFlags(
+ unrecognized_flags,
+ undef_flag_action ==
+ flags_internal::OnUndefinedFlag::kAbortIfUndefined);
- std::vector<std::string> flagfile_value;
+ if (!unrecognized_flags.empty()) { parse_successful = false; }
+ }
+
+#if ABSL_FLAGS_STRIP_NAMES
+ if (!parse_successful) {
+ ReportUsageError("NOTE: command line flags are disabled in this build",
+ true);
+ }
+#endif
+
+ if (!parse_successful) {
+ HandleUsageFlags(std::cout, ProgramUsageMessage());
+ std::exit(1);
+ }
+
+ if (usage_flag_action == UsageFlagsAction::kHandleUsage) {
+ int exit_code = HandleUsageFlags(std::cout, ProgramUsageMessage());
+
+ if (exit_code != -1) {
+ std::exit(exit_code);
+ }
+ }
+
+ return positional_args;
+}
+// --------------------------------------------------------------------
+
+} // namespace flags_internal
+
+bool ParseAbseilFlagsOnly(int argc, char* argv[],
+ std::vector<char*>& positional_args,
+ std::vector<UnrecognizedFlag>& unrecognized_flags) {
+ ABSL_INTERNAL_CHECK(argc > 0, "Missing argv[0]");
+
+ using flags_internal::ArgsList;
+ using flags_internal::specified_flags;
+
+ std::vector<std::string> flagfile_value;
std::vector<ArgsList> input_args;
- input_args.emplace_back(argc, argv);
- std::vector<char*> output_args;
- std::vector<char*> positional_args;
- output_args.reserve(static_cast<size_t>(argc));
+ // Once parsing has started we will not allow more flag registrations.
+ flags_internal::FinalizeRegistry();
- // This is the list of undefined flags. The element of the list is the pair
- // consisting of boolean indicating if flag came from command line (vs from
- // some flag file we've read) and flag name.
- // TODO(rogeeff): Eliminate the first element in the pair after cleanup.
- std::vector<std::pair<bool, std::string>> undefined_flag_names;
+ // This routine does not return anything since we abort on failure.
+ flags_internal::CheckDefaultValuesParsingRoundtrip();
+
+ input_args.push_back(ArgsList(argc, argv));
// Set program invocation name if it is not set before.
- if (ProgramInvocationName() == "UNKNOWN") {
+ if (flags_internal::ProgramInvocationName() == "UNKNOWN") {
flags_internal::SetProgramInvocationName(argv[0]);
}
- output_args.push_back(argv[0]);
+ positional_args.push_back(argv[0]);
- absl::MutexLock l(&specified_flags_guard);
+ absl::MutexLock l(&flags_internal::specified_flags_guard);
if (specified_flags == nullptr) {
specified_flags = new std::vector<const CommandLineFlag*>;
} else {
@@ -709,13 +746,15 @@ std::vector<char*> ParseCommandLineImpl(int argc, char* argv[],
// recursive parsing of flagfile(s).
bool success = true;
while (!input_args.empty()) {
- // 10. First we process the built-in generator flags.
- success &= HandleGeneratorFlags(input_args, flagfile_value);
+ // First we process the built-in generator flags.
+ success &= flags_internal::HandleGeneratorFlags(input_args, flagfile_value);
- // 30. Select top-most (most recent) arguments list. If it is empty drop it
+ // Select top-most (most recent) arguments list. If it is empty drop it
// and re-try.
ArgsList& curr_list = input_args.back();
+ // Every ArgsList starts with real or fake program name, so we can always
+ // start by skipping it.
curr_list.PopFront();
if (curr_list.Size() == 0) {
@@ -723,13 +762,13 @@ std::vector<char*> ParseCommandLineImpl(int argc, char* argv[],
continue;
}
- // 40. Pick up the front remaining argument in the current list. If current
- // stack of argument lists contains only one element - we are processing an
- // argument from the original argv.
+ // Handle the next argument in the current list. If the stack of argument
+ // lists contains only one element - we are processing an argument from the
+ // original argv.
absl::string_view arg(curr_list.Front());
bool arg_from_argv = input_args.size() == 1;
- // 50. If argument does not start with - or is just "-" - this is
+ // If argument does not start with '-' or is just "-" - this is
// positional argument.
if (!absl::ConsumePrefix(&arg, "-") || arg.empty()) {
ABSL_INTERNAL_CHECK(arg_from_argv,
@@ -739,8 +778,8 @@ std::vector<char*> ParseCommandLineImpl(int argc, char* argv[],
continue;
}
- // 60. Split the current argument on '=' to figure out the argument
- // name and value. If flag name is empty it means we've got "--". value
+ // Split the current argument on '=' to deduce the argument flag name and
+ // value. If flag name is empty it means we've got an "--" argument. Value
// can be empty either if there were no '=' in argument string at all or
// an argument looked like "--foo=". In a latter case is_empty_value is
// true.
@@ -748,10 +787,11 @@ std::vector<char*> ParseCommandLineImpl(int argc, char* argv[],
absl::string_view value;
bool is_empty_value = false;
- std::tie(flag_name, value, is_empty_value) = SplitNameAndValue(arg);
+ std::tie(flag_name, value, is_empty_value) =
+ flags_internal::SplitNameAndValue(arg);
- // 70. "--" alone means what it does for GNU: stop flags parsing. We do
- // not support positional arguments in flagfiles, so we just drop them.
+ // Standalone "--" argument indicates that the rest of the arguments are
+ // positional. We do not support positional arguments in flagfiles.
if (flag_name.empty()) {
ABSL_INTERNAL_CHECK(arg_from_argv,
"Flagfile cannot contain positional argument");
@@ -760,36 +800,36 @@ std::vector<char*> ParseCommandLineImpl(int argc, char* argv[],
break;
}
- // 80. Locate the flag based on flag name. Handle both --foo and --nofoo
+ // Locate the flag based on flag name. Handle both --foo and --nofoo.
CommandLineFlag* flag = nullptr;
bool is_negative = false;
- std::tie(flag, is_negative) = LocateFlag(flag_name);
+ std::tie(flag, is_negative) = flags_internal::LocateFlag(flag_name);
if (flag == nullptr) {
// Usage flags are not modeled as Abseil flags. Locate them separately.
if (flags_internal::DeduceUsageFlags(flag_name, value)) {
continue;
}
-
- if (on_undef_flag != OnUndefinedFlag::kIgnoreUndefined) {
- undefined_flag_names.emplace_back(arg_from_argv,
- std::string(flag_name));
- }
+ unrecognized_flags.emplace_back(arg_from_argv
+ ? UnrecognizedFlag::kFromArgv
+ : UnrecognizedFlag::kFromFlagfile,
+ flag_name);
continue;
}
- // 90. Deduce flag's value (from this or next argument)
+ // Deduce flag's value (from this or next argument).
bool value_success = true;
- std::tie(value_success, value) =
- DeduceFlagValue(*flag, value, is_negative, is_empty_value, &curr_list);
+ std::tie(value_success, value) = flags_internal::DeduceFlagValue(
+ *flag, value, is_negative, is_empty_value, &curr_list);
success &= value_success;
- // 100. Set the located flag to a new new value, unless it is retired.
- // Setting retired flag fails, but we ignoring it here while also reporting
- // access to retired flag.
+ // Set the located flag to a new value, unless it is retired. Setting
+ // retired flag fails, but we ignoring it here while also reporting access
+ // to retired flag.
std::string error;
if (!flags_internal::PrivateHandleAccessor::ParseFrom(
- *flag, value, SET_FLAGS_VALUE, kCommandLine, error)) {
+ *flag, value, flags_internal::SET_FLAGS_VALUE,
+ flags_internal::kCommandLine, error)) {
if (flag->IsRetired()) continue;
flags_internal::ReportUsageError(error, true);
@@ -799,71 +839,62 @@ std::vector<char*> ParseCommandLineImpl(int argc, char* argv[],
}
}
- for (const auto& flag_name : undefined_flag_names) {
- if (CanIgnoreUndefinedFlag(flag_name.second)) continue;
- // Verify if flag_name has the "no" already removed
- std::vector<std::string> flags;
- if (flag_name.first) flags = GetMisspellingHints(flag_name.second);
- if (flags.empty()) {
- flags_internal::ReportUsageError(
- absl::StrCat("Unknown command line flag '", flag_name.second, "'"),
- true);
- } else {
- flags_internal::ReportUsageError(
- absl::StrCat("Unknown command line flag '", flag_name.second,
- "'. Did you mean: ", absl::StrJoin(flags, ", "), " ?"),
- true);
- }
-
- success = false;
- }
-
-#if ABSL_FLAGS_STRIP_NAMES
- if (!success) {
- flags_internal::ReportUsageError(
- "NOTE: command line flags are disabled in this build", true);
- }
-#endif
-
- if (!success) {
- flags_internal::HandleUsageFlags(std::cout,
- ProgramUsageMessage());
- std::exit(1);
- }
-
- if (usage_flag_act == UsageFlagsAction::kHandleUsage) {
- int exit_code = flags_internal::HandleUsageFlags(
- std::cout, ProgramUsageMessage());
-
- if (exit_code != -1) {
- std::exit(exit_code);
- }
- }
-
- ResetGeneratorFlags(flagfile_value);
-
- // Reinstate positional args which were intermixed with flags in the arguments
- // list.
- for (auto arg : positional_args) {
- output_args.push_back(arg);
- }
+ flags_internal::ResetGeneratorFlags(flagfile_value);
// All the remaining arguments are positional.
if (!input_args.empty()) {
for (size_t arg_index = input_args.back().FrontIndex();
arg_index < static_cast<size_t>(argc); ++arg_index) {
- output_args.push_back(argv[arg_index]);
+ positional_args.push_back(argv[arg_index]);
}
}
// Trim and sort the vector.
specified_flags->shrink_to_fit();
std::sort(specified_flags->begin(), specified_flags->end(),
- SpecifiedFlagsCompare{});
- return output_args;
+ flags_internal::SpecifiedFlagsCompare{});
+
+ // Filter out unrecognized flags, which are ok to ignore.
+ std::vector<UnrecognizedFlag> filtered;
+ filtered.reserve(unrecognized_flags.size());
+ for (const auto& unrecognized : unrecognized_flags) {
+ if (flags_internal::CanIgnoreUndefinedFlag(unrecognized.flag_name))
+ continue;
+ filtered.push_back(unrecognized);
+ }
+
+ std::swap(unrecognized_flags, filtered);
+
+ return success;
}
-} // namespace flags_internal
+// --------------------------------------------------------------------
+
+void ReportUnrecognizedFlags(
+ const std::vector<UnrecognizedFlag>& unrecognized_flags,
+ bool report_fatal_error) {
+ for (const auto& unrecognized : unrecognized_flags) {
+ // Verify if flag_name has the "no" already removed
+ std::vector<std::string> misspelling_hints;
+ if (unrecognized.source == UnrecognizedFlag::kFromArgv) {
+ misspelling_hints =
+ flags_internal::GetMisspellingHints(unrecognized.flag_name);
+ }
+
+ if (misspelling_hints.empty()) {
+ flags_internal::ReportUsageError(
+ absl::StrCat("Unknown command line flag '", unrecognized.flag_name,
+ "'"),
+ report_fatal_error);
+ } else {
+ flags_internal::ReportUsageError(
+ absl::StrCat("Unknown command line flag '", unrecognized.flag_name,
+ "'. Did you mean: ",
+ absl::StrJoin(misspelling_hints, ", "), " ?"),
+ report_fatal_error);
+ }
+ }
+}
// --------------------------------------------------------------------
diff --git a/absl/flags/parse.h b/absl/flags/parse.h
index 929de2cb..a5629375 100644
--- a/absl/flags/parse.h
+++ b/absl/flags/parse.h
@@ -23,35 +23,103 @@
#ifndef ABSL_FLAGS_PARSE_H_
#define ABSL_FLAGS_PARSE_H_
+#include <string>
#include <vector>
#include "absl/base/config.h"
#include "absl/flags/internal/parse.h"
+#include "absl/strings/string_view.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
+// This type represent information about an unrecognized flag in the command
+// line.
+struct UnrecognizedFlag {
+ enum Source { kFromArgv, kFromFlagfile };
+
+ explicit UnrecognizedFlag(Source s, absl::string_view f)
+ : source(s), flag_name(f) {}
+ // This field indicates where we found this flag: on the original command line
+ // or read in some flag file.
+ Source source;
+ // Name of the flag we did not recognize in --flag_name=value or --flag_name.
+ std::string flag_name;
+};
+
+inline bool operator==(const UnrecognizedFlag& lhs,
+ const UnrecognizedFlag& rhs) {
+ return lhs.source == rhs.source && lhs.flag_name == rhs.flag_name;
+}
+
+// ParseAbseilFlagsOnly()
+//
+// Parses a list of command-line arguments, passed in the `argc` and `argv[]`
+// parameters, into a set of Abseil Flag values, returning any unparsed
+// arguments in `positional_args` and `unrecognized_flags` output parameters.
+//
+// This function classifies all the arguments (including content of the
+// flagfiles, if any) into one of the following groups:
+//
+// * arguments specified as "--flag=value" or "--flag value" that match
+// registered or built-in Abseil Flags. These are "Abseil Flag arguments."
+// * arguments specified as "--flag" that are unrecognized as Abseil Flags
+// * arguments that are not specified as "--flag" are positional arguments
+// * arguments that follow the flag-terminating delimiter (`--`) are also
+// treated as positional arguments regardless of their syntax.
+//
+// All of the deduced Abseil Flag arguments are then parsed into their
+// corresponding flag values.
+//
+// All the remaining positional arguments including original program name
+// (argv[0]) are are returned in the `positional_args` output parameter.
+//
+// All unrecognized flags that are not otherwise ignored are returned in the
+// `unrecognized_flags` output parameter. Note that the special `undefok`
+// flag allows you to specify flags which can be safely ignored; `undefok`
+// specifies these flags as a comma-separated list. Any unrecognized flags
+// that appear within `undefok` will therefore be ignored and not included in
+// the `unrecognized_flag` output parameter.
+//
+// This function returns true if no syntax errors were found on the command line
+// or in the referenced flag files. Unrecognized flags do not cause this routine
+// to return false.
+bool ParseAbseilFlagsOnly(int argc, char* argv[],
+ std::vector<char*>& positional_args,
+ std::vector<UnrecognizedFlag>& unrecognized_flags);
+
+// ReportUnrecognizedFlags()
+//
+// Reports an error for all non-ignored unrecognized flags in the provided
+// `unrecognized_flags` list.
+//
+// If `report_fatal_error` is true, the fatal error is reported and program is
+// aborted. Otherwise non-fatal error is reported for all flags.
+//
+// This function returns true if any non-ignored unrecognized flags were
+// located in the list and false otherwise.
+void ReportUnrecognizedFlags(
+ const std::vector<UnrecognizedFlag>& unrecognized_flags,
+ bool report_fatal_error);
+
// ParseCommandLine()
//
-// Parses the set of command-line arguments passed in the `argc` (argument
-// count) and `argv[]` (argument vector) parameters from `main()`, assigning
-// values to any defined Abseil flags. (Any arguments passed after the
-// flag-terminating delimiter (`--`) are treated as positional arguments and
-// ignored.)
-//
-// Any command-line flags (and arguments to those flags) are parsed into Abseil
-// Flag values, if those flags are defined. Any undefined flags will either
-// return an error, or be ignored if that flag is designated using `undefok` to
-// indicate "undefined is OK."
-//
-// Any command-line positional arguments not part of any command-line flag (or
-// arguments to a flag) are returned in a vector, with the program invocation
-// name at position 0 of that vector. (Note that this includes positional
-// arguments after the flag-terminating delimiter `--`.)
-//
-// After all flags and flag arguments are parsed, this function looks for any
-// built-in usage flags (e.g. `--help`), and if any were specified, it reports
-// help messages and then exits the program.
+// First parses Abseil Flags only from the command line according to the
+// description in `ParseAbseilFlagsOnly`. In addition this function handles
+// unrecognized and usage flags.
+//
+// If any unrecognized flags are located they are reported using
+// `ReportUnrecognizedFlags`.
+//
+// If any errors detected during command line parsing, this routine reports a
+// usage message and aborts the program.
+//
+// If any built-in usage flags were specified on the command line (e.g.
+// `--help`), this function reports help messages and then gracefully exits the
+// program.
+//
+// This function returns all the remaining positional arguments collected by
+// `ParseAbseilFlagsOnly`.
std::vector<char*> ParseCommandLine(int argc, char* argv[]);
ABSL_NAMESPACE_END
diff --git a/absl/flags/parse_test.cc b/absl/flags/parse_test.cc
index 18a01377..c46bb46e 100644
--- a/absl/flags/parse_test.cc
+++ b/absl/flags/parse_test.cc
@@ -20,13 +20,13 @@
#include <cstddef>
#include <fstream>
#include <string>
+#include <utility>
#include <vector>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/base/internal/raw_logging.h"
#include "absl/base/internal/scoped_set_env.h"
-#include "absl/flags/declare.h"
#include "absl/flags/flag.h"
#include "absl/flags/internal/parse.h"
#include "absl/flags/internal/usage.h"
@@ -199,7 +199,7 @@ constexpr const char* const ff2_data[] = {
// Builds flagfile flag in the flagfile_flag buffer and returns it. This
// function also creates a temporary flagfile based on FlagfileData input.
// We create a flagfile in a temporary directory with the name specified in
-// FlagfileData and populate it with lines specifed in FlagfileData. If $0 is
+// FlagfileData and populate it with lines specified in FlagfileData. If $0 is
// referenced in any of the lines in FlagfileData they are replaced with
// temporary directory location. This way we can test inclusion of one flagfile
// from another flagfile.
@@ -257,6 +257,17 @@ std::vector<char*> InvokeParse(const char* (&in_argv)[N]) {
// --------------------------------------------------------------------
template <int N>
+bool InvokeParseAbslOnly(const char* (&in_argv)[N]) {
+ std::vector<char*> positional_args;
+ std::vector<absl::UnrecognizedFlag> unrecognized_flags;
+
+ return absl::ParseAbseilFlagsOnly(N, const_cast<char**>(in_argv),
+ positional_args, unrecognized_flags);
+}
+
+// --------------------------------------------------------------------
+
+template <int N>
void TestParse(const char* (&in_argv)[N], int int_flag_value,
double double_flag_val, absl::string_view string_flag_val,
bool bool_flag_val, int exp_position_args = 0) {
@@ -854,26 +865,6 @@ TEST_F(ParseTest, TestReadingFlagsFromEnvMoxedWithRegularFlags) {
// --------------------------------------------------------------------
-TEST_F(ParseTest, TestIgnoreUndefinedFlags) {
- const char* in_args1[] = {
- "testbin",
- "arg1",
- "--undef_flag=aa",
- "--int_flag=21",
- };
-
- auto out_args1 = flags::ParseCommandLineImpl(
- 4, const_cast<char**>(in_args1), flags::UsageFlagsAction::kHandleUsage,
- flags::OnUndefinedFlag::kIgnoreUndefined);
-
- EXPECT_THAT(out_args1, ElementsAreArray({absl::string_view("testbin"),
- absl::string_view("arg1")}));
-
- EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 21);
-}
-
-// --------------------------------------------------------------------
-
TEST_F(ParseDeathTest, TestSimpleHelpFlagHandling) {
const char* in_args1[] = {
"testbin",
@@ -888,12 +879,16 @@ TEST_F(ParseDeathTest, TestSimpleHelpFlagHandling) {
"--int_flag=3",
};
- auto out_args2 = flags::ParseCommandLineImpl(
- 3, const_cast<char**>(in_args2), flags::UsageFlagsAction::kIgnoreUsage,
- flags::OnUndefinedFlag::kAbortIfUndefined);
+ InvokeParseAbslOnly(in_args2);
EXPECT_EQ(flags::GetFlagsHelpMode(), flags::HelpMode::kImportant);
EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 3);
+
+ const char* in_args3[] = {"testbin", "--help", "some_positional_arg"};
+
+ InvokeParseAbslOnly(in_args3);
+
+ EXPECT_EQ(flags::GetFlagsHelpMode(), flags::HelpMode::kImportant);
}
// --------------------------------------------------------------------
@@ -904,20 +899,10 @@ TEST_F(ParseDeathTest, TestSubstringHelpFlagHandling) {
"--help=abcd",
};
- auto out_args1 = flags::ParseCommandLineImpl(
- 2, const_cast<char**>(in_args1), flags::UsageFlagsAction::kIgnoreUsage,
- flags::OnUndefinedFlag::kAbortIfUndefined);
+ InvokeParseAbslOnly(in_args1);
EXPECT_EQ(flags::GetFlagsHelpMode(), flags::HelpMode::kMatch);
EXPECT_EQ(flags::GetFlagsHelpMatchSubstr(), "abcd");
-
- const char* in_args2[] = {"testbin", "--help", "some_positional_arg"};
-
- auto out_args2 = flags::ParseCommandLineImpl(
- 3, const_cast<char**>(in_args2), flags::UsageFlagsAction::kIgnoreUsage,
- flags::OnUndefinedFlag::kAbortIfUndefined);
-
- EXPECT_EQ(flags::GetFlagsHelpMode(), flags::HelpMode::kImportant);
}
// --------------------------------------------------------------------
@@ -942,4 +927,106 @@ TEST_F(ParseTest, WasPresentOnCommandLine) {
// --------------------------------------------------------------------
+TEST_F(ParseTest, ParseAbseilFlagsOnlySuccess) {
+ const char* in_args[] = {
+ "testbin",
+ "arg1",
+ "--bool_flag",
+ "--int_flag=211",
+ "arg2",
+ "--double_flag=1.1",
+ "--undef_flag1",
+ "--undef_flag2=123",
+ "--string_flag",
+ "asd",
+ "--",
+ "--some_flag",
+ "arg4",
+ };
+
+ std::vector<char*> positional_args;
+ std::vector<absl::UnrecognizedFlag> unrecognized_flags;
+
+ EXPECT_TRUE(absl::ParseAbseilFlagsOnly(13, const_cast<char**>(in_args),
+ positional_args, unrecognized_flags));
+ EXPECT_THAT(positional_args,
+ ElementsAreArray(
+ {absl::string_view("testbin"), absl::string_view("arg1"),
+ absl::string_view("arg2"), absl::string_view("--some_flag"),
+ absl::string_view("arg4")}));
+ EXPECT_THAT(unrecognized_flags,
+ ElementsAreArray(
+ {absl::UnrecognizedFlag(absl::UnrecognizedFlag::kFromArgv,
+ "undef_flag1"),
+ absl::UnrecognizedFlag(absl::UnrecognizedFlag::kFromArgv,
+ "undef_flag2")}));
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(ParseTest, ParseAbseilFlagsOnlyFailure) {
+ const char* in_args[] = {
+ "testbin",
+ "--int_flag=21.1",
+ };
+
+ std::vector<char*> positional_args;
+ std::vector<absl::UnrecognizedFlag> unrecognized_flags;
+
+ EXPECT_FALSE(absl::ParseAbseilFlagsOnly(2, const_cast<char**>(in_args),
+ positional_args, unrecognized_flags));
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(ParseTest, UndefOkFlagsAreIgnored) {
+ const char* in_args[] = {
+ "testbin", "--undef_flag1",
+ "--undef_flag2=123", "--undefok=undef_flag2",
+ "--undef_flag3", "value",
+ };
+
+ std::vector<char*> positional_args;
+ std::vector<absl::UnrecognizedFlag> unrecognized_flags;
+
+ EXPECT_TRUE(absl::ParseAbseilFlagsOnly(6, const_cast<char**>(in_args),
+ positional_args, unrecognized_flags));
+ EXPECT_THAT(positional_args, ElementsAreArray({absl::string_view("testbin"),
+ absl::string_view("value")}));
+ EXPECT_THAT(unrecognized_flags,
+ ElementsAreArray(
+ {absl::UnrecognizedFlag(absl::UnrecognizedFlag::kFromArgv,
+ "undef_flag1"),
+ absl::UnrecognizedFlag(absl::UnrecognizedFlag::kFromArgv,
+ "undef_flag3")}));
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(ParseTest, AllUndefOkFlagsAreIgnored) {
+ const char* in_args[] = {
+ "testbin",
+ "--undef_flag1",
+ "--undef_flag2=123",
+ "--undefok=undef_flag2,undef_flag1,undef_flag3",
+ "--undef_flag3",
+ "value",
+ "--",
+ "--undef_flag4",
+ };
+
+ std::vector<char*> positional_args;
+ std::vector<absl::UnrecognizedFlag> unrecognized_flags;
+
+ EXPECT_TRUE(absl::ParseAbseilFlagsOnly(8, const_cast<char**>(in_args),
+ positional_args, unrecognized_flags));
+ EXPECT_THAT(positional_args,
+ ElementsAreArray({absl::string_view("testbin"),
+ absl::string_view("value"),
+ absl::string_view("--undef_flag4")}));
+ EXPECT_THAT(unrecognized_flags, testing::IsEmpty());
+}
+
+// --------------------------------------------------------------------
+
} // namespace
diff --git a/absl/synchronization/internal/futex.h b/absl/synchronization/internal/futex.h
index 9cf9841d..62bb40f7 100644
--- a/absl/synchronization/internal/futex.h
+++ b/absl/synchronization/internal/futex.h
@@ -16,9 +16,7 @@
#include "absl/base/config.h"
-#ifdef _WIN32
-#include <windows.h>
-#else
+#ifndef _WIN32
#include <sys/time.h>
#include <unistd.h>
#endif
@@ -85,34 +83,60 @@ namespace synchronization_internal {
class FutexImpl {
public:
- static int WaitUntil(std::atomic<int32_t> *v, int32_t val,
+ // Atomically check that `*v == val`, and if it is, then sleep until the
+ // timeout `t` has been reached, or until woken by `Wake()`.
+ static int WaitUntil(std::atomic<int32_t>* v, int32_t val,
KernelTimeout t) {
- long err = 0; // NOLINT(runtime/int)
- if (t.has_timeout()) {
- // https://locklessinc.com/articles/futex_cheat_sheet/
- // Unlike FUTEX_WAIT, FUTEX_WAIT_BITSET uses absolute time.
- struct timespec abs_timeout = t.MakeAbsTimespec();
- // Atomically check that the futex value is still 0, and if it
- // is, sleep until abs_timeout or until woken by FUTEX_WAKE.
- err = syscall(
- SYS_futex, reinterpret_cast<int32_t *>(v),
- FUTEX_WAIT_BITSET | FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME, val,
- &abs_timeout, nullptr, FUTEX_BITSET_MATCH_ANY);
+ if (!t.has_timeout()) {
+ return Wait(v, val);
+ } else if (t.is_absolute_timeout()) {
+ auto abs_timespec = t.MakeAbsTimespec();
+ return WaitAbsoluteTimeout(v, val, &abs_timespec);
} else {
- // Atomically check that the futex value is still 0, and if it
- // is, sleep until woken by FUTEX_WAKE.
- err = syscall(SYS_futex, reinterpret_cast<int32_t *>(v),
- FUTEX_WAIT | FUTEX_PRIVATE_FLAG, val, nullptr);
+ auto rel_timespec = t.MakeRelativeTimespec();
+ return WaitRelativeTimeout(v, val, &rel_timespec);
}
- if (ABSL_PREDICT_FALSE(err != 0)) {
+ }
+
+ // Atomically check that `*v == val`, and if it is, then sleep until the until
+ // woken by `Wake()`.
+ static int Wait(std::atomic<int32_t>* v, int32_t val) {
+ return WaitAbsoluteTimeout(v, val, nullptr);
+ }
+
+ // Atomically check that `*v == val`, and if it is, then sleep until
+ // CLOCK_REALTIME reaches `*abs_timeout`, or until woken by `Wake()`.
+ static int WaitAbsoluteTimeout(std::atomic<int32_t>* v, int32_t val,
+ const struct timespec* abs_timeout) {
+ // https://locklessinc.com/articles/futex_cheat_sheet/
+ // Unlike FUTEX_WAIT, FUTEX_WAIT_BITSET uses absolute time.
+ auto err =
+ syscall(SYS_futex, reinterpret_cast<int32_t*>(v),
+ FUTEX_WAIT_BITSET | FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME,
+ val, abs_timeout, nullptr, FUTEX_BITSET_MATCH_ANY);
+ if (err != 0) {
+ return -errno;
+ }
+ return 0;
+ }
+
+ // Atomically check that `*v == val`, and if it is, then sleep until
+ // `*rel_timeout` has elapsed, or until woken by `Wake()`.
+ static int WaitRelativeTimeout(std::atomic<int32_t>* v, int32_t val,
+ const struct timespec* rel_timeout) {
+ // Atomically check that the futex value is still 0, and if it
+ // is, sleep until abs_timeout or until woken by FUTEX_WAKE.
+ auto err = syscall(SYS_futex, reinterpret_cast<int32_t*>(v),
+ FUTEX_PRIVATE_FLAG, val, rel_timeout);
+ if (err != 0) {
return -errno;
}
return 0;
}
- static int Wake(std::atomic<int32_t> *v, int32_t count) {
- // NOLINTNEXTLINE(runtime/int)
- long err = syscall(SYS_futex, reinterpret_cast<int32_t*>(v),
+ // Wakes at most `count` waiters that have entered the sleep state on `v`.
+ static int Wake(std::atomic<int32_t>* v, int32_t count) {
+ auto err = syscall(SYS_futex, reinterpret_cast<int32_t*>(v),
FUTEX_WAKE | FUTEX_PRIVATE_FLAG, count);
if (ABSL_PREDICT_FALSE(err < 0)) {
return -errno;
diff --git a/absl/synchronization/internal/waiter.cc b/absl/synchronization/internal/waiter.cc
index f2051d67..883ae811 100644
--- a/absl/synchronization/internal/waiter.cc
+++ b/absl/synchronization/internal/waiter.cc
@@ -67,11 +67,9 @@ static void MaybeBecomeIdle() {
#if ABSL_WAITER_MODE == ABSL_WAITER_MODE_FUTEX
-Waiter::Waiter() {
- futex_.store(0, std::memory_order_relaxed);
-}
+Waiter::Waiter() : futex_(0) {}
-bool Waiter::Wait(KernelTimeout t) {
+bool Waiter::WaitAbsoluteTimeout(KernelTimeout t) {
// Loop until we can atomically decrement futex from a positive
// value, waiting on a futex while we believe it is zero.
// Note that, since the thread ticker is just reset, we don't need to check
@@ -90,7 +88,88 @@ bool Waiter::Wait(KernelTimeout t) {
}
if (!first_pass) MaybeBecomeIdle();
- const int err = Futex::WaitUntil(&futex_, 0, t);
+ auto abs_timeout = t.MakeAbsTimespec();
+ const int err = Futex::WaitAbsoluteTimeout(&futex_, 0, &abs_timeout);
+ if (err != 0) {
+ if (err == -EINTR || err == -EWOULDBLOCK) {
+ // Do nothing, the loop will retry.
+ } else if (err == -ETIMEDOUT) {
+ return false;
+ } else {
+ ABSL_RAW_LOG(FATAL, "Futex operation failed with error %d\n", err);
+ }
+ }
+ first_pass = false;
+ }
+}
+
+#if defined(CLOCK_MONOTONIC)
+
+// Subtracts the timespec `sub` from `in` if the result would not be negative,
+// and returns true. Returns false if the result would be negative, and leaves
+// `in` unchanged.
+static bool TimespecSubtract(struct timespec& in, const struct timespec& sub) {
+ if (in.tv_sec < sub.tv_sec) {
+ return false;
+ }
+ if (in.tv_nsec < sub.tv_nsec) {
+ if (in.tv_sec == sub.tv_sec) {
+ return false;
+ }
+ // Borrow from tv_sec.
+ in.tv_sec -= 1;
+ in.tv_nsec += 1'000'000'000;
+ }
+ in.tv_sec -= sub.tv_sec;
+ in.tv_nsec -= sub.tv_nsec;
+ return true;
+}
+
+// On some platforms a background thread periodically calls `Poke()` to briefly
+// wake waiter threads so that they may call `MaybeBecomeIdle()`. This means
+// that `WaitRelativeTimeout()` differs slightly from `WaitAbsoluteTimeout()`
+// because it must adjust the timeout by the amount of time that it has already
+// slept.
+bool Waiter::WaitRelativeTimeout(KernelTimeout t) {
+ struct timespec start;
+ ABSL_RAW_CHECK(clock_gettime(CLOCK_MONOTONIC, &start) == 0,
+ "clock_gettime() failed");
+
+ // Loop until we can atomically decrement futex from a positive
+ // value, waiting on a futex while we believe it is zero.
+ // Note that, since the thread ticker is just reset, we don't need to check
+ // whether the thread is idle on the very first pass of the loop.
+ bool first_pass = true;
+
+ while (true) {
+ int32_t x = futex_.load(std::memory_order_relaxed);
+ while (x != 0) {
+ if (!futex_.compare_exchange_weak(x, x - 1,
+ std::memory_order_acquire,
+ std::memory_order_relaxed)) {
+ continue; // Raced with someone, retry.
+ }
+ return true; // Consumed a wakeup, we are done.
+ }
+
+ auto relative_timeout = t.MakeRelativeTimespec();
+ if (!first_pass) {
+ MaybeBecomeIdle();
+
+ // Adjust relative_timeout for `Poke()`s.
+ struct timespec now;
+ ABSL_RAW_CHECK(clock_gettime(CLOCK_MONOTONIC, &now) == 0,
+ "clock_gettime() failed");
+ // If TimespecSubstract(now, start) returns false, then the clock isn't
+ // truly monotonic.
+ if (TimespecSubtract(now, start)) {
+ if (!TimespecSubtract(relative_timeout, now)) {
+ return false; // Timeout.
+ }
+ }
+ }
+
+ const int err = Futex::WaitRelativeTimeout(&futex_, 0, &relative_timeout);
if (err != 0) {
if (err == -EINTR || err == -EWOULDBLOCK) {
// Do nothing, the loop will retry.
@@ -104,6 +183,23 @@ bool Waiter::Wait(KernelTimeout t) {
}
}
+#else // CLOCK_MONOTONIC
+
+// No support for CLOCK_MONOTONIC.
+// KernelTimeout will automatically convert to an absolute timeout.
+bool Waiter::WaitRelativeTimeout(KernelTimeout t) {
+ return WaitAbsoluteTimeout(t);
+}
+
+#endif // CLOCK_MONOTONIC
+
+bool Waiter::Wait(KernelTimeout t) {
+ if (t.is_absolute_timeout()) {
+ return WaitAbsoluteTimeout(t);
+ }
+ return WaitRelativeTimeout(t);
+}
+
void Waiter::Post() {
if (futex_.fetch_add(1, std::memory_order_release) == 0) {
// We incremented from 0, need to wake a potential waiter.
diff --git a/absl/synchronization/internal/waiter.h b/absl/synchronization/internal/waiter.h
index b8adfeb5..c206cc3f 100644
--- a/absl/synchronization/internal/waiter.h
+++ b/absl/synchronization/internal/waiter.h
@@ -110,6 +110,9 @@ class Waiter {
~Waiter() = delete;
#if ABSL_WAITER_MODE == ABSL_WAITER_MODE_FUTEX
+ bool WaitAbsoluteTimeout(KernelTimeout t);
+ bool WaitRelativeTimeout(KernelTimeout t);
+
// Futexes are defined by specification to be 32-bits.
// Thus std::atomic<int32_t> must be just an int32_t with lockfree methods.
std::atomic<int32_t> futex_;
diff --git a/absl/synchronization/mutex.cc b/absl/synchronization/mutex.cc
index ef6d063e..a8911614 100644
--- a/absl/synchronization/mutex.cc
+++ b/absl/synchronization/mutex.cc
@@ -635,21 +635,6 @@ void Mutex::InternalAttemptToUseMutexInFatalSignalHandler() {
std::memory_order_release);
}
-// --------------------------time support
-
-// Return the current time plus the timeout. Use the same clock as
-// PerThreadSem::Wait() for consistency. Unfortunately, we don't have
-// such a choice when a deadline is given directly.
-static absl::Time DeadlineFromTimeout(absl::Duration timeout) {
-#ifndef _WIN32
- struct timeval tv;
- gettimeofday(&tv, nullptr);
- return absl::TimeFromTimeval(tv) + timeout;
-#else
- return absl::Now() + timeout;
-#endif
-}
-
// --------------------------Mutexes
// In the layout below, the msb of the bottom byte is currently unused. Also,
@@ -1549,7 +1534,13 @@ void Mutex::LockWhen(const Condition &cond) {
}
bool Mutex::LockWhenWithTimeout(const Condition &cond, absl::Duration timeout) {
- return LockWhenWithDeadline(cond, DeadlineFromTimeout(timeout));
+ ABSL_TSAN_MUTEX_PRE_LOCK(this, 0);
+ GraphId id = DebugOnlyDeadlockCheck(this);
+ bool res = LockSlowWithDeadline(kExclusive, &cond,
+ KernelTimeout(timeout), 0);
+ DebugOnlyLockEnter(this, id);
+ ABSL_TSAN_MUTEX_POST_LOCK(this, 0, 0);
+ return res;
}
bool Mutex::LockWhenWithDeadline(const Condition &cond, absl::Time deadline) {
@@ -1572,7 +1563,12 @@ void Mutex::ReaderLockWhen(const Condition &cond) {
bool Mutex::ReaderLockWhenWithTimeout(const Condition &cond,
absl::Duration timeout) {
- return ReaderLockWhenWithDeadline(cond, DeadlineFromTimeout(timeout));
+ ABSL_TSAN_MUTEX_PRE_LOCK(this, __tsan_mutex_read_lock);
+ GraphId id = DebugOnlyDeadlockCheck(this);
+ bool res = LockSlowWithDeadline(kShared, &cond, KernelTimeout(timeout), 0);
+ DebugOnlyLockEnter(this, id);
+ ABSL_TSAN_MUTEX_POST_LOCK(this, __tsan_mutex_read_lock, 0);
+ return res;
}
bool Mutex::ReaderLockWhenWithDeadline(const Condition &cond,
@@ -1597,7 +1593,18 @@ void Mutex::Await(const Condition &cond) {
}
bool Mutex::AwaitWithTimeout(const Condition &cond, absl::Duration timeout) {
- return AwaitWithDeadline(cond, DeadlineFromTimeout(timeout));
+ if (cond.Eval()) { // condition already true; nothing to do
+ if (kDebugMode) {
+ this->AssertReaderHeld();
+ }
+ return true;
+ }
+
+ KernelTimeout t{timeout};
+ bool res = this->AwaitCommon(cond, t);
+ ABSL_RAW_CHECK(res || t.has_timeout(),
+ "condition untrue on return from Await");
+ return res;
}
bool Mutex::AwaitWithDeadline(const Condition &cond, absl::Time deadline) {
@@ -2663,7 +2670,7 @@ bool CondVar::WaitCommon(Mutex *mutex, KernelTimeout t) {
}
bool CondVar::WaitWithTimeout(Mutex *mu, absl::Duration timeout) {
- return WaitWithDeadline(mu, DeadlineFromTimeout(timeout));
+ return WaitCommon(mu, KernelTimeout(timeout));
}
bool CondVar::WaitWithDeadline(Mutex *mu, absl::Time deadline) {