diff --git a/absl/base/attributes.h b/absl/base/attributes.h index b7826e7..91bf954 100644 --- a/absl/base/attributes.h +++ b/absl/base/attributes.h
@@ -779,4 +779,18 @@ #define ABSL_ATTRIBUTE_TRIVIAL_ABI #endif +// ABSL_ATTRIBUTE_NO_UNIQUE_ADDRESS +// +// Indicates a data member can be optimized to occupy no space (if it is empty) +// and/or its tail padding can be used for other members. +// +// For code that is assured to only build with C++20 or later, prefer using +// the standard attribute `[[no_unique_address]]` directly instead of this +// macro. +#if ABSL_HAVE_CPP_ATTRIBUTE(no_unique_address) +#define ABSL_ATTRIBUTE_NO_UNIQUE_ADDRESS [[no_unique_address]] +#else +#define ABSL_ATTRIBUTE_NO_UNIQUE_ADDRESS +#endif + #endif // ABSL_BASE_ATTRIBUTES_H_
diff --git a/absl/base/internal/thread_identity.h b/absl/base/internal/thread_identity.h index 463acbc..b99c957 100644 --- a/absl/base/internal/thread_identity.h +++ b/absl/base/internal/thread_identity.h
@@ -170,7 +170,10 @@ // // Does not malloc(*), and is async-signal safe. // [*] Technically pthread_setspecific() does malloc on first use; however this -// is handled internally within tcmalloc's initialization already. +// is handled internally within tcmalloc's initialization already. Note that +// darwin does *not* use tcmalloc, so this can catch you if using MallocHooks +// on Apple platforms. Whatever function is calling your MallocHooks will need +// to watch for recursion on Apple platforms. // // New ThreadIdentity objects can be constructed and associated with a thread // by calling GetOrCreateCurrentThreadIdentity() in per-thread-sem.h.
diff --git a/absl/crc/internal/crc.cc b/absl/crc/internal/crc.cc index 337a173..8720216 100644 --- a/absl/crc/internal/crc.cc +++ b/absl/crc/internal/crc.cc
@@ -261,7 +261,7 @@ const uint8_t* e = p + length; uint32_t l = *crc; - auto step_one_byte = [this, &p, &l] () { + auto step_one_byte = [this, &p, &l]() { int c = (l & 0xff) ^ *p++; l = this->table0_[c] ^ (l >> 8); }; @@ -359,7 +359,7 @@ void CRC32::ExtendByZeroesImpl(uint32_t* crc, size_t length, const uint32_t zeroes_table[256], - const uint32_t poly_table[256]) const { + const uint32_t poly_table[256]) { if (length != 0) { uint32_t l = *crc; // For each ZEROES_BASE_LG bits in length
diff --git a/absl/crc/internal/crc_internal.h b/absl/crc/internal/crc_internal.h index 9f08091..3a04788 100644 --- a/absl/crc/internal/crc_internal.h +++ b/absl/crc/internal/crc_internal.h
@@ -118,9 +118,9 @@ // // These will be set to reverse_zeroes_ and reverse_table0_ for Unextend, and // CRC32::zeroes_ and CRC32::table0_ for Extend. - void ExtendByZeroesImpl(uint32_t* crc, size_t length, - const uint32_t zeroes_table[256], - const uint32_t poly_table[256]) const; + static void ExtendByZeroesImpl(uint32_t* crc, size_t length, + const uint32_t zeroes_table[256], + const uint32_t poly_table[256]); uint32_t table0_[256]; // table of byte extensions uint32_t zeroes_[256]; // table of zero extensions
diff --git a/absl/flags/parse.cc b/absl/flags/parse.cc index 768652d..236d31b 100644 --- a/absl/flags/parse.cc +++ b/absl/flags/parse.cc
@@ -19,9 +19,9 @@ #include <algorithm> #include <cstdint> +#include <cstdlib> #include <fstream> #include <iostream> -#include <iterator> #include <string> #include <tuple> #include <utility> @@ -278,7 +278,7 @@ 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); @@ -599,6 +599,34 @@ return false; } +// -------------------------------------------------------------------- + +void ReportUnrecognizedFlags( + const std::vector<UnrecognizedFlag>& unrecognized_flags, + bool report_as_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_as_fatal_error); + } else { + flags_internal::ReportUsageError( + absl::StrCat("Unknown command line flag '", unrecognized.flag_name, + "'. Did you mean: ", + absl::StrJoin(misspelling_hints, ", "), " ?"), + report_as_fatal_error); + } + } +} + } // namespace // -------------------------------------------------------------------- @@ -664,40 +692,79 @@ // -------------------------------------------------------------------- std::vector<char*> ParseCommandLineImpl(int argc, char* argv[], - UsageFlagsAction usage_flag_act, - OnUndefinedFlag on_undef_flag) { + UsageFlagsAction usage_flag_action, + OnUndefinedFlag undef_flag_action) { + std::vector<char*> positional_args; + std::vector<UnrecognizedFlag> unrecognized_flags; + + bool parse_successful = absl::ParseAbseilFlagsOnly( + argc, argv, positional_args, unrecognized_flags); + + if (undef_flag_action != OnUndefinedFlag::kIgnoreUndefined) { + if (parse_successful && + undef_flag_action == OnUndefinedFlag::kAbortIfUndefined) { + if (!unrecognized_flags.empty()) { parse_successful = false; } + } + + flags_internal::ReportUnrecognizedFlags( + unrecognized_flags, + !parse_successful && + (undef_flag_action == OnUndefinedFlag::kAbortIfUndefined)); + } + +#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]"); - // 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. + using flags_internal::ArgsList; + using flags_internal::specified_flags; + + std::vector<std::string> flagfile_value; + std::vector<ArgsList> input_args; + + // Once parsing has started we will not allow more flag registrations. flags_internal::FinalizeRegistry(); // This routine does not return anything since we abort on failure. - CheckDefaultValuesParsingRoundtrip(); + flags_internal::CheckDefaultValuesParsingRoundtrip(); - 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)); - - // 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; + 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 +776,15 @@ // 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 +792,13 @@ 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 +808,8 @@ 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 +817,11 @@ 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 +830,36 @@ 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 +869,41 @@ } } - 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) { + flags_internal::ReportUnrecognizedFlags(unrecognized_flags, true); +} // --------------------------------------------------------------------
diff --git a/absl/flags/parse.h b/absl/flags/parse.h index 929de2c..9732727 100644 --- a/absl/flags/parse.h +++ b/absl/flags/parse.h
@@ -23,35 +23,96 @@ #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 to `stderr` for all non-ignored unrecognized flags in +// the provided `unrecognized_flags` list. +void ReportUnrecognizedFlags( + const std::vector<UnrecognizedFlag>& unrecognized_flags); + // 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.) +// First parses Abseil Flags only from the command line according to the +// description in `ParseAbseilFlagsOnly`. In addition this function handles +// unrecognized and usage flags. // -// 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." +// If any unrecognized flags are located they are reported using +// `ReportUnrecognizedFlags`. // -// 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 `--`.) +// If any errors detected during command line parsing, this routine reports a +// usage message and aborts the program. // -// 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. +// 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 18a0137..c46bb46 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 @@ // 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 @@ // -------------------------------------------------------------------- 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, 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 @@ "--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 @@ "--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, 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/hash/hash.h b/absl/hash/hash.h index 74e2d7c..956befa 100644 --- a/absl/hash/hash.h +++ b/absl/hash/hash.h
@@ -110,9 +110,10 @@ // * std::unique_ptr and std::shared_ptr // * All string-like types including: // * absl::Cord -// * std::string -// * std::string_view (as well as any instance of std::basic_string that -// uses char and std::char_traits) +// * std::string (as well as any instance of std::basic_string that +// uses one of {char, wchar_t, char16_t, char32_t} and its associated +// std::char_traits) +// * std::string_view // * All the standard sequence containers (provided the elements are hashable) // * All the standard associative containers (provided the elements are // hashable)
diff --git a/absl/hash/internal/hash.h b/absl/hash/internal/hash.h index 61970e0..a22a537 100644 --- a/absl/hash/internal/hash.h +++ b/absl/hash/internal/hash.h
@@ -516,14 +516,14 @@ // the same character sequence. These types are: // // - `absl::Cord` -// - `std::string` (and std::basic_string<char, std::char_traits<char>, A> for -// any allocator A) +// - `std::string` (and std::basic_string<T, std::char_traits<T>, A> for +// any allocator A and any T in {char, wchar_t, char16_t, char32_t}) // - `absl::string_view` and `std::string_view` // -// For simplicity, we currently support only `char` strings. This support may -// be broadened, if necessary, but with some caution - this overload would -// misbehave in cases where the traits' `eq()` member isn't equivalent to `==` -// on the underlying character type. +// For simplicity, we currently support only strings built on `char`, `wchar_t`, +// `char16_t`, or `char32_t`. This support may be broadened, if necessary, but +// with some caution - this overload would misbehave in cases where the traits' +// `eq()` member isn't equivalent to `==` on the underlying character type. template <typename H> H AbslHashValue(H hash_state, absl::string_view str) { return H::combine(
diff --git a/absl/strings/ascii.cc b/absl/strings/ascii.cc index 868df2d..16c9689 100644 --- a/absl/strings/ascii.cc +++ b/absl/strings/ascii.cc
@@ -14,6 +14,10 @@ #include "absl/strings/ascii.h" +#include <climits> +#include <cstring> +#include <string> + namespace absl { ABSL_NAMESPACE_BEGIN namespace ascii_internal { @@ -153,18 +157,62 @@ }; // clang-format on -} // namespace ascii_internal +template <bool ToUpper> +constexpr void AsciiStrCaseFold(char* p, char* end) { + // The upper- and lowercase versions of ASCII characters differ by only 1 bit. + // When we need to flip the case, we can xor with this bit to achieve the + // desired result. Note that the choice of 'a' and 'A' here is arbitrary. We + // could have chosen 'z' and 'Z', or any other pair of characters as they all + // have the same single bit difference. + constexpr unsigned char kAsciiCaseBitFlip = 'a' ^ 'A'; -void AsciiStrToLower(std::string* s) { - for (auto& ch : *s) { - ch = absl::ascii_tolower(static_cast<unsigned char>(ch)); + constexpr char ch_a = ToUpper ? 'a' : 'A'; + constexpr char ch_z = ToUpper ? 'z' : 'Z'; + for (; p < end; ++p) { + unsigned char v = static_cast<unsigned char>(*p); + // We use & instead of && to ensure this always stays branchless + // We use static_cast<int> to suppress -Wbitwise-instead-of-logical + bool is_in_range = static_cast<bool>(static_cast<int>(ch_a <= v) & + static_cast<int>(v <= ch_z)); + v ^= is_in_range ? kAsciiCaseBitFlip : 0; + *p = static_cast<char>(v); } } -void AsciiStrToUpper(std::string* s) { - for (auto& ch : *s) { - ch = absl::ascii_toupper(static_cast<unsigned char>(ch)); +static constexpr size_t ValidateAsciiCasefold() { + constexpr size_t num_chars = 1 + CHAR_MAX - CHAR_MIN; + size_t incorrect_index = 0; + char lowered[num_chars] = {}; + char uppered[num_chars] = {}; + for (unsigned int i = 0; i < num_chars; ++i) { + uppered[i] = lowered[i] = static_cast<char>(i); } + AsciiStrCaseFold<false>(&lowered[0], &lowered[num_chars]); + AsciiStrCaseFold<true>(&uppered[0], &uppered[num_chars]); + for (size_t i = 0; i < num_chars; ++i) { + const char ch = static_cast<char>(i), + ch_upper = ('a' <= ch && ch <= 'z' ? 'A' + (ch - 'a') : ch), + ch_lower = ('A' <= ch && ch <= 'Z' ? 'a' + (ch - 'A') : ch); + if (uppered[i] != ch_upper || lowered[i] != ch_lower) { + incorrect_index = i > 0 ? i : num_chars; + break; + } + } + return incorrect_index; +} + +static_assert(ValidateAsciiCasefold() == 0, "error in case conversion"); + +} // namespace ascii_internal + +void AsciiStrToLower(std::string* s) { + char* p = &(*s)[0]; // Guaranteed to be valid for empty strings + return ascii_internal::AsciiStrCaseFold<false>(p, p + s->size()); +} + +void AsciiStrToUpper(std::string* s) { + char* p = &(*s)[0]; // Guaranteed to be valid for empty strings + return ascii_internal::AsciiStrCaseFold<true>(p, p + s->size()); } void RemoveExtraAsciiWhitespace(std::string* str) {
diff --git a/absl/strings/ascii_test.cc b/absl/strings/ascii_test.cc index dfed114..4ea262f 100644 --- a/absl/strings/ascii_test.cc +++ b/absl/strings/ascii_test.cc
@@ -14,6 +14,7 @@ #include "absl/strings/ascii.h" +#include <algorithm> #include <cctype> #include <clocale> #include <cstring> @@ -189,14 +190,14 @@ const std::string str("GHIJKL"); const std::string str2("MNOPQR"); const absl::string_view sp(str2); - std::string mutable_str("STUVWX"); + std::string mutable_str("_`?@[{AMNOPQRSTUVWXYZ"); EXPECT_EQ("abcdef", absl::AsciiStrToLower(buf)); EXPECT_EQ("ghijkl", absl::AsciiStrToLower(str)); EXPECT_EQ("mnopqr", absl::AsciiStrToLower(sp)); absl::AsciiStrToLower(&mutable_str); - EXPECT_EQ("stuvwx", mutable_str); + EXPECT_EQ("_`?@[{amnopqrstuvwxyz", mutable_str); char mutable_buf[] = "Mutable"; std::transform(mutable_buf, mutable_buf + strlen(mutable_buf), @@ -207,12 +208,12 @@ TEST(AsciiStrTo, Upper) { const char buf[] = "abcdef"; const std::string str("ghijkl"); - const std::string str2("mnopqr"); + const std::string str2("_`?@[{amnopqrstuvwxyz"); const absl::string_view sp(str2); EXPECT_EQ("ABCDEF", absl::AsciiStrToUpper(buf)); EXPECT_EQ("GHIJKL", absl::AsciiStrToUpper(str)); - EXPECT_EQ("MNOPQR", absl::AsciiStrToUpper(sp)); + EXPECT_EQ("_`?@[{AMNOPQRSTUVWXYZ", absl::AsciiStrToUpper(sp)); char mutable_buf[] = "Mutable"; std::transform(mutable_buf, mutable_buf + strlen(mutable_buf),
diff --git a/absl/strings/cord_buffer.h b/absl/strings/cord_buffer.h index a3600ac..bc0e4e4 100644 --- a/absl/strings/cord_buffer.h +++ b/absl/strings/cord_buffer.h
@@ -160,7 +160,6 @@ // for more information on buffer capacities and intended usage. static CordBuffer CreateWithDefaultLimit(size_t capacity); - // CordBuffer::CreateWithCustomLimit() // // Creates a CordBuffer instance of the desired `capacity` rounded to an @@ -336,7 +335,7 @@ } // Returns the available area of the internal SSO data - absl::Span<char> long_available() { + absl::Span<char> long_available() const { assert(!is_short()); const size_t length = long_rep.rep->length; return absl::Span<char>(long_rep.rep->Data() + length,
diff --git a/absl/strings/cord_test.cc b/absl/strings/cord_test.cc index 5603e94..3fe3967 100644 --- a/absl/strings/cord_test.cc +++ b/absl/strings/cord_test.cc
@@ -58,6 +58,8 @@ using absl::cord_internal::CordzUpdateTracker; using absl::cord_internal::kFlatOverhead; using absl::cord_internal::kMaxFlatLength; +using ::testing::ElementsAre; +using ::testing::Le; static std::string RandomLowercaseString(RandomEngine* rng); static std::string RandomLowercaseString(RandomEngine* rng, size_t length); @@ -618,7 +620,7 @@ TEST_P(CordTest, AppendSmallBuffer) { absl::Cord cord; absl::CordBuffer buffer = absl::CordBuffer::CreateWithDefaultLimit(3); - ASSERT_THAT(buffer.capacity(), ::testing::Le(15)); + ASSERT_THAT(buffer.capacity(), Le(15)); memcpy(buffer.data(), "Abc", 3); buffer.SetLength(3); cord.Append(std::move(buffer)); @@ -632,7 +634,7 @@ EXPECT_EQ(buffer.length(), 0); // NOLINT EXPECT_GT(buffer.capacity(), 0); // NOLINT - EXPECT_THAT(cord.Chunks(), ::testing::ElementsAre("Abcdefgh")); + EXPECT_THAT(cord.Chunks(), ElementsAre("Abcdefgh")); } TEST_P(CordTest, AppendAndPrependBufferArePrecise) { @@ -671,7 +673,7 @@ TEST_P(CordTest, PrependSmallBuffer) { absl::Cord cord; absl::CordBuffer buffer = absl::CordBuffer::CreateWithDefaultLimit(3); - ASSERT_THAT(buffer.capacity(), ::testing::Le(15)); + ASSERT_THAT(buffer.capacity(), Le(15)); memcpy(buffer.data(), "Abc", 3); buffer.SetLength(3); cord.Prepend(std::move(buffer)); @@ -685,7 +687,7 @@ EXPECT_EQ(buffer.length(), 0); // NOLINT EXPECT_GT(buffer.capacity(), 0); // NOLINT - EXPECT_THAT(cord.Chunks(), ::testing::ElementsAre("defghAbc")); + EXPECT_THAT(cord.Chunks(), ElementsAre("defghAbc")); } TEST_P(CordTest, AppendLargeBuffer) { @@ -707,7 +709,7 @@ EXPECT_EQ(buffer.length(), 0); // NOLINT EXPECT_GT(buffer.capacity(), 0); // NOLINT - EXPECT_THAT(cord.Chunks(), ::testing::ElementsAre(s1, s2)); + EXPECT_THAT(cord.Chunks(), ElementsAre(s1, s2)); } TEST_P(CordTest, PrependLargeBuffer) { @@ -729,7 +731,7 @@ EXPECT_EQ(buffer.length(), 0); // NOLINT EXPECT_GT(buffer.capacity(), 0); // NOLINT - EXPECT_THAT(cord.Chunks(), ::testing::ElementsAre(s2, s1)); + EXPECT_THAT(cord.Chunks(), ElementsAre(s2, s1)); } class CordAppendBufferTest : public testing::TestWithParam<bool> {
diff --git a/absl/strings/internal/str_format/float_conversion.cc b/absl/strings/internal/str_format/float_conversion.cc index fe0ad4b..f1490e5 100644 --- a/absl/strings/internal/str_format/float_conversion.cc +++ b/absl/strings/internal/str_format/float_conversion.cc
@@ -1015,7 +1015,7 @@ --end; } - char &back() { + char &back() const { assert(begin < end); return end[-1]; }
diff --git a/absl/strings/str_split.cc b/absl/strings/str_split.cc index e08c26b..72ba7c0 100644 --- a/absl/strings/str_split.cc +++ b/absl/strings/str_split.cc
@@ -60,19 +60,23 @@ // Finds using absl::string_view::find(), therefore the length of the found // delimiter is delimiter.length(). struct LiteralPolicy { - size_t Find(absl::string_view text, absl::string_view delimiter, size_t pos) { + static size_t Find(absl::string_view text, absl::string_view delimiter, + size_t pos) { return text.find(delimiter, pos); } - size_t Length(absl::string_view delimiter) { return delimiter.length(); } + static size_t Length(absl::string_view delimiter) { + return delimiter.length(); + } }; // Finds using absl::string_view::find_first_of(), therefore the length of the // found delimiter is 1. struct AnyOfPolicy { - size_t Find(absl::string_view text, absl::string_view delimiter, size_t pos) { + static size_t Find(absl::string_view text, absl::string_view delimiter, + size_t pos) { return text.find_first_of(delimiter, pos); } - size_t Length(absl::string_view /* delimiter */) { return 1; } + static size_t Length(absl::string_view /* delimiter */) { return 1; } }; } // namespace @@ -123,8 +127,7 @@ ABSL_RAW_CHECK(length > 0, ""); } -absl::string_view ByLength::Find(absl::string_view text, - size_t pos) const { +absl::string_view ByLength::Find(absl::string_view text, size_t pos) const { pos = std::min(pos, text.size()); // truncate `pos` absl::string_view substr = text.substr(pos); // If the string is shorter than the chunk size we say we
diff --git a/absl/synchronization/internal/futex.h b/absl/synchronization/internal/futex.h index 9cf9841..8d97326 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,70 @@ 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); + // Monotonic waits are disabled for production builds because go/btm + // requires synchronized clocks. + // TODO(b/160682823): Find a way to enable this when BTM is not enabled + // since production builds don't always run on Borg. +#if defined(CLOCK_MONOTONIC) && !defined(__GOOGLE_GRTE_VERSION__) + constexpr bool kRelativeTimeoutSupported = true; +#else + constexpr bool kRelativeTimeoutSupported = false; +#endif + + if (!t.has_timeout()) { + return Wait(v, val); + } else if (kRelativeTimeoutSupported && t.is_relative_timeout()) { + auto rel_timespec = t.MakeRelativeTimespec(); + return WaitRelativeTimeout(v, val, &rel_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 abs_timespec = t.MakeAbsTimespec(); + return WaitAbsoluteTimeout(v, val, &abs_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; } - static int Wake(std::atomic<int32_t> *v, int32_t count) { - // NOLINTNEXTLINE(runtime/int) - long err = syscall(SYS_futex, reinterpret_cast<int32_t*>(v), + // 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; + } + + // 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/kernel_timeout.cc b/absl/synchronization/internal/kernel_timeout.cc index 548a8fc..2e48509 100644 --- a/absl/synchronization/internal/kernel_timeout.cc +++ b/absl/synchronization/internal/kernel_timeout.cc
@@ -32,6 +32,18 @@ constexpr int64_t KernelTimeout::kMaxNanos; #endif +int64_t KernelTimeout::SteadyClockNow() { +#ifdef __GOOGLE_GRTE_VERSION__ + // go/btm requires synchronized clocks, so we have to use the system + // clock. + return absl::GetCurrentTimeNanos(); +#else + return std::chrono::duration_cast<std::chrono::nanoseconds>( + std::chrono::steady_clock::now().time_since_epoch()) + .count(); +#endif +} + KernelTimeout::KernelTimeout(absl::Time t) { // `absl::InfiniteFuture()` is a common "no timeout" value and cheaper to // compare than convert. @@ -73,12 +85,14 @@ nanos = 0; } - // Values greater than or equal to kMaxNanos are converted to infinite. - if (nanos >= kMaxNanos) { + int64_t now = SteadyClockNow(); + if (nanos > kMaxNanos - now) { + // Durations that would be greater than kMaxNanos are converted to infinite. rep_ = kNoTimeout; return; } + nanos += now; rep_ = (static_cast<uint64_t>(nanos) << 1) | uint64_t{1}; } @@ -90,6 +104,9 @@ int64_t nanos = RawNanos(); if (is_relative_timeout()) { + // We need to change epochs, because the relative timeout might be + // represented by an absolute timestamp from another clock. + nanos = std::max<int64_t>(nanos - SteadyClockNow(), 0); int64_t now = absl::GetCurrentTimeNanos(); if (nanos > kMaxNanos - now) { // Overflow. @@ -106,27 +123,24 @@ return nanos; } +int64_t KernelTimeout::InNanosecondsFromNow() const { + if (!has_timeout()) { + return kMaxNanos; + } + + int64_t nanos = RawNanos(); + if (is_absolute_timeout()) { + return std::max<int64_t>(nanos - absl::GetCurrentTimeNanos(), 0); + } + return std::max<int64_t>(nanos - SteadyClockNow(), 0); +} + struct timespec KernelTimeout::MakeAbsTimespec() const { return absl::ToTimespec(absl::Nanoseconds(MakeAbsNanos())); } struct timespec KernelTimeout::MakeRelativeTimespec() const { - if (!has_timeout()) { - return absl::ToTimespec(absl::Nanoseconds(kMaxNanos)); - } - if (is_relative_timeout()) { - return absl::ToTimespec(absl::Nanoseconds(RawNanos())); - } - - int64_t nanos = RawNanos(); - int64_t now = absl::GetCurrentTimeNanos(); - if (now > nanos) { - // Convert past values to 0 to be safe. - nanos = 0; - } else { - nanos -= now; - } - return absl::ToTimespec(absl::Nanoseconds(nanos)); + return absl::ToTimespec(absl::Nanoseconds(InNanosecondsFromNow())); } KernelTimeout::DWord KernelTimeout::InMillisecondsFromNow() const { @@ -136,32 +150,21 @@ return kInfinite; } - const int64_t nanos = RawNanos(); - constexpr uint64_t kNanosInMillis = uint64_t{1000000}; + constexpr uint64_t kNanosInMillis = uint64_t{1'000'000}; + constexpr uint64_t kMaxValueNanos = + std::numeric_limits<int64_t>::max() - kNanosInMillis + 1; - if (is_relative_timeout()) { - uint64_t ms = static_cast<uint64_t>(nanos) / kNanosInMillis; - if (ms > static_cast<uint64_t>(kInfinite)) { - ms = static_cast<uint64_t>(kInfinite); - } - return static_cast<DWord>(ms); + uint64_t ns_from_now = static_cast<uint64_t>(InNanosecondsFromNow()); + if (ns_from_now >= kMaxValueNanos) { + // Rounding up would overflow. + return kInfinite; } - - int64_t now = absl::GetCurrentTimeNanos(); - if (nanos >= now) { - // Round up so that now + ms_from_now >= nanos. - constexpr uint64_t kMaxValueNanos = - std::numeric_limits<int64_t>::max() - kNanosInMillis + 1; - uint64_t ms_from_now = - (std::min(kMaxValueNanos, static_cast<uint64_t>(nanos - now)) + - kNanosInMillis - 1) / - kNanosInMillis; - if (ms_from_now > kInfinite) { - return kInfinite; - } - return static_cast<DWord>(ms_from_now); + // Convert to milliseconds, always rounding up. + uint64_t ms_from_now = (ns_from_now + kNanosInMillis - 1) / kNanosInMillis; + if (ms_from_now > kInfinite) { + return kInfinite; } - return DWord{0}; + return static_cast<DWord>(ms_from_now); } std::chrono::time_point<std::chrono::system_clock> @@ -174,16 +177,7 @@ // std::ratio used by std::chrono::steady_clock doesn't convert to // std::nanoseconds, so it doesn't compile. auto micros = std::chrono::duration_cast<std::chrono::microseconds>( - std::chrono::nanoseconds(RawNanos())); - if (is_relative_timeout()) { - auto now = std::chrono::system_clock::now(); - if (micros > - std::chrono::time_point<std::chrono::system_clock>::max() - now) { - // Overflow. - return std::chrono::time_point<std::chrono::system_clock>::max(); - } - return now + micros; - } + std::chrono::nanoseconds(MakeAbsNanos())); return std::chrono::system_clock::from_time_t(0) + micros; } @@ -191,17 +185,7 @@ if (!has_timeout()) { return std::chrono::nanoseconds::max(); } - if (is_absolute_timeout()) { - auto d = std::chrono::duration_cast<std::chrono::nanoseconds>( - std::chrono::nanoseconds(RawNanos()) - - (std::chrono::system_clock::now() - - std::chrono::system_clock::from_time_t(0))); - if (d < std::chrono::nanoseconds(0)) { - d = std::chrono::nanoseconds(0); - } - return d; - } - return std::chrono::nanoseconds(RawNanos()); + return std::chrono::nanoseconds(InNanosecondsFromNow()); } } // namespace synchronization_internal
diff --git a/absl/synchronization/internal/kernel_timeout.h b/absl/synchronization/internal/kernel_timeout.h index f7c4033..e2cf3c2 100644 --- a/absl/synchronization/internal/kernel_timeout.h +++ b/absl/synchronization/internal/kernel_timeout.h
@@ -75,7 +75,9 @@ // Convert to `struct timespec` for interfaces that expect a relative // timeout. If !has_timeout() or is_absolute_timeout(), attempts to convert to // a reasonable relative timeout, but callers should to test has_timeout() and - // is_absolute_timeout() and prefer to use a more appropriate interface. + // is_absolute_timeout() and prefer to use a more appropriate interface. Since + // the return value is a relative duration, it should be recomputed by calling + // this method in the case of a spurious wakeup. struct timespec MakeRelativeTimespec() const; // Convert to unix epoch nanos for interfaces that expect an absolute timeout @@ -107,17 +109,24 @@ // timeout, like std::condition_variable::wait_for(). If !has_timeout() or // is_absolute_timeout(), attempts to convert to a reasonable relative // timeout, but callers should test has_timeout() and is_absolute_timeout() - // and prefer to use a more appropriate interface. + // and prefer to use a more appropriate interface. Since the return value is a + // relative duration, it should be recomputed by calling this method in the + // case of a spurious wakeup. std::chrono::nanoseconds ToChronoDuration() const; private: + // Returns the current time, expressed as a count of nanoseconds since the + // epoch used by an arbitrary clock. The implementation tries to use a steady + // (monotonic) clock if one is available. + static int64_t SteadyClockNow(); + // Internal representation. // - If the value is kNoTimeout, then the timeout is infinite, and // has_timeout() will return true. - // - If the low bit is 0, then the high 63 bits is number of nanoseconds + // - If the low bit is 0, then the high 63 bits is the number of nanoseconds // after the unix epoch. - // - If the low bit is 1, then the high 63 bits is a relative duration in - // nanoseconds. + // - If the low bit is 1, then the high 63 bits is the number of nanoseconds + // after the epoch used by SteadyClockNow(). uint64_t rep_; // Returns the number of nanoseconds stored in the internal representation. @@ -125,6 +134,11 @@ // value is used to compute when the timeout should occur. int64_t RawNanos() const { return static_cast<int64_t>(rep_ >> 1); } + // Converts to nanoseconds from now. Since the return value is a relative + // duration, it should be recomputed by calling this method in the case of a + // spurious wakeup. + int64_t InNanosecondsFromNow() const; + // A value that represents no timeout (or an infinite timeout). static constexpr uint64_t kNoTimeout = (std::numeric_limits<uint64_t>::max)();
diff --git a/absl/synchronization/internal/kernel_timeout_test.cc b/absl/synchronization/internal/kernel_timeout_test.cc index 431ffcf..a96f806 100644 --- a/absl/synchronization/internal/kernel_timeout_test.cc +++ b/absl/synchronization/internal/kernel_timeout_test.cc
@@ -62,9 +62,6 @@ EXPECT_TRUE(t.is_absolute_timeout()); EXPECT_FALSE(t.is_relative_timeout()); EXPECT_EQ(absl::TimeFromTimespec(t.MakeAbsTimespec()), when); - // MakeRelativeTimespec() doesn't quite round trip when using an absolute - // time, but it should get pretty close. Past times are converted to zero - // durations. EXPECT_LE( absl::AbsDuration(absl::DurationFromTimespec(t.MakeRelativeTimespec()) - std::max(duration, absl::ZeroDuration())), @@ -201,7 +198,10 @@ EXPECT_LE(absl::AbsDuration(absl::Now() + duration - absl::TimeFromTimespec(t.MakeAbsTimespec())), absl::Milliseconds(5)); - EXPECT_EQ(absl::DurationFromTimespec(t.MakeRelativeTimespec()), duration); + EXPECT_LE( + absl::AbsDuration(absl::DurationFromTimespec(t.MakeRelativeTimespec()) - + duration), + kTimingBound); EXPECT_LE(absl::AbsDuration(absl::Now() + duration - absl::FromUnixNanos(t.MakeAbsNanos())), absl::Milliseconds(5)); @@ -210,7 +210,9 @@ EXPECT_LE(absl::AbsDuration(absl::Now() + duration - absl::FromChrono(t.ToChronoTimePoint())), kTimingBound); - EXPECT_EQ(absl::FromChrono(t.ToChronoDuration()), duration); + EXPECT_LE( + absl::AbsDuration(absl::FromChrono(t.ToChronoDuration()) - duration), + kTimingBound); } } @@ -298,7 +300,6 @@ int64_t limit = std::numeric_limits<int64_t>::max() - now_nanos; absl::Duration duration = absl::Nanoseconds(limit) + absl::Seconds(1); KernelTimeout t(duration); - EXPECT_TRUE(t.has_timeout()); // Timeouts should still be far in the future. EXPECT_GT(absl::TimeFromTimespec(t.MakeAbsTimespec()), absl::Now() + absl::Hours(100000));
diff --git a/absl/synchronization/internal/waiter.cc b/absl/synchronization/internal/waiter.cc index f2051d6..1b8d8d0 100644 --- a/absl/synchronization/internal/waiter.cc +++ b/absl/synchronization/internal/waiter.cc
@@ -48,7 +48,6 @@ #include "absl/base/optimization.h" #include "absl/synchronization/internal/kernel_timeout.h" - namespace absl { ABSL_NAMESPACE_BEGIN namespace synchronization_internal { @@ -67,9 +66,7 @@ #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) { // Loop until we can atomically decrement futex from a positive
diff --git a/absl/synchronization/mutex.cc b/absl/synchronization/mutex.cc index ef6d063..a891161 100644 --- a/absl/synchronization/mutex.cc +++ b/absl/synchronization/mutex.cc
@@ -635,21 +635,6 @@ 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 @@ } 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 @@ 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 @@ } 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::WaitWithTimeout(Mutex *mu, absl::Duration timeout) { - return WaitWithDeadline(mu, DeadlineFromTimeout(timeout)); + return WaitCommon(mu, KernelTimeout(timeout)); } bool CondVar::WaitWithDeadline(Mutex *mu, absl::Time deadline) {
diff --git a/absl/time/BUILD.bazel b/absl/time/BUILD.bazel index c7b07c2..88d2088 100644 --- a/absl/time/BUILD.bazel +++ b/absl/time/BUILD.bazel
@@ -91,6 +91,7 @@ "//absl/base:config", "//absl/base:core_headers", "//absl/numeric:int128", + "//absl/strings:str_format", "//absl/time/internal/cctz:time_zone", "@com_google_googletest//:gtest_main", ],
diff --git a/absl/time/CMakeLists.txt b/absl/time/CMakeLists.txt index 7b72054..b312425 100644 --- a/absl/time/CMakeLists.txt +++ b/absl/time/CMakeLists.txt
@@ -122,6 +122,8 @@ absl::time absl::config absl::core_headers + absl::strings + absl::str_format absl::time_zone GTest::gmock_main )
diff --git a/absl/time/duration_test.cc b/absl/time/duration_test.cc index b7abf4b..76d3217 100644 --- a/absl/time/duration_test.cc +++ b/absl/time/duration_test.cc
@@ -16,8 +16,8 @@ #include <winsock2.h> // for timeval #endif -#include <chrono> // NOLINT(build/c++11) #include <cfloat> +#include <chrono> // NOLINT(build/c++11) #include <cmath> #include <cstdint> #include <ctime> @@ -28,6 +28,7 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "absl/strings/str_format.h" #include "absl/time/time.h" namespace { @@ -1853,4 +1854,11 @@ #undef TEST_PARSE_ROUNDTRIP } +TEST(Duration, AbslStringify) { + // FormatDuration is already well tested, so just use one test case here to + // verify that StrFormat("%v", d) works as expected. + absl::Duration d = absl::Seconds(1); + EXPECT_EQ(absl::StrFormat("%v", d), absl::FormatDuration(d)); +} + } // namespace
diff --git a/absl/time/time.h b/absl/time/time.h index cc39008..01b5532 100644 --- a/absl/time/time.h +++ b/absl/time/time.h
@@ -609,6 +609,12 @@ return os << FormatDuration(d); } +// Support for StrFormat(), StrCat() etc. +template <typename Sink> +void AbslStringify(Sink& sink, Duration d) { + sink.Append(FormatDuration(d)); +} + // ParseDuration() // // Parses a duration string consisting of a possibly signed sequence of @@ -1386,6 +1392,12 @@ return os << FormatTime(t); } +// Support for StrFormat(), StrCat() etc. +template <typename Sink> +void AbslStringify(Sink& sink, Time t) { + sink.Append(FormatTime(t)); +} + // ParseTime() // // Parses an input string according to the provided format string and
diff --git a/absl/time/time_test.cc b/absl/time/time_test.cc index d235e9a..6a89399 100644 --- a/absl/time/time_test.cc +++ b/absl/time/time_test.cc
@@ -28,6 +28,7 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/numeric/int128.h" +#include "absl/strings/str_format.h" #include "absl/time/clock.h" #include "absl/time/internal/test_util.h" @@ -1287,4 +1288,11 @@ // We have a transition but we don't know which one. } +TEST(Time, AbslStringify) { + // FormatTime is already well tested, so just use one test case here to + // verify that StrFormat("%v", t) works as expected. + absl::Time t = absl::Now(); + EXPECT_EQ(absl::StrFormat("%v", t), absl::FormatTime(t)); +} + } // namespace