From e9324d926a9189e222741fce6e676f0944661a72 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Fri, 21 Jun 2019 13:11:42 -0700 Subject: Export of internal Abseil changes. -- 7a6ff16a85beb730c172d5d25cf1b5e1be885c56 by Laramie Leavitt : Internal change. PiperOrigin-RevId: 254454546 -- ff8f9bafaefc26d451f576ea4a06d150aed63f6f by Andy Soffer : Internal changes PiperOrigin-RevId: 254451562 -- deefc5b651b479ce36f0b4ef203e119c0c8936f2 by CJ Johnson : Account for subtracting unsigned values from the size of InlinedVector PiperOrigin-RevId: 254450625 -- 3c677316a27bcadc17e41957c809ca472d5fef14 by Andy Soffer : Add C++17's std::make_from_tuple to absl/utility/utility.h PiperOrigin-RevId: 254411573 -- 4ee3536a918830eeec402a28fc31a62c7c90b940 by CJ Johnson : Adds benchmark for the rest of the InlinedVector public API PiperOrigin-RevId: 254408378 -- e5a21a00700ee83498ff1efbf649169756463ee4 by CJ Johnson : Updates the definition of InlinedVector::shrink_to_fit() to be exception safe and adds exception safety tests for it. PiperOrigin-RevId: 254401387 -- 2ea82e72b86d82d78b4e4712a63a55981b53c64b by Laramie Leavitt : Use absl::InsecureBitGen in place of std::mt19937 in tests absl/random/...distribution_test.cc PiperOrigin-RevId: 254289444 -- fa099e02c413a7ffda732415e8105cad26a90337 by Andy Soffer : Internal changes PiperOrigin-RevId: 254286334 -- ce34b7f36933b30cfa35b9c9a5697a792b5666e4 by Andy Soffer : Internal changes PiperOrigin-RevId: 254273059 -- 6f9c473da7c2090c2e85a37c5f00622e8a912a89 by Jorg Brown : Change absl::container_internal::CompressedTuple to instantiate its internal Storage class with the name of the type it's holding, rather than the name of the Tuple. This is not an externally-visible change, other than less compiler memory is used and less debug information is generated. PiperOrigin-RevId: 254269285 -- 8bd3c186bf2fc0c55d8a2dd6f28a5327502c9fba by Andy Soffer : Adding short-hand IntervalClosed for IntervalClosedClosed and IntervalOpen for IntervalOpenOpen. PiperOrigin-RevId: 254252419 -- ea957f99b6a04fccd42aa05605605f3b44b1ecfd by Abseil Team : Do not directly use __SIZEOF_INT128__. In order to avoid linker errors when building with clang-cl (__fixunsdfti, __udivti3 and __fixunssfti are undefined), this CL uses ABSL_HAVE_INTRINSIC_INT128 which is not defined for clang-cl. PiperOrigin-RevId: 254250739 -- 89ab385cd26b34d64130bce856253aaba96d2345 by Andy Soffer : Internal changes PiperOrigin-RevId: 254242321 -- cffc793d93eca6d6bdf7de733847b6ab4a255ae9 by CJ Johnson : Adds benchmark for InlinedVector::reserve(size_type) PiperOrigin-RevId: 254199226 -- c90c7a9fa3c8f0c9d5114036979548b055ea2f2a by Gennadiy Rozental : Import of CCTZ from GitHub. PiperOrigin-RevId: 254072387 -- c4c388beae016c9570ab54ffa1d52660e4a85b7b by Laramie Leavitt : Internal cleanup. PiperOrigin-RevId: 254062381 -- d3c992e221cc74e5372d0c8fa410170b6a43c062 by Tom Manshreck : Update distributions.h to Abseil standards PiperOrigin-RevId: 254054946 -- d15ad0035c34ef11b14fadc5a4a2d3ec415f5518 by CJ Johnson : Removes functions with only one caller from the implementation details of InlinedVector by manually inlining the definitions PiperOrigin-RevId: 254005427 -- 2f37e807efc3a8ef1f4b539bdd379917d4151520 by Andy Soffer : Initial release of Abseil Random PiperOrigin-RevId: 253999861 -- 24ed1694b6430791d781ed533a8f8ccf6cac5856 by CJ Johnson : Updates the definition of InlinedVector::assign(...)/InlinedVector::operator=(...) to new, exception-safe implementations with exception safety tests to boot PiperOrigin-RevId: 253993691 -- 5613d95f5a7e34a535cfaeadce801441e990843e by CJ Johnson : Adds benchmarks for InlinedVector::shrink_to_fit() PiperOrigin-RevId: 253989647 -- 2a96ddfdac40bbb8cb6a7f1aeab90917067c6e63 by Abseil Team : Initial release of Abseil Random PiperOrigin-RevId: 253927497 -- bf1aff8fc9ffa921ad74643e9525ecf25b0d8dc1 by Andy Soffer : Initial release of Abseil Random PiperOrigin-RevId: 253920512 -- bfc03f4a3dcda3cf3a4b84bdb84cda24e3394f41 by Laramie Leavitt : Internal change. PiperOrigin-RevId: 253886486 -- 05036cfcc078ca7c5f581a00dfb0daed568cbb69 by Eric Fiselier : Don't include `winsock2.h` because it drags in `windows.h` and friends, and they define awful macros like OPAQUE, ERROR, and more. This has the potential to break abseil users. Instead we only forward declare `timeval` and require Windows users include `winsock2.h` themselves. This is both inconsistent and poor QoI, but so including 'windows.h' is bad too. PiperOrigin-RevId: 253852615 GitOrigin-RevId: 7a6ff16a85beb730c172d5d25cf1b5e1be885c56 Change-Id: Icd6aff87da26f29ec8915da856f051129987cef6 --- absl/random/internal/iostream_state_saver_test.cc | 369 ++++++++++++++++++++++ 1 file changed, 369 insertions(+) create mode 100644 absl/random/internal/iostream_state_saver_test.cc (limited to 'absl/random/internal/iostream_state_saver_test.cc') diff --git a/absl/random/internal/iostream_state_saver_test.cc b/absl/random/internal/iostream_state_saver_test.cc new file mode 100644 index 00000000..2ecbaac1 --- /dev/null +++ b/absl/random/internal/iostream_state_saver_test.cc @@ -0,0 +1,369 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/random/internal/iostream_state_saver.h" + +#include +#include + +#include "gtest/gtest.h" + +namespace { + +using absl::random_internal::make_istream_state_saver; +using absl::random_internal::make_ostream_state_saver; +using absl::random_internal::stream_precision_helper; + +template +typename absl::enable_if_t::value, T> // +StreamRoundTrip(T t) { + std::stringstream ss; + { + auto saver = make_ostream_state_saver(ss); + ss.precision(stream_precision_helper::kPrecision); + ss << t; + } + T result = 0; + { + auto saver = make_istream_state_saver(ss); + ss >> result; + } + EXPECT_FALSE(ss.fail()) // + << ss.str() << " " // + << (ss.good() ? "good " : "") // + << (ss.bad() ? "bad " : "") // + << (ss.eof() ? "eof " : "") // + << (ss.fail() ? "fail " : ""); + + return result; +} + +template +typename absl::enable_if_t::value, T> // +StreamRoundTrip(T t) { + std::stringstream ss; + { + auto saver = make_ostream_state_saver(ss); + ss.precision(stream_precision_helper::kPrecision); + ss << t; + } + T result = 0; + { + auto saver = make_istream_state_saver(ss); + result = absl::random_internal::read_floating_point(ss); + } + EXPECT_FALSE(ss.fail()) // + << ss.str() << " " // + << (ss.good() ? "good " : "") // + << (ss.bad() ? "bad " : "") // + << (ss.eof() ? "eof " : "") // + << (ss.fail() ? "fail " : ""); + + return result; +} + +TEST(IOStreamStateSaver, BasicSaverState) { + std::stringstream ss; + ss.precision(2); + ss.fill('x'); + ss.flags(std::ios_base::dec | std::ios_base::right); + + { + auto saver = make_ostream_state_saver(ss); + ss.precision(10); + EXPECT_NE('x', ss.fill()); + EXPECT_EQ(10, ss.precision()); + EXPECT_NE(std::ios_base::dec | std::ios_base::right, ss.flags()); + + ss << 1.23; + } + + EXPECT_EQ('x', ss.fill()); + EXPECT_EQ(2, ss.precision()); + EXPECT_EQ(std::ios_base::dec | std::ios_base::right, ss.flags()); +} + +TEST(IOStreamStateSaver, RoundTripInts) { + const uint64_t kUintValues[] = { + 0, + 1, + static_cast(-1), + 2, + static_cast(-2), + + 1 << 7, + 1 << 8, + 1 << 16, + 1ull << 32, + 1ull << 50, + 1ull << 62, + 1ull << 63, + + (1 << 7) - 1, + (1 << 8) - 1, + (1 << 16) - 1, + (1ull << 32) - 1, + (1ull << 50) - 1, + (1ull << 62) - 1, + (1ull << 63) - 1, + + static_cast(-(1 << 8)), + static_cast(-(1 << 16)), + static_cast(-(1ll << 32)), + static_cast(-(1ll << 50)), + static_cast(-(1ll << 62)), + + static_cast(-(1 << 8) - 1), + static_cast(-(1 << 16) - 1), + static_cast(-(1ll << 32) - 1), + static_cast(-(1ll << 50) - 1), + static_cast(-(1ll << 62) - 1), + }; + + for (const uint64_t u : kUintValues) { + EXPECT_EQ(u, StreamRoundTrip(u)); + + int64_t x = static_cast(u); + EXPECT_EQ(x, StreamRoundTrip(x)); + + double d = static_cast(x); + EXPECT_EQ(d, StreamRoundTrip(d)); + + float f = d; + EXPECT_EQ(f, StreamRoundTrip(f)); + } +} + +TEST(IOStreamStateSaver, RoundTripFloats) { + static_assert( + stream_precision_helper::kPrecision >= 9, + "stream_precision_helper::kPrecision should be at least 9"); + + const float kValues[] = { + 1, + std::nextafter(1.0f, 0.0f), // 1 - epsilon + std::nextafter(1.0f, 2.0f), // 1 + epsilon + + 1.0e+1f, + 1.0e-1f, + 1.0e+2f, + 1.0e-2f, + 1.0e+10f, + 1.0e-10f, + + 0.00000051110000111311111111f, + -0.00000051110000111211111111f, + + 1.234678912345678912345e+6f, + 1.234678912345678912345e-6f, + 1.234678912345678912345e+30f, + 1.234678912345678912345e-30f, + 1.234678912345678912345e+38f, + 1.0234678912345678912345e-38f, + + // Boundary cases. + std::numeric_limits::max(), + std::numeric_limits::lowest(), + std::numeric_limits::epsilon(), + std::nextafter(std::numeric_limits::min(), + 1.0f), // min + epsilon + std::numeric_limits::min(), // smallest normal + // There are some errors dealing with denorms on apple platforms. + std::numeric_limits::denorm_min(), // smallest denorm + std::numeric_limits::min() / 2, + std::nextafter(std::numeric_limits::min(), + 0.0f), // denorm_max + std::nextafter(std::numeric_limits::denorm_min(), 1.0f), + }; + + for (const float f : kValues) { + EXPECT_EQ(f, StreamRoundTrip(f)); + EXPECT_EQ(-f, StreamRoundTrip(-f)); + + double d = f; + EXPECT_EQ(d, StreamRoundTrip(d)); + EXPECT_EQ(-d, StreamRoundTrip(-d)); + + // Avoid undefined behavior (overflow/underflow). + if (d <= std::numeric_limits::max() && + d >= std::numeric_limits::lowest()) { + int64_t x = static_cast(f); + EXPECT_EQ(x, StreamRoundTrip(x)); + } + } +} + +TEST(IOStreamStateSaver, RoundTripDoubles) { + static_assert( + stream_precision_helper::kPrecision >= 17, + "stream_precision_helper::kPrecision should be at least 17"); + + const double kValues[] = { + 1, + std::nextafter(1.0, 0.0), // 1 - epsilon + std::nextafter(1.0, 2.0), // 1 + epsilon + + 1.0e+1, + 1.0e-1, + 1.0e+2, + 1.0e-2, + 1.0e+10, + 1.0e-10, + + 0.00000051110000111311111111, + -0.00000051110000111211111111, + + 1.234678912345678912345e+6, + 1.234678912345678912345e-6, + 1.234678912345678912345e+30, + 1.234678912345678912345e-30, + 1.234678912345678912345e+38, + 1.0234678912345678912345e-38, + + 1.0e+100, + 1.0e-100, + 1.234678912345678912345e+308, + 1.0234678912345678912345e-308, + 2.22507385850720138e-308, + + // Boundary cases. + std::numeric_limits::max(), + std::numeric_limits::lowest(), + std::numeric_limits::epsilon(), + std::nextafter(std::numeric_limits::min(), + 1.0), // min + epsilon + std::numeric_limits::min(), // smallest normal + // There are some errors dealing with denorms on apple platforms. + std::numeric_limits::denorm_min(), // smallest denorm + std::numeric_limits::min() / 2, + std::nextafter(std::numeric_limits::min(), + 0.0), // denorm_max + std::nextafter(std::numeric_limits::denorm_min(), 1.0f), + }; + + for (const double d : kValues) { + EXPECT_EQ(d, StreamRoundTrip(d)); + EXPECT_EQ(-d, StreamRoundTrip(-d)); + + // Avoid undefined behavior (overflow/underflow). + if (d <= std::numeric_limits::max() && + d >= std::numeric_limits::lowest()) { + float f = static_cast(d); + EXPECT_EQ(f, StreamRoundTrip(f)); + } + + // Avoid undefined behavior (overflow/underflow). + if (d <= std::numeric_limits::max() && + d >= std::numeric_limits::lowest()) { + int64_t x = static_cast(d); + EXPECT_EQ(x, StreamRoundTrip(x)); + } + } +} + +TEST(IOStreamStateSaver, RoundTripLongDoubles) { + // Technically, C++ only guarantees that long double is at least as large as a + // double. Practically it varies from 64-bits to 128-bits. + // + // So it is best to consider long double a best-effort extended precision + // type. + + static_assert( + stream_precision_helper::kPrecision >= 36, + "stream_precision_helper::kPrecision should be at least 36"); + + using real_type = long double; + const real_type kValues[] = { + 1, + std::nextafter(1.0, 0.0), // 1 - epsilon + std::nextafter(1.0, 2.0), // 1 + epsilon + + 1.0e+1, + 1.0e-1, + 1.0e+2, + 1.0e-2, + 1.0e+10, + 1.0e-10, + + 0.00000051110000111311111111, + -0.00000051110000111211111111, + + 1.2346789123456789123456789123456789e+6, + 1.2346789123456789123456789123456789e-6, + 1.2346789123456789123456789123456789e+30, + 1.2346789123456789123456789123456789e-30, + 1.2346789123456789123456789123456789e+38, + 1.2346789123456789123456789123456789e-38, + 1.2346789123456789123456789123456789e+308, + 1.2346789123456789123456789123456789e-308, + + 1.0e+100, + 1.0e-100, + 1.234678912345678912345e+308, + 1.0234678912345678912345e-308, + + // Boundary cases. + std::numeric_limits::max(), + std::numeric_limits::lowest(), + std::numeric_limits::epsilon(), + std::nextafter(std::numeric_limits::min(), + real_type(1)), // min + epsilon + std::numeric_limits::min(), // smallest normal + // There are some errors dealing with denorms on apple platforms. + std::numeric_limits::denorm_min(), // smallest denorm + std::numeric_limits::min() / 2, + std::nextafter(std::numeric_limits::min(), + 0.0), // denorm_max + std::nextafter(std::numeric_limits::denorm_min(), 1.0f), + }; + + int index = -1; + for (const long double dd : kValues) { + index++; + EXPECT_EQ(dd, StreamRoundTrip(dd)) << index; + EXPECT_EQ(-dd, StreamRoundTrip(-dd)) << index; + + // Avoid undefined behavior (overflow/underflow). + if (dd <= std::numeric_limits::max() && + dd >= std::numeric_limits::lowest()) { + double d = static_cast(dd); + EXPECT_EQ(d, StreamRoundTrip(d)); + } + + // Avoid undefined behavior (overflow/underflow). + if (dd <= std::numeric_limits::max() && + dd >= std::numeric_limits::lowest()) { + int64_t x = static_cast(dd); + EXPECT_EQ(x, StreamRoundTrip(x)); + } + } +} + +TEST(StrToDTest, DoubleMin) { + const char kV[] = "2.22507385850720138e-308"; + char* end; + double x = std::strtod(kV, &end); + EXPECT_EQ(std::numeric_limits::min(), x); + // errno may equal ERANGE. +} + +TEST(StrToDTest, DoubleDenormMin) { + const char kV[] = "4.94065645841246544e-324"; + char* end; + double x = std::strtod(kV, &end); + EXPECT_EQ(std::numeric_limits::denorm_min(), x); + // errno may equal ERANGE. +} + +} // namespace -- cgit v1.2.3 From 25597bdfc148e91e27678ec30efa52f4fc8c164f Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Wed, 2 Oct 2019 15:20:57 -0700 Subject: Export of internal Abseil changes -- 3e60f355db5afd7a864591d81a6c383b6c0a0780 by Samuel Benzaquen : Internal change PiperOrigin-RevId: 272531442 -- 6d189240b8cebe3a390c730de491156d03049229 by Andy Getzendanner : Fix AtomicHook init-order fiasco under MSVC 2019. On this platform, constexpr static init sometimes happens after dynamic init =/. When it does, we should not zero hook_ (overwriting the value written there by dynamic init); instead we should leave it alone. This works even when constexpr static init goes first since all uses of AtomicHook should have static storage duration and be zero-initialized. https://developercommunity.visualstudio.com/content/problem/336946/class-with-constexpr-constructor-not-using-static.html PiperOrigin-RevId: 272525226 -- d01b14fc06bc75b41c51976ed32e7c304ea1aab7 by Abseil Team : exclude emscripten from running tests involving long doubles PiperOrigin-RevId: 272497628 GitOrigin-RevId: 3e60f355db5afd7a864591d81a6c383b6c0a0780 Change-Id: I3c8a8f5acaf7652a06ef40cf028ef5d2e142f81b --- absl/base/BUILD.bazel | 14 ++++++++++ absl/base/CMakeLists.txt | 15 ++++++++++ absl/base/internal/atomic_hook.h | 26 ++++++++++++------ absl/base/internal/atomic_hook_test.cc | 24 ++++++++++++++++ absl/base/internal/atomic_hook_test_helper.cc | 29 ++++++++++++++++++++ absl/base/internal/atomic_hook_test_helper.h | 32 ++++++++++++++++++++++ absl/container/internal/hashtablez_sampler.cc | 20 +++++++++++++- absl/container/internal/hashtablez_sampler.h | 2 +- .../hashtablez_sampler_force_weak_definition.cc | 5 ++-- absl/random/exponential_distribution_test.cc | 4 +++ absl/random/internal/iostream_state_saver_test.cc | 2 ++ absl/random/uniform_real_distribution_test.cc | 5 ++++ 12 files changed, 166 insertions(+), 12 deletions(-) create mode 100644 absl/base/internal/atomic_hook_test_helper.cc create mode 100644 absl/base/internal/atomic_hook_test_helper.h (limited to 'absl/random/internal/iostream_state_saver_test.cc') diff --git a/absl/base/BUILD.bazel b/absl/base/BUILD.bazel index 57caa065..1e1f0d2d 100644 --- a/absl/base/BUILD.bazel +++ b/absl/base/BUILD.bazel @@ -207,6 +207,19 @@ cc_library( ], ) +cc_library( + name = "atomic_hook_test_helper", + testonly = 1, + srcs = ["internal/atomic_hook_test_helper.cc"], + hdrs = ["internal/atomic_hook_test_helper.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":atomic_hook", + ":core_headers", + ], +) + cc_test( name = "atomic_hook_test", size = "small", @@ -215,6 +228,7 @@ cc_test( linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":atomic_hook", + ":atomic_hook_test_helper", ":core_headers", "@com_google_googletest//:gtest_main", ], diff --git a/absl/base/CMakeLists.txt b/absl/base/CMakeLists.txt index 2f11ef86..51705a5a 100644 --- a/absl/base/CMakeLists.txt +++ b/absl/base/CMakeLists.txt @@ -254,6 +254,19 @@ absl_cc_test( gtest_main ) +absl_cc_library( + NAME + atomic_hook_test_helper + SRCS + "internal/atomic_hook_test_helper.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::atomic_hook + absl::core_headers + TESTONLY +) + absl_cc_test( NAME atomic_hook_test @@ -262,8 +275,10 @@ absl_cc_test( COPTS ${ABSL_TEST_COPTS} DEPS + absl::atomic_hook_test_helper absl::atomic_hook absl::core_headers + gmock gtest_main ) diff --git a/absl/base/internal/atomic_hook.h b/absl/base/internal/atomic_hook.h index 803e9059..09f763d0 100644 --- a/absl/base/internal/atomic_hook.h +++ b/absl/base/internal/atomic_hook.h @@ -11,7 +11,6 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -// #ifndef ABSL_BASE_INTERNAL_ATOMIC_HOOK_H_ #define ABSL_BASE_INTERNAL_ATOMIC_HOOK_H_ @@ -23,8 +22,10 @@ #ifdef _MSC_FULL_VER #define ABSL_HAVE_WORKING_ATOMIC_POINTER 0 +#define ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT 0 #else #define ABSL_HAVE_WORKING_ATOMIC_POINTER 1 +#define ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT 1 #endif namespace absl { @@ -33,16 +34,17 @@ namespace base_internal { template class AtomicHook; -// AtomicHook is a helper class, templatized on a raw function pointer type, for -// implementing Abseil customization hooks. It is a callable object that -// dispatches to the registered hook. +// `AtomicHook` is a helper class, templatized on a raw function pointer type, +// for implementing Abseil customization hooks. It is a callable object that +// dispatches to the registered hook. Objects of type `AtomicHook` must have +// static or thread storage duration. // // A default constructed object performs a no-op (and returns a default // constructed object) if no hook has been registered. // // Hooks can be pre-registered via constant initialization, for example, -// ABSL_CONST_INIT static AtomicHook my_hook(DefaultAction); -// and then changed at runtime via a call to Store(). +// `ABSL_CONST_INIT static AtomicHook my_hook(DefaultAction);` +// and then changed at runtime via a call to `Store()`. // // Reads and writes guarantee memory_order_acquire/memory_order_release // semantics. @@ -57,12 +59,19 @@ class AtomicHook { // Constructs an object that by default dispatches to/returns the // pre-registered default_fn when no hook has been registered at runtime. -#if ABSL_HAVE_WORKING_ATOMIC_POINTER +#if ABSL_HAVE_WORKING_ATOMIC_POINTER && ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT explicit constexpr AtomicHook(FnPtr default_fn) : hook_(default_fn), default_fn_(default_fn) {} #else + // On MSVC, this function sometimes executes after dynamic initiazliation =(. + // If a non-zero `hook_` has been installed by a dynamic initializer, we want + // to preserve it. If not, `hook_` will be zero initialized and we have no + // need to set it to `kUninitialized`. + // https://developercommunity.visualstudio.com/content/problem/336946/class-with-constexpr-constructor-not-using-static.html explicit constexpr AtomicHook(FnPtr default_fn) - : hook_(kUninitialized), default_fn_(default_fn) {} + : /* hook_(deliberately omitted), */ default_fn_(default_fn) { + static_assert(kUninitialized == 0, "here we rely on zero-initialization"); + } #endif // Stores the provided function pointer as the value for this hook. @@ -158,6 +167,7 @@ class AtomicHook { }; #undef ABSL_HAVE_WORKING_ATOMIC_POINTER +#undef ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT } // namespace base_internal } // namespace absl diff --git a/absl/base/internal/atomic_hook_test.cc b/absl/base/internal/atomic_hook_test.cc index ecc80406..794072ee 100644 --- a/absl/base/internal/atomic_hook_test.cc +++ b/absl/base/internal/atomic_hook_test.cc @@ -14,11 +14,15 @@ #include "absl/base/internal/atomic_hook.h" +#include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/base/attributes.h" +#include "absl/base/internal/atomic_hook_test_helper.h" namespace { +using ::testing::Eq; + int value = 0; void TestHook(int x) { value = x; } @@ -67,4 +71,24 @@ TEST(AtomicHookTest, WithDefaultFunction) { EXPECT_EQ(value, 2); } +ABSL_CONST_INIT int override_func_calls = 0; +void OverrideFunc() { override_func_calls++; } +static struct OverrideInstaller { + OverrideInstaller() { absl::atomic_hook_internal::func.Store(OverrideFunc); } +} override_installer; + +TEST(AtomicHookTest, DynamicInitFromAnotherTU) { + // MSVC 14.2 doesn't do constexpr static init correctly; in particular it + // tends to sequence static init (i.e. defaults) of `AtomicHook` objects + // after their dynamic init (i.e. overrides), overwriting whatever value was + // written during dynamic init. This regression test validates the fix. + // https://developercommunity.visualstudio.com/content/problem/336946/class-with-constexpr-constructor-not-using-static.html + EXPECT_THAT(absl::atomic_hook_internal::default_func_calls, Eq(0)); + EXPECT_THAT(override_func_calls, Eq(0)); + absl::atomic_hook_internal::func(); + EXPECT_THAT(absl::atomic_hook_internal::default_func_calls, Eq(0)); + EXPECT_THAT(override_func_calls, Eq(1)); + EXPECT_THAT(absl::atomic_hook_internal::func.Load(), Eq(OverrideFunc)); +} + } // namespace diff --git a/absl/base/internal/atomic_hook_test_helper.cc b/absl/base/internal/atomic_hook_test_helper.cc new file mode 100644 index 00000000..fad7a89a --- /dev/null +++ b/absl/base/internal/atomic_hook_test_helper.cc @@ -0,0 +1,29 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/base/internal/atomic_hook_test_helper.h" + +#include "absl/base/attributes.h" +#include "absl/base/internal/atomic_hook.h" + +namespace absl { +namespace atomic_hook_internal { + +ABSL_CONST_INIT absl::base_internal::AtomicHook func(DefaultFunc); +ABSL_CONST_INIT int default_func_calls = 0; +void DefaultFunc() { default_func_calls++; } +void RegisterFunc(VoidF f) { func.Store(f); } + +} // namespace atomic_hook_internal +} // namespace absl diff --git a/absl/base/internal/atomic_hook_test_helper.h b/absl/base/internal/atomic_hook_test_helper.h new file mode 100644 index 00000000..44ff780d --- /dev/null +++ b/absl/base/internal/atomic_hook_test_helper.h @@ -0,0 +1,32 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_BASE_ATOMIC_HOOK_TEST_HELPER_H_ +#define ABSL_BASE_ATOMIC_HOOK_TEST_HELPER_H_ + +#include "absl/base/internal/atomic_hook.h" + +namespace absl { +namespace atomic_hook_internal { + +using VoidF = void (*)(); +extern absl::base_internal::AtomicHook func; +extern int default_func_calls; +void DefaultFunc(); +void RegisterFunc(VoidF func); + +} // namespace atomic_hook_internal +} // namespace absl + +#endif // ABSL_BASE_ATOMIC_HOOK_TEST_HELPER_H_ diff --git a/absl/container/internal/hashtablez_sampler.cc b/absl/container/internal/hashtablez_sampler.cc index d03dd82e..054e8981 100644 --- a/absl/container/internal/hashtablez_sampler.cc +++ b/absl/container/internal/hashtablez_sampler.cc @@ -228,8 +228,26 @@ int64_t HashtablezSampler::Iterate( return dropped_samples_.load(std::memory_order_relaxed); } +static bool ShouldForceSampling() { + enum ForceState { + kDontForce, + kForce, + kUninitialized + }; + ABSL_CONST_INIT static std::atomic global_state{ + kUninitialized}; + ForceState state = global_state.load(std::memory_order_relaxed); + if (ABSL_PREDICT_TRUE(state == kDontForce)) return false; + + if (state == kUninitialized) { + state = AbslContainerInternalSampleEverything() ? kForce : kDontForce; + global_state.store(state, std::memory_order_relaxed); + } + return state == kForce; +} + HashtablezInfo* SampleSlow(int64_t* next_sample) { - if (kAbslContainerInternalSampleEverything) { + if (ABSL_PREDICT_FALSE(ShouldForceSampling())) { *next_sample = 1; return HashtablezSampler::Global().Register(); } diff --git a/absl/container/internal/hashtablez_sampler.h b/absl/container/internal/hashtablez_sampler.h index 41121754..53996bb9 100644 --- a/absl/container/internal/hashtablez_sampler.h +++ b/absl/container/internal/hashtablez_sampler.h @@ -280,7 +280,7 @@ void SetHashtablezMaxSamples(int32_t max); // initialization of static storage duration objects. // The definition of this constant is weak, which allows us to inject a // different value for it at link time. -extern "C" const bool kAbslContainerInternalSampleEverything; +extern "C" bool AbslContainerInternalSampleEverything(); } // namespace container_internal } // namespace absl diff --git a/absl/container/internal/hashtablez_sampler_force_weak_definition.cc b/absl/container/internal/hashtablez_sampler_force_weak_definition.cc index 4ca6ffda..984dce5d 100644 --- a/absl/container/internal/hashtablez_sampler_force_weak_definition.cc +++ b/absl/container/internal/hashtablez_sampler_force_weak_definition.cc @@ -20,8 +20,9 @@ namespace absl { namespace container_internal { // See hashtablez_sampler.h for details. -extern "C" ABSL_ATTRIBUTE_WEAK const bool - kAbslContainerInternalSampleEverything = false; +extern "C" ABSL_ATTRIBUTE_WEAK bool AbslContainerInternalSampleEverything() { + return false; +} } // namespace container_internal } // namespace absl diff --git a/absl/random/exponential_distribution_test.cc b/absl/random/exponential_distribution_test.cc index dc49044d..f3cfd764 100644 --- a/absl/random/exponential_distribution_test.cc +++ b/absl/random/exponential_distribution_test.cc @@ -46,7 +46,11 @@ using absl::random_internal::kChiSquared; template class ExponentialDistributionTypedTest : public ::testing::Test {}; +#if defined(__EMSCRIPTEN__) +using RealTypes = ::testing::Types; +#else using RealTypes = ::testing::Types; +#endif // defined(__EMSCRIPTEN__) TYPED_TEST_CASE(ExponentialDistributionTypedTest, RealTypes); TYPED_TEST(ExponentialDistributionTypedTest, SerializeTest) { diff --git a/absl/random/internal/iostream_state_saver_test.cc b/absl/random/internal/iostream_state_saver_test.cc index 2ecbaac1..722766d0 100644 --- a/absl/random/internal/iostream_state_saver_test.cc +++ b/absl/random/internal/iostream_state_saver_test.cc @@ -272,6 +272,7 @@ TEST(IOStreamStateSaver, RoundTripDoubles) { } } +#if !defined(__EMSCRIPTEN__) TEST(IOStreamStateSaver, RoundTripLongDoubles) { // Technically, C++ only guarantees that long double is at least as large as a // double. Practically it varies from 64-bits to 128-bits. @@ -349,6 +350,7 @@ TEST(IOStreamStateSaver, RoundTripLongDoubles) { } } } +#endif // !defined(__EMSCRIPTEN__) TEST(StrToDTest, DoubleMin) { const char kV[] = "2.22507385850720138e-308"; diff --git a/absl/random/uniform_real_distribution_test.cc b/absl/random/uniform_real_distribution_test.cc index 597f0ee5..9f14d1c5 100644 --- a/absl/random/uniform_real_distribution_test.cc +++ b/absl/random/uniform_real_distribution_test.cc @@ -54,7 +54,12 @@ namespace { template class UniformRealDistributionTest : public ::testing::Test {}; +#if defined(__EMSCRIPTEN__) +using RealTypes = ::testing::Types; +#else using RealTypes = ::testing::Types; +#endif // defined(__EMSCRIPTEN__) + TYPED_TEST_SUITE(UniformRealDistributionTest, RealTypes); TYPED_TEST(UniformRealDistributionTest, ParamSerializeTest) { -- cgit v1.2.3 From 2796d500aea5a31d26b8b24a33fab7a1c8fa2f32 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Mon, 21 Oct 2019 10:21:03 -0700 Subject: Export of internal Abseil changes -- b770d03c2f1042d399c3f9576e881691cbe962c4 by Abseil Team : Avoid applying the workaround for MSVC's static initialization problems when using clang-cl. PiperOrigin-RevId: 275870089 -- 2b8fc02ec49aeb5ad56cef20259cdd7f6ee0c917 by Abseil Team : Document return values. PiperOrigin-RevId: 275839042 -- baa99064f9a28b188661df7fba989fcd558818af by Abseil Team : Support "auto" and other uncommon builtin types in absl::debugging_internal::Demangle. PiperOrigin-RevId: 275556195 -- f9d5c6a3a0d374dbf105d6e2e9e0c8fa949ed187 by Gennadiy Rozental : Internal rework. PiperOrigin-RevId: 275550005 -- 2679a77db5b26349e8c8b2059621af55d2fca139 by Mark Barolak : Remove a comment reference to the no longer extant ::string implementation. PiperOrigin-RevId: 275531987 -- 7b427a7613c44a98c6f13da43b2bff2837ca6b19 by Derek Mauro : Upgrade to Bazel 1.0.0 and CMake 3.15.4 PiperOrigin-RevId: 275500823 -- 81f7d20905debf9d1e300bd2e9899f88d27f632a by Derek Mauro : Fix -Wimplicit-int-float-conversion warning in latest clang PiperOrigin-RevId: 275492439 GitOrigin-RevId: b770d03c2f1042d399c3f9576e881691cbe962c4 Change-Id: I9b39dad524489f0d62c912d02e8ac43761c81e55 --- absl/debugging/internal/demangle.cc | 34 ++- absl/debugging/stacktrace.h | 30 +- absl/flags/BUILD.bazel | 4 +- absl/flags/declare.h | 2 +- absl/flags/flag.cc | 2 +- absl/flags/flag.h | 10 +- absl/flags/flag_test.cc | 9 +- absl/flags/internal/commandlineflag.cc | 333 --------------------- absl/flags/internal/commandlineflag.h | 187 ++++-------- absl/flags/internal/flag.cc | 337 ++++++++++++++++++++-- absl/flags/internal/flag.h | 237 ++++++++++----- absl/flags/internal/registry.cc | 45 +-- absl/flags/internal/registry.h | 6 +- absl/random/internal/iostream_state_saver_test.cc | 8 +- absl/strings/internal/resize_uninitialized.h | 3 +- ci/cmake_install_test.sh | 3 +- ci/linux_clang-latest_libcxx_asan_bazel.sh | 2 +- ci/linux_clang-latest_libcxx_bazel.sh | 2 +- ci/linux_clang-latest_libcxx_tsan_bazel.sh | 2 +- ci/linux_clang-latest_libstdcxx_bazel.sh | 2 +- ci/linux_gcc-4.9_libstdcxx_bazel.sh | 2 +- ci/linux_gcc-latest_libstdcxx_bazel.sh | 2 +- ci/linux_gcc-latest_libstdcxx_cmake.sh | 2 +- 23 files changed, 634 insertions(+), 630 deletions(-) (limited to 'absl/random/internal/iostream_state_saver_test.cc') diff --git a/absl/debugging/internal/demangle.cc b/absl/debugging/internal/demangle.cc index 52a553fd..3809e496 100644 --- a/absl/debugging/internal/demangle.cc +++ b/absl/debugging/internal/demangle.cc @@ -93,6 +93,8 @@ static const AbbrevPair kOperatorList[] = { }; // List of builtin types from Itanium C++ ABI. +// +// Invariant: only one- or two-character type abbreviations here. static const AbbrevPair kBuiltinTypeList[] = { {"v", "void", 0}, {"w", "wchar_t", 0}, @@ -115,6 +117,16 @@ static const AbbrevPair kBuiltinTypeList[] = { {"e", "long double", 0}, {"g", "__float128", 0}, {"z", "ellipsis", 0}, + + {"De", "decimal128", 0}, // IEEE 754r decimal floating point (128 bits) + {"Dd", "decimal64", 0}, // IEEE 754r decimal floating point (64 bits) + {"Dc", "decltype(auto)", 0}, + {"Da", "auto", 0}, + {"Dn", "std::nullptr_t", 0}, // i.e., decltype(nullptr) + {"Df", "decimal32", 0}, // IEEE 754r decimal floating point (32 bits) + {"Di", "char32_t", 0}, + {"Ds", "char16_t", 0}, + {"Dh", "float16", 0}, // IEEE 754r half-precision float (16 bits) {nullptr, nullptr, 0}, }; @@ -1168,12 +1180,6 @@ static bool ParseType(State *state) { } state->parse_state = copy; - // nullptr_t, i.e. decltype(nullptr). - if (ParseTwoCharToken(state, "Dn")) { - return true; - } - state->parse_state = copy; - if (ParseOneCharToken(state, 'U') && ParseSourceName(state) && ParseType(state)) { return true; @@ -1214,16 +1220,26 @@ static bool ParseCVQualifiers(State *state) { return num_cv_qualifiers > 0; } -// ::= v, etc. +// ::= v, etc. # single-character builtin types // ::= u +// ::= Dd, etc. # two-character builtin types +// +// Not supported: +// ::= DF _ # _FloatN (N bits) +// static bool ParseBuiltinType(State *state) { ComplexityGuard guard(state); if (guard.IsTooComplex()) return false; const AbbrevPair *p; for (p = kBuiltinTypeList; p->abbrev != nullptr; ++p) { - if (RemainingInput(state)[0] == p->abbrev[0]) { + // Guaranteed only 1- or 2-character strings in kBuiltinTypeList. + if (p->abbrev[1] == '\0') { + if (ParseOneCharToken(state, p->abbrev[0])) { + MaybeAppend(state, p->real_name); + return true; + } + } else if (p->abbrev[2] == '\0' && ParseTwoCharToken(state, p->abbrev)) { MaybeAppend(state, p->real_name); - ++state->parse_state.mangled_idx; return true; } } diff --git a/absl/debugging/stacktrace.h b/absl/debugging/stacktrace.h index 3fc1c03f..cd8fae76 100644 --- a/absl/debugging/stacktrace.h +++ b/absl/debugging/stacktrace.h @@ -36,10 +36,10 @@ namespace absl { // GetStackFrames() // // Records program counter values for up to `max_depth` frames, skipping the -// most recent `skip_count` stack frames, and stores their corresponding values -// and sizes in `results` and `sizes` buffers. (Note that the frame generated -// for the `absl::GetStackFrames()` routine itself is also skipped.) -// routine itself. +// most recent `skip_count` stack frames, stores their corresponding values +// and sizes in `results` and `sizes` buffers, and returns the number of frames +// stored. (Note that the frame generated for the `absl::GetStackFrames()` +// routine itself is also skipped.) // // Example: // @@ -54,8 +54,8 @@ namespace absl { // The current stack frame would consist of three function calls: `bar()`, // `foo()`, and then `main()`; however, since the `GetStackFrames()` call sets // `skip_count` to `1`, it will skip the frame for `bar()`, the most recently -// invoked function call. It will therefore return two program counters and will -// produce values that map to the following function calls: +// invoked function call. It will therefore return 2 and fill `result` with +// program counters within the following functions: // // result[0] foo() // result[1] main() @@ -82,9 +82,10 @@ extern int GetStackFrames(void** result, int* sizes, int max_depth, // // Records program counter values obtained from a signal handler. Records // program counter values for up to `max_depth` frames, skipping the most recent -// `skip_count` stack frames, and stores their corresponding values and sizes in -// `results` and `sizes` buffers. (Note that the frame generated for the -// `absl::GetStackFramesWithContext()` routine itself is also skipped.) +// `skip_count` stack frames, stores their corresponding values and sizes in +// `results` and `sizes` buffers, and returns the number of frames stored. (Note +// that the frame generated for the `absl::GetStackFramesWithContext()` routine +// itself is also skipped.) // // The `uc` parameter, if non-null, should be a pointer to a `ucontext_t` value // passed to a signal handler registered via the `sa_sigaction` field of a @@ -105,8 +106,9 @@ extern int GetStackFramesWithContext(void** result, int* sizes, int max_depth, // GetStackTrace() // // Records program counter values for up to `max_depth` frames, skipping the -// most recent `skip_count` stack frames, and stores their corresponding values -// in `results`. Note that this function is similar to `absl::GetStackFrames()` +// most recent `skip_count` stack frames, stores their corresponding values +// in `results`, and returns the number of frames +// stored. Note that this function is similar to `absl::GetStackFrames()` // except that it returns the stack trace only, and not stack frame sizes. // // Example: @@ -131,9 +133,9 @@ extern int GetStackTrace(void** result, int max_depth, int skip_count); // // Records program counter values obtained from a signal handler. Records // program counter values for up to `max_depth` frames, skipping the most recent -// `skip_count` stack frames, and stores their corresponding values in -// `results`. (Note that the frame generated for the -// `absl::GetStackFramesWithContext()` routine itself is also skipped.) +// `skip_count` stack frames, stores their corresponding values in `results`, +// and returns the number of frames stored. (Note that the frame generated for +// the `absl::GetStackFramesWithContext()` routine itself is also skipped.) // // The `uc` parameter, if non-null, should be a pointer to a `ucontext_t` value // passed to a signal handler registered via the `sa_sigaction` field of a diff --git a/absl/flags/BUILD.bazel b/absl/flags/BUILD.bazel index 2e0dc389..cf449198 100644 --- a/absl/flags/BUILD.bazel +++ b/absl/flags/BUILD.bazel @@ -40,6 +40,7 @@ cc_library( deps = [ ":handle", ":registry", + "//absl/base:core_headers", "//absl/memory", "//absl/strings", "//absl/synchronization", @@ -135,9 +136,6 @@ cc_library( ":config", ":marshalling", "//absl/base:core_headers", - "//absl/base:raw_logging_internal", - "//absl/strings", - "//absl/synchronization", "//absl/types:optional", ], ) diff --git a/absl/flags/declare.h b/absl/flags/declare.h index 0a113a2b..4926a09e 100644 --- a/absl/flags/declare.h +++ b/absl/flags/declare.h @@ -39,7 +39,7 @@ class Flag; // Flag // // Forward declaration of the `absl::Flag` type for use in defining the macro. -#if defined(_MSC_VER) +#if defined(_MSC_VER) && !defined(__clang__) template class Flag; #else diff --git a/absl/flags/flag.cc b/absl/flags/flag.cc index 69038bbf..37bbce23 100644 --- a/absl/flags/flag.cc +++ b/absl/flags/flag.cc @@ -43,7 +43,7 @@ ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(ABSL_FLAGS_ATOMIC_GET) // This global nutex protects on-demand construction of flag objects in MSVC // builds. -#if defined(_MSC_VER) +#if defined(_MSC_VER) && !defined(__clang__) namespace flags_internal { diff --git a/absl/flags/flag.h b/absl/flags/flag.h index 85900ef8..c0060b44 100644 --- a/absl/flags/flag.h +++ b/absl/flags/flag.h @@ -63,7 +63,7 @@ namespace absl { // ABSL_FLAG(int, count, 0, "Count of items to process"); // // No public methods of `absl::Flag` are part of the Abseil Flags API. -#if !defined(_MSC_VER) +#if !defined(_MSC_VER) || defined(__clang__) template using Flag = flags_internal::Flag; #else @@ -119,7 +119,6 @@ class Flag { absl::string_view Name() const { return GetImpl()->Name(); } std::string Help() const { return GetImpl()->Help(); } bool IsModified() const { return GetImpl()->IsModified(); } - void SetModified(bool is_modified) { GetImpl()->SetModified(is_modified); } bool IsSpecifiedOnCommandLine() const { return GetImpl()->IsSpecifiedOnCommandLine(); } @@ -127,9 +126,6 @@ class Flag { std::string Filename() const { return GetImpl()->Filename(); } std::string DefaultValue() const { return GetImpl()->DefaultValue(); } std::string CurrentValue() const { return GetImpl()->CurrentValue(); } - bool InvokeValidator(const void* value) const { - return GetImpl()->InvokeValidator(value); - } template inline bool IsOfType() const { return GetImpl()->template IsOfType(); @@ -272,7 +268,7 @@ void SetFlag(absl::Flag* flag, const V& v) { #if ABSL_FLAGS_STRIP_NAMES #define ABSL_FLAG_IMPL_FLAGNAME(txt) "" #define ABSL_FLAG_IMPL_FILENAME() "" -#if !defined(_MSC_VER) +#if !defined(_MSC_VER) || defined(__clang__) #define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \ absl::flags_internal::FlagRegistrar(&flag) #else @@ -282,7 +278,7 @@ void SetFlag(absl::Flag* flag, const V& v) { #else #define ABSL_FLAG_IMPL_FLAGNAME(txt) txt #define ABSL_FLAG_IMPL_FILENAME() __FILE__ -#if !defined(_MSC_VER) +#if !defined(_MSC_VER) || defined(__clang__) #define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \ absl::flags_internal::FlagRegistrar(&flag) #else diff --git a/absl/flags/flag_test.cc b/absl/flags/flag_test.cc index a652042c..59dc579c 100644 --- a/absl/flags/flag_test.cc +++ b/absl/flags/flag_test.cc @@ -42,15 +42,14 @@ void TestCallback() {} template bool TestConstructionFor() { constexpr flags::Flag f1("f1", &TestHelpMsg, "file", - &absl::flags_internal::FlagMarshallingOps, - &TestMakeDflt); + &flags::FlagMarshallingOps, &TestMakeDflt); EXPECT_EQ(f1.Name(), "f1"); EXPECT_EQ(f1.Help(), "help"); EXPECT_EQ(f1.Filename(), "file"); - ABSL_CONST_INIT static flags::Flag f2( - "f2", &TestHelpMsg, "file", &absl::flags_internal::FlagMarshallingOps, - &TestMakeDflt); + ABSL_CONST_INIT static flags::Flag f2("f2", &TestHelpMsg, "file", + &flags::FlagMarshallingOps, + &TestMakeDflt); flags::FlagRegistrar(&f2).OnUpdate(TestCallback); EXPECT_EQ(f2.Name(), "f2"); diff --git a/absl/flags/internal/commandlineflag.cc b/absl/flags/internal/commandlineflag.cc index 99f73611..caf33bc4 100644 --- a/absl/flags/internal/commandlineflag.cc +++ b/absl/flags/internal/commandlineflag.cc @@ -15,14 +15,7 @@ #include "absl/flags/internal/commandlineflag.h" -#include - -#include "absl/base/internal/raw_logging.h" -#include "absl/base/optimization.h" -#include "absl/flags/config.h" #include "absl/flags/usage_config.h" -#include "absl/strings/str_cat.h" -#include "absl/synchronization/mutex.h" namespace absl { namespace flags_internal { @@ -35,80 +28,6 @@ namespace flags_internal { // This is used by this file, and also in commandlineflags_reporting.cc const char kStrippedFlagHelp[] = "\001\002\003\004 (unknown) \004\003\002\001"; -namespace { - -// Currently we only validate flag values for user-defined flag types. -bool ShouldValidateFlagValue(const CommandLineFlag& flag) { -#define DONT_VALIDATE(T) \ - if (flag.IsOfType()) return false; - ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(DONT_VALIDATE) - DONT_VALIDATE(std::string) - DONT_VALIDATE(std::vector) -#undef DONT_VALIDATE - - return true; -} - -} // namespace - -absl::Mutex* InitFlag(CommandLineFlag* flag) { - ABSL_CONST_INIT static absl::Mutex init_lock(absl::kConstInit); - absl::Mutex* mu; - - { - absl::MutexLock lock(&init_lock); - - if (flag->locks_ == nullptr) { // Must initialize Mutexes for this flag. - flag->locks_ = new flags_internal::CommandLineFlagLocks; - } - - mu = &flag->locks_->primary_mu; - } - - { - absl::MutexLock lock(mu); - - if (!flag->IsRetired() && flag->def_ == nullptr) { - // Need to initialize def and cur fields. - flag->def_ = (*flag->make_init_value_)(); - flag->cur_ = Clone(flag->op_, flag->def_); - UpdateCopy(flag); - flag->inited_.store(true, std::memory_order_release); - flag->InvokeCallback(); - } - } - - flag->inited_.store(true, std::memory_order_release); - return mu; -} - -// Ensure that the lazily initialized fields of *flag have been initialized, -// and return &flag->locks_->primary_mu. -absl::Mutex* CommandLineFlag::InitFlagIfNecessary() const - ABSL_LOCK_RETURNED(locks_->primary_mu) { - if (!inited_.load(std::memory_order_acquire)) { - return InitFlag(const_cast(this)); - } - - // All fields initialized; locks_ is therefore safe to read. - return &locks_->primary_mu; -} - -bool CommandLineFlag::IsModified() const { - absl::MutexLock l(InitFlagIfNecessary()); - return modified_; -} - -void CommandLineFlag::SetModified(bool is_modified) { - absl::MutexLock l(InitFlagIfNecessary()); - modified_ = is_modified; -} - -bool CommandLineFlag::IsSpecifiedOnCommandLine() const { - absl::MutexLock l(InitFlagIfNecessary()); - return on_command_line_; -} - absl::string_view CommandLineFlag::Typename() const { // We do not store/report type in Abseil Flags, so that user do not rely on in // at runtime @@ -137,223 +56,6 @@ std::string CommandLineFlag::Filename() const { return flags_internal::GetUsageConfig().normalize_filename(filename_); } -std::string CommandLineFlag::DefaultValue() const { - absl::MutexLock l(InitFlagIfNecessary()); - - return Unparse(marshalling_op_, def_); -} - -std::string CommandLineFlag::CurrentValue() const { - absl::MutexLock l(InitFlagIfNecessary()); - - return Unparse(marshalling_op_, cur_); -} - -int64_t CommandLineFlag::MutationCounter() const { - absl::MutexLock l(InitFlagIfNecessary()); - - return counter_; -} - -// Attempts to parse supplied `value` string using parsing routine in the `flag` -// argument. If parsing is successful, it will try to validate that the parsed -// value is valid for the specified 'flag'. Finally this function stores the -// parsed value in 'dst' assuming it is a pointer to the flag's value type. In -// case if any error is encountered in either step, the error message is stored -// in 'err' -bool TryParseLocked(CommandLineFlag* flag, void* dst, absl::string_view value, - std::string* err) - ABSL_EXCLUSIVE_LOCKS_REQUIRED(flag->locks_->primary_mu) { - void* tentative_value = Clone(flag->op_, flag->def_); - std::string parse_err; - if (!Parse(flag->marshalling_op_, value, tentative_value, &parse_err)) { - auto type_name = flag->Typename(); - absl::string_view err_sep = parse_err.empty() ? "" : "; "; - absl::string_view typename_sep = type_name.empty() ? "" : " "; - *err = absl::StrCat("Illegal value '", value, "' specified for", - typename_sep, type_name, " flag '", flag->Name(), "'", - err_sep, parse_err); - Delete(flag->op_, tentative_value); - return false; - } - - if (!flag->InvokeValidator(tentative_value)) { - *err = absl::StrCat("Failed validation of new value '", - Unparse(flag->marshalling_op_, tentative_value), - "' for flag '", flag->Name(), "'"); - Delete(flag->op_, tentative_value); - return false; - } - - flag->counter_++; - Copy(flag->op_, tentative_value, dst); - Delete(flag->op_, tentative_value); - return true; -} - -// Sets the value of the flag based on specified string `value`. If the flag -// was successfully set to new value, it returns true. Otherwise, sets `err` -// to indicate the error, leaves the flag unchanged, and returns false. There -// are three ways to set the flag's value: -// * Update the current flag value -// * Update the flag's default value -// * Update the current flag value if it was never set before -// The mode is selected based on 'set_mode' parameter. -bool CommandLineFlag::SetFromString(absl::string_view value, - FlagSettingMode set_mode, - ValueSource source, std::string* err) { - if (IsRetired()) return false; - - absl::MutexLock l(InitFlagIfNecessary()); - - // Direct-access flags can be modified without going through the - // flag API. Detect such changes and update the flag->modified_ bit. - if (!IsAbseilFlag()) { - if (!modified_ && ChangedDirectly(this, cur_, def_)) { - modified_ = true; - } - } - - switch (set_mode) { - case SET_FLAGS_VALUE: { - // set or modify the flag's value - if (!TryParseLocked(this, cur_, value, err)) return false; - modified_ = true; - UpdateCopy(this); - InvokeCallback(); - - if (source == kCommandLine) { - on_command_line_ = true; - } - break; - } - case SET_FLAG_IF_DEFAULT: { - // set the flag's value, but only if it hasn't been set by someone else - if (!modified_) { - if (!TryParseLocked(this, cur_, value, err)) return false; - modified_ = true; - UpdateCopy(this); - InvokeCallback(); - } else { - // TODO(rogeeff): review and fix this semantic. Currently we do not fail - // in this case if flag is modified. This is misleading since the flag's - // value is not updated even though we return true. - // *err = absl::StrCat(Name(), " is already set to ", - // CurrentValue(), "\n"); - // return false; - return true; - } - break; - } - case SET_FLAGS_DEFAULT: { - // modify the flag's default-value - if (!TryParseLocked(this, def_, value, err)) return false; - - if (!modified_) { - // Need to set both defvalue *and* current, in this case - Copy(op_, def_, cur_); - UpdateCopy(this); - InvokeCallback(); - } - break; - } - default: { - // unknown set_mode - assert(false); - return false; - } - } - - return true; -} - -void CommandLineFlag::CheckDefaultValueParsingRoundtrip() const { - std::string v = DefaultValue(); - - absl::MutexLock lock(InitFlagIfNecessary()); - - void* dst = Clone(op_, def_); - std::string error; - if (!flags_internal::Parse(marshalling_op_, v, dst, &error)) { - ABSL_INTERNAL_LOG( - FATAL, - absl::StrCat("Flag ", Name(), " (from ", Filename(), - "): std::string form of default value '", v, - "' could not be parsed; error=", error)); - } - - // We do not compare dst to def since parsing/unparsing may make - // small changes, e.g., precision loss for floating point types. - Delete(op_, dst); -} - -bool CommandLineFlag::ValidateDefaultValue() const { - absl::MutexLock lock(InitFlagIfNecessary()); - return InvokeValidator(def_); -} - -bool CommandLineFlag::ValidateInputValue(absl::string_view value) const { - absl::MutexLock l(InitFlagIfNecessary()); // protect default value access - - void* obj = Clone(op_, def_); - std::string ignored_error; - const bool result = - flags_internal::Parse(marshalling_op_, value, obj, &ignored_error) && - InvokeValidator(obj); - Delete(op_, obj); - return result; -} - -void CommandLineFlag::Read(void* dst, - const flags_internal::FlagOpFn dst_op) const { - absl::ReaderMutexLock l(InitFlagIfNecessary()); - - // `dst_op` is the unmarshaling operation corresponding to the declaration - // visibile at the call site. `op` is the Flag's defined unmarshalling - // operation. They must match for this operation to be well-defined. - if (ABSL_PREDICT_FALSE(dst_op != op_)) { - ABSL_INTERNAL_LOG( - ERROR, - absl::StrCat("Flag '", Name(), - "' is defined as one type and declared as another")); - } - CopyConstruct(op_, cur_, dst); -} - -void CommandLineFlag::Write(const void* src, - const flags_internal::FlagOpFn src_op) { - absl::MutexLock l(InitFlagIfNecessary()); - - // `src_op` is the marshalling operation corresponding to the declaration - // visible at the call site. `op` is the Flag's defined marshalling operation. - // They must match for this operation to be well-defined. - if (ABSL_PREDICT_FALSE(src_op != op_)) { - ABSL_INTERNAL_LOG( - ERROR, - absl::StrCat("Flag '", Name(), - "' is defined as one type and declared as another")); - } - - if (ShouldValidateFlagValue(*this)) { - void* obj = Clone(op_, src); - std::string ignored_error; - std::string src_as_str = Unparse(marshalling_op_, src); - if (!Parse(marshalling_op_, src_as_str, obj, &ignored_error) || - !InvokeValidator(obj)) { - ABSL_INTERNAL_LOG(ERROR, absl::StrCat("Attempt to set flag '", Name(), - "' to invalid value ", src_as_str)); - } - Delete(op_, obj); - } - - modified_ = true; - counter_++; - Copy(op_, src, cur_); - - UpdateCopy(this); - InvokeCallback(); -} - std::string HelpText::GetHelpText() const { if (help_function_) return help_function_(); if (help_message_) return help_message_; @@ -361,40 +63,5 @@ std::string HelpText::GetHelpText() const { return {}; } -// Update any copy of the flag value that is stored in an atomic word. -// In addition if flag has a mutation callback this function invokes it. -void UpdateCopy(CommandLineFlag* flag) { -#define STORE_ATOMIC(T) \ - else if (flag->IsOfType()) { \ - flag->StoreAtomic(); \ - } - - if (false) { - } - ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(STORE_ATOMIC) -#undef STORE_ATOMIC -} - -// Return true iff flag value was changed via direct-access. -bool ChangedDirectly(CommandLineFlag* flag, const void* a, const void* b) { - if (!flag->IsAbseilFlag()) { -// Need to compare values for direct-access flags. -#define CHANGED_FOR_TYPE(T) \ - if (flag->IsOfType()) { \ - return *reinterpret_cast(a) != *reinterpret_cast(b); \ - } - - CHANGED_FOR_TYPE(bool); - CHANGED_FOR_TYPE(int32_t); - CHANGED_FOR_TYPE(int64_t); - CHANGED_FOR_TYPE(uint64_t); - CHANGED_FOR_TYPE(double); - CHANGED_FOR_TYPE(std::string); -#undef CHANGED_FOR_TYPE - } - - return false; -} - } // namespace flags_internal } // namespace absl diff --git a/absl/flags/internal/commandlineflag.h b/absl/flags/internal/commandlineflag.h index 528d3106..13a3352e 100644 --- a/absl/flags/internal/commandlineflag.h +++ b/absl/flags/internal/commandlineflag.h @@ -16,12 +16,10 @@ #ifndef ABSL_FLAGS_INTERNAL_COMMANDLINEFLAG_H_ #define ABSL_FLAGS_INTERNAL_COMMANDLINEFLAG_H_ -#include #include #include "absl/base/macros.h" #include "absl/flags/marshalling.h" -#include "absl/synchronization/mutex.h" #include "absl/types/optional.h" namespace absl { @@ -151,14 +149,6 @@ inline size_t Sizeof(FlagOpFn op) { op(flags_internal::kSizeof, nullptr, nullptr))); } -// The following struct contains the locks in a CommandLineFlag struct. -// They are in a separate struct that is lazily allocated to avoid problems -// with static initialization and to avoid multiple allocations. -struct CommandLineFlagLocks { - absl::Mutex primary_mu; // protects several fields in CommandLineFlag - absl::Mutex callback_mu; // used to serialize callbacks -}; - // Holds either a pointer to help text or a function which produces it. This is // needed for supporting both static initialization of Flags while supporting // the legacy registration framework. We can't use absl::variant inline bool IsOfType() const { - return op_ == &flags_internal::FlagOps; + return TypeId() == &flags_internal::FlagOps; } // Attempts to retrieve the flag value. Returns value on success, // absl::nullopt otherwise. template absl::optional Get() const { - if (IsRetired() || flags_internal::FlagOps != op_) return absl::nullopt; - - T res; - Read(&res, flags_internal::FlagOps); + if (IsRetired() || !IsOfType()) { + return absl::nullopt; + } - return res; + // Implementation notes: + // + // We are wrapping a union around the value of `T` to serve three purposes: + // + // 1. `U.value` has correct size and alignment for a value of type `T` + // 2. The `U.value` constructor is not invoked since U's constructor does + // not + // do it explicitly. + // 3. The `U.value` destructor is invoked since U's destructor does it + // explicitly. This makes `U` a kind of RAII wrapper around non default + // constructible value of T, which is destructed when we leave the + // scope. We do need to destroy U.value, which is constructed by + // CommandLineFlag::Read even though we left it in a moved-from state + // after std::move. + // + // All of this serves to avoid requiring `T` being default constructible. + union U { + T value; + U() {} + ~U() { value.~T(); } + }; + U u; + + Read(&u.value); + return std::move(u.value); } + // Polymorphic access methods + + // Returns true iff this object corresponds to retired flag + virtual bool IsRetired() const { return false; } + // Returns true iff this is a handle to an Abseil Flag. + virtual bool IsAbseilFlag() const { return true; } + // Returns id of the flag's value type. + virtual flags_internal::FlagOpFn TypeId() const = 0; + virtual bool IsModified() const = 0; + virtual bool IsSpecifiedOnCommandLine() const = 0; + virtual std::string DefaultValue() const = 0; + virtual std::string CurrentValue() const = 0; + + // Interfaces to operate on validators. + virtual bool ValidateInputValue(absl::string_view value) const = 0; + // Interface to save flag to some persistent state. Returns current flag state // or nullptr if flag does not support saving and restoring a state. virtual std::unique_ptr SaveState() = 0; - // Interfaces to overate on callbacks. - virtual void InvokeCallback() {} - // Sets the value of the flag based on specified std::string `value`. If the flag // was successfully set to new value, it returns true. Otherwise, sets `error` // to indicate the error, leaves the flag unchanged, and returns false. There @@ -289,74 +276,28 @@ class CommandLineFlag { // * Update the flag's default value // * Update the current flag value if it was never set before // The mode is selected based on `set_mode` parameter. - bool SetFromString(absl::string_view value, - flags_internal::FlagSettingMode set_mode, - flags_internal::ValueSource source, std::string* error); + virtual bool SetFromString(absl::string_view value, + flags_internal::FlagSettingMode set_mode, + flags_internal::ValueSource source, + std::string* error) = 0; - void CheckDefaultValueParsingRoundtrip() const; + // Checks that flags default value can be converted to std::string and back to the + // flag's value type. + virtual void CheckDefaultValueParsingRoundtrip() const = 0; // Constant configuration for a particular flag. protected: ~CommandLineFlag() = default; - // Thread safe access to mutation counter. - int64_t MutationCounter() const; - - const char* const name_; - const HelpText help_; - const char* const filename_; - - const FlagOpFn op_; // Type-specific handler - const FlagMarshallingOpFn marshalling_op_; // Marshalling ops handler - const InitialValGenFunc make_init_value_; // Makes initial value for the flag - std::atomic inited_; // fields have been lazily initialized - - // Mutable state (guarded by locks_->primary_mu). - bool modified_; // Has flag value been modified? - bool on_command_line_; // Specified on command line. - void* def_; // Lazily initialized pointer to default value - void* cur_; // Lazily initialized pointer to current value - int64_t counter_; // Mutation counter - - // Lazily initialized mutexes for this flag value. We cannot inline a - // SpinLock or Mutex here because those have non-constexpr constructors and - // so would prevent constant initialization of this type. - // TODO(rogeeff): fix it once Mutex has constexpr constructor - struct CommandLineFlagLocks* locks_; // locks, laziliy allocated. - - // Ensure that the lazily initialized fields of *flag have been initialized, - // and return the lock which should be locked when flag's state is mutated. - absl::Mutex* InitFlagIfNecessary() const ABSL_LOCK_RETURNED(locks_->primary_mu); - - // copy construct new value of flag's type in a memory referenced by dst - // based on current flag's value - void Read(void* dst, const flags_internal::FlagOpFn dst_op) const; - // updates flag's value to *src (locked) - void Write(const void* src, const flags_internal::FlagOpFn src_op); - - friend class FlagRegistry; - friend class FlagPtrMap; - friend class FlagSaverImpl; - friend bool TryParseLocked(CommandLineFlag* flag, void* dst, - absl::string_view value, std::string* err); - friend absl::Mutex* InitFlag(CommandLineFlag* flag); -}; + const char* const name_; // Flags name passed to ABSL_FLAG as second arg. + const HelpText help_; // The function generating help message. + const char* const filename_; // The file name where ABSL_FLAG resides. -// Update any copy of the flag value that is stored in an atomic word. -// In addition if flag has a mutation callback this function invokes it. While -// callback is being invoked the primary flag's mutex is unlocked and it is -// re-locked back after call to callback is completed. Callback invocation is -// guarded by flag's secondary mutex instead which prevents concurrent callback -// invocation. Note that it is possible for other thread to grab the primary -// lock and update flag's value at any time during the callback invocation. -// This is by design. Callback can get a value of the flag if necessary, but it -// might be different from the value initiated the callback and it also can be -// different by the time the callback invocation is completed. -// Requires that *primary_lock be held in exclusive mode; it may be released -// and reacquired by the implementation. -void UpdateCopy(CommandLineFlag* flag); -// Return true iff flag value was changed via direct-access. -bool ChangedDirectly(CommandLineFlag* flag, const void* a, const void* b); + private: + // Copy-construct a new value of the flag's type in a memory referenced by + // the dst based on the current flag's value. + virtual void Read(void* dst) const = 0; +}; // This macro is the "source of truth" for the list of supported flag types we // expect to perform lock free operations on. Specifically it generates code, diff --git a/absl/flags/internal/flag.cc b/absl/flags/internal/flag.cc index 0f403581..061113d7 100644 --- a/absl/flags/internal/flag.cc +++ b/absl/flags/internal/flag.cc @@ -15,36 +15,331 @@ #include "absl/flags/internal/flag.h" +#include "absl/base/optimization.h" #include "absl/synchronization/mutex.h" namespace absl { namespace flags_internal { +namespace { -// If the flag has a mutation callback this function invokes it. While the -// callback is being invoked the primary flag's mutex is unlocked and it is -// re-locked back after call to callback is completed. Callback invocation is -// guarded by flag's secondary mutex instead which prevents concurrent -// callback invocation. Note that it is possible for other thread to grab the -// primary lock and update flag's value at any time during the callback -// invocation. This is by design. Callback can get a value of the flag if -// necessary, but it might be different from the value initiated the callback -// and it also can be different by the time the callback invocation is -// completed. Requires that *primary_lock be held in exclusive mode; it may be -// released and reacquired by the implementation. -void InvokeCallback(absl::Mutex* primary_mu, absl::Mutex* callback_mu, - FlagCallback cb) ABSL_EXCLUSIVE_LOCKS_REQUIRED(primary_mu) { - if (!cb) return; - - // When executing the callback we need the primary flag's mutex to be - // unlocked so that callback can retrieve the flag's value. - primary_mu->Unlock(); +// Currently we only validate flag values for user-defined flag types. +bool ShouldValidateFlagValue(const CommandLineFlag& flag) { +#define DONT_VALIDATE(T) \ + if (flag.IsOfType()) return false; + ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(DONT_VALIDATE) + DONT_VALIDATE(std::string) + DONT_VALIDATE(std::vector) +#undef DONT_VALIDATE + return true; +} + +} // namespace + +void FlagImpl::Init() { + ABSL_CONST_INIT static absl::Mutex init_lock(absl::kConstInit); + + { + absl::MutexLock lock(&init_lock); + + if (locks_ == nullptr) { // Must initialize Mutexes for this flag. + locks_ = new FlagImpl::CommandLineFlagLocks; + } + } + + absl::MutexLock lock(&locks_->primary_mu); + + if (def_ != nullptr) { + inited_.store(true, std::memory_order_release); + } else { + // Need to initialize def and cur fields. + def_ = (*initial_value_gen_)(); + cur_ = Clone(op_, def_); + StoreAtomic(); + inited_.store(true, std::memory_order_release); + InvokeCallback(); + } +} + +// Ensures that the lazily initialized data is initialized, +// and returns pointer to the mutex guarding flags data. +absl::Mutex* FlagImpl::DataGuard() const + ABSL_LOCK_RETURNED(locks_->primary_mu) { + if (ABSL_PREDICT_FALSE(!inited_.load(std::memory_order_acquire))) { + const_cast(this)->Init(); + } + + // All fields initialized; locks_ is therefore safe to read. + return &locks_->primary_mu; +} + +void FlagImpl::Destroy() const { { - absl::MutexLock lock(callback_mu); - cb(); + absl::MutexLock l(DataGuard()); + + // Values are heap allocated for Abseil Flags. + if (cur_) Delete(op_, cur_); + if (def_) Delete(op_, def_); } - primary_mu->Lock(); + delete locks_; +} + +bool FlagImpl::IsModified() const { + absl::MutexLock l(DataGuard()); + return modified_; +} + +bool FlagImpl::IsSpecifiedOnCommandLine() const { + absl::MutexLock l(DataGuard()); + return on_command_line_; +} + +std::string FlagImpl::DefaultValue() const { + absl::MutexLock l(DataGuard()); + + return Unparse(marshalling_op_, def_); +} + +std::string FlagImpl::CurrentValue() const { + absl::MutexLock l(DataGuard()); + + return Unparse(marshalling_op_, cur_); +} + +void FlagImpl::SetCallback( + const flags_internal::FlagCallback mutation_callback) { + absl::MutexLock l(DataGuard()); + + callback_ = mutation_callback; + + InvokeCallback(); +} + +void FlagImpl::InvokeCallback() const + ABSL_EXCLUSIVE_LOCKS_REQUIRED(locks_->primary_mu) { + if (!callback_) return; + + // If the flag has a mutation callback this function invokes it. While the + // callback is being invoked the primary flag's mutex is unlocked and it is + // re-locked back after call to callback is completed. Callback invocation is + // guarded by flag's secondary mutex instead which prevents concurrent + // callback invocation. Note that it is possible for other thread to grab the + // primary lock and update flag's value at any time during the callback + // invocation. This is by design. Callback can get a value of the flag if + // necessary, but it might be different from the value initiated the callback + // and it also can be different by the time the callback invocation is + // completed. Requires that *primary_lock be held in exclusive mode; it may be + // released and reacquired by the implementation. + DataGuard()->Unlock(); + + { + absl::MutexLock lock(&locks_->callback_mu); + callback_(); + } + + DataGuard()->Lock(); +} + +bool FlagImpl::RestoreState(const CommandLineFlag& flag, const void* value, + bool modified, bool on_command_line, + int64_t counter) { + { + absl::MutexLock l(DataGuard()); + + if (counter_ == counter) return false; + } + + Write(flag, value, op_); + + { + absl::MutexLock l(DataGuard()); + + modified_ = modified; + on_command_line_ = on_command_line; + } + + return true; +} + +// Attempts to parse supplied `value` string using parsing routine in the `flag` +// argument. If parsing successful, this function stores the parsed value in +// 'dst' assuming it is a pointer to the flag's value type. In case if any error +// is encountered in either step, the error message is stored in 'err' +bool FlagImpl::TryParse(const CommandLineFlag& flag, void* dst, + absl::string_view value, std::string* err) const + ABSL_EXCLUSIVE_LOCKS_REQUIRED(locks_->primary_mu) { + void* tentative_value = Clone(op_, def_); + std::string parse_err; + if (!Parse(marshalling_op_, value, tentative_value, &parse_err)) { + auto type_name = flag.Typename(); + absl::string_view err_sep = parse_err.empty() ? "" : "; "; + absl::string_view typename_sep = type_name.empty() ? "" : " "; + *err = absl::StrCat("Illegal value '", value, "' specified for", + typename_sep, type_name, " flag '", flag.Name(), "'", + err_sep, parse_err); + Delete(op_, tentative_value); + return false; + } + + Copy(op_, tentative_value, dst); + Delete(op_, tentative_value); + return true; +} + +void FlagImpl::Read(const CommandLineFlag& flag, void* dst, + const flags_internal::FlagOpFn dst_op) const { + absl::ReaderMutexLock l(DataGuard()); + + // `dst_op` is the unmarshaling operation corresponding to the declaration + // visibile at the call site. `op` is the Flag's defined unmarshalling + // operation. They must match for this operation to be well-defined. + if (ABSL_PREDICT_FALSE(dst_op != op_)) { + ABSL_INTERNAL_LOG( + ERROR, + absl::StrCat("Flag '", flag.Name(), + "' is defined as one type and declared as another")); + } + CopyConstruct(op_, cur_, dst); +} + +void FlagImpl::StoreAtomic() ABSL_EXCLUSIVE_LOCKS_REQUIRED(locks_->primary_mu) { + size_t data_size = Sizeof(op_); + + if (data_size <= sizeof(int64_t)) { + int64_t t = 0; + std::memcpy(&t, cur_, data_size); + atomic_.store(t, std::memory_order_release); + } +} + +void FlagImpl::Write(const CommandLineFlag& flag, const void* src, + const flags_internal::FlagOpFn src_op) { + absl::MutexLock l(DataGuard()); + + // `src_op` is the marshalling operation corresponding to the declaration + // visible at the call site. `op` is the Flag's defined marshalling operation. + // They must match for this operation to be well-defined. + if (ABSL_PREDICT_FALSE(src_op != op_)) { + ABSL_INTERNAL_LOG( + ERROR, + absl::StrCat("Flag '", flag.Name(), + "' is defined as one type and declared as another")); + } + + if (ShouldValidateFlagValue(flag)) { + void* obj = Clone(op_, src); + std::string ignored_error; + std::string src_as_str = Unparse(marshalling_op_, src); + if (!Parse(marshalling_op_, src_as_str, obj, &ignored_error)) { + ABSL_INTERNAL_LOG(ERROR, + absl::StrCat("Attempt to set flag '", flag.Name(), + "' to invalid value ", src_as_str)); + } + Delete(op_, obj); + } + + modified_ = true; + counter_++; + Copy(op_, src, cur_); + + StoreAtomic(); + InvokeCallback(); +} + +// Sets the value of the flag based on specified string `value`. If the flag +// was successfully set to new value, it returns true. Otherwise, sets `err` +// to indicate the error, leaves the flag unchanged, and returns false. There +// are three ways to set the flag's value: +// * Update the current flag value +// * Update the flag's default value +// * Update the current flag value if it was never set before +// The mode is selected based on 'set_mode' parameter. +bool FlagImpl::SetFromString(const CommandLineFlag& flag, + absl::string_view value, FlagSettingMode set_mode, + ValueSource source, std::string* err) { + absl::MutexLock l(DataGuard()); + + switch (set_mode) { + case SET_FLAGS_VALUE: { + // set or modify the flag's value + if (!TryParse(flag, cur_, value, err)) return false; + modified_ = true; + counter_++; + StoreAtomic(); + InvokeCallback(); + + if (source == kCommandLine) { + on_command_line_ = true; + } + break; + } + case SET_FLAG_IF_DEFAULT: { + // set the flag's value, but only if it hasn't been set by someone else + if (!modified_) { + if (!TryParse(flag, cur_, value, err)) return false; + modified_ = true; + counter_++; + StoreAtomic(); + InvokeCallback(); + } else { + // TODO(rogeeff): review and fix this semantic. Currently we do not fail + // in this case if flag is modified. This is misleading since the flag's + // value is not updated even though we return true. + // *err = absl::StrCat(Name(), " is already set to ", + // CurrentValue(), "\n"); + // return false; + return true; + } + break; + } + case SET_FLAGS_DEFAULT: { + // modify the flag's default-value + if (!TryParse(flag, def_, value, err)) return false; + + if (!modified_) { + // Need to set both default value *and* current, in this case + Copy(op_, def_, cur_); + StoreAtomic(); + InvokeCallback(); + } + break; + } + } + + return true; +} + +void FlagImpl::CheckDefaultValueParsingRoundtrip( + const CommandLineFlag& flag) const { + std::string v = DefaultValue(); + + absl::MutexLock lock(DataGuard()); + + void* dst = Clone(op_, def_); + std::string error; + if (!flags_internal::Parse(marshalling_op_, v, dst, &error)) { + ABSL_INTERNAL_LOG( + FATAL, + absl::StrCat("Flag ", flag.Name(), " (from ", flag.Filename(), + "): std::string form of default value '", v, + "' could not be parsed; error=", error)); + } + + // We do not compare dst to def since parsing/unparsing may make + // small changes, e.g., precision loss for floating point types. + Delete(op_, dst); +} + +bool FlagImpl::ValidateInputValue(absl::string_view value) const { + absl::MutexLock l(DataGuard()); + + void* obj = Clone(op_, def_); + std::string ignored_error; + const bool result = + flags_internal::Parse(marshalling_op_, value, obj, &ignored_error); + Delete(op_, obj); + return result; } } // namespace flags_internal diff --git a/absl/flags/internal/flag.h b/absl/flags/internal/flag.h index 2b21c440..ce0ccf2d 100644 --- a/absl/flags/internal/flag.h +++ b/absl/flags/internal/flag.h @@ -16,12 +16,15 @@ #ifndef ABSL_FLAGS_INTERNAL_FLAG_H_ #define ABSL_FLAGS_INTERNAL_FLAG_H_ +#include #include +#include "absl/base/thread_annotations.h" #include "absl/flags/internal/commandlineflag.h" #include "absl/flags/internal/registry.h" #include "absl/memory/memory.h" #include "absl/strings/str_cat.h" +#include "absl/synchronization/mutex.h" namespace absl { namespace flags_internal { @@ -66,6 +69,124 @@ using FlagCallback = void (*)(); void InvokeCallback(absl::Mutex* primary_mu, absl::Mutex* callback_mu, FlagCallback cb) ABSL_EXCLUSIVE_LOCKS_REQUIRED(primary_mu); +// The class encapsulates the Flag's data and safe access to it. +class FlagImpl { + public: + constexpr FlagImpl(const flags_internal::FlagOpFn op, + const flags_internal::FlagMarshallingOpFn marshalling_op, + const flags_internal::InitialValGenFunc initial_value_gen) + : op_(op), + marshalling_op_(marshalling_op), + initial_value_gen_(initial_value_gen) {} + + // Forces destruction of the Flag's data. + void Destroy() const; + + // Constant access methods + bool IsModified() const ABSL_LOCKS_EXCLUDED(locks_->primary_mu); + bool IsSpecifiedOnCommandLine() const ABSL_LOCKS_EXCLUDED(locks_->primary_mu); + std::string DefaultValue() const ABSL_LOCKS_EXCLUDED(locks_->primary_mu); + std::string CurrentValue() const ABSL_LOCKS_EXCLUDED(locks_->primary_mu); + void Read(const CommandLineFlag& flag, void* dst, + const flags_internal::FlagOpFn dst_op) const + ABSL_LOCKS_EXCLUDED(locks_->primary_mu); + // Attempts to parse supplied `value` std::string. + bool TryParse(const CommandLineFlag& flag, void* dst, absl::string_view value, + std::string* err) const + ABSL_EXCLUSIVE_LOCKS_REQUIRED(locks_->primary_mu); + template + bool AtomicGet(T* v) const { + const int64_t r = atomic_.load(std::memory_order_acquire); + if (r != flags_internal::AtomicInit()) { + std::memcpy(v, &r, sizeof(T)); + return true; + } + + return false; + } + + // Mutating access methods + void Write(const CommandLineFlag& flag, const void* src, + const flags_internal::FlagOpFn src_op) + ABSL_LOCKS_EXCLUDED(locks_->primary_mu); + bool SetFromString(const CommandLineFlag& flag, absl::string_view value, + FlagSettingMode set_mode, ValueSource source, + std::string* err) ABSL_LOCKS_EXCLUDED(locks_->primary_mu); + // If possible, updates copy of the Flag's value that is stored in an + // atomic word. + void StoreAtomic() ABSL_EXCLUSIVE_LOCKS_REQUIRED(locks_->primary_mu); + + // Interfaces to operate on callbacks. + void SetCallback(const flags_internal::FlagCallback mutation_callback) + ABSL_LOCKS_EXCLUDED(locks_->primary_mu); + void InvokeCallback() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(locks_->primary_mu); + + // Interfaces to save/restore mutable flag data + template + std::unique_ptr SaveState( + Flag* flag) const ABSL_LOCKS_EXCLUDED(locks_->primary_mu) { + T&& cur_value = flag->Get(); + absl::MutexLock l(DataGuard()); + + return absl::make_unique>( + flag, std::move(cur_value), modified_, on_command_line_, counter_); + } + bool RestoreState(const CommandLineFlag& flag, const void* value, + bool modified, bool on_command_line, int64_t counter) + ABSL_LOCKS_EXCLUDED(locks_->primary_mu); + + // Value validation interfaces. + void CheckDefaultValueParsingRoundtrip(const CommandLineFlag& flag) const + ABSL_LOCKS_EXCLUDED(locks_->primary_mu); + bool ValidateInputValue(absl::string_view value) const + ABSL_LOCKS_EXCLUDED(locks_->primary_mu); + + private: + // Lazy initialization of the Flag's data. + void Init(); + // Ensures that the lazily initialized data is initialized, + // and returns pointer to the mutex guarding flags data. + absl::Mutex* DataGuard() const ABSL_LOCK_RETURNED(locks_->primary_mu); + + // Immutable Flag's data. + const FlagOpFn op_; // Type-specific handler + const FlagMarshallingOpFn marshalling_op_; // Marshalling ops handler + const InitialValGenFunc initial_value_gen_; // Makes flag's initial value + + // Mutable Flag's data. (guarded by locks_->primary_mu). + // Indicates that locks_, cur_ and def_ fields have been lazily initialized. + std::atomic inited_{false}; + // Has flag value been modified? + bool modified_ ABSL_GUARDED_BY(locks_->primary_mu) = false; + // Specified on command line. + bool on_command_line_ ABSL_GUARDED_BY(locks_->primary_mu) = false; + // Lazily initialized pointer to default value + void* def_ ABSL_GUARDED_BY(locks_->primary_mu) = nullptr; + // Lazily initialized pointer to current value + void* cur_ ABSL_GUARDED_BY(locks_->primary_mu) = nullptr; + // Mutation counter + int64_t counter_ ABSL_GUARDED_BY(locks_->primary_mu) = 0; + // For some types, a copy of the current value is kept in an atomically + // accessible field. + std::atomic atomic_{flags_internal::AtomicInit()}; + // Mutation callback + FlagCallback callback_ = nullptr; + + // Lazily initialized mutexes for this flag value. We cannot inline a + // SpinLock or Mutex here because those have non-constexpr constructors and + // so would prevent constant initialization of this type. + // TODO(rogeeff): fix it once Mutex has constexpr constructor + // The following struct contains the locks in a CommandLineFlag struct. + // They are in a separate struct that is lazily allocated to avoid problems + // with static initialization and to avoid multiple allocations. + struct CommandLineFlagLocks { + absl::Mutex primary_mu; // protects several fields in CommandLineFlag + absl::Mutex callback_mu; // used to serialize callbacks + }; + + CommandLineFlagLocks* locks_ = nullptr; // locks, laziliy allocated. +}; + // This is "unspecified" implementation of absl::Flag type. template class Flag final : public flags_internal::CommandLineFlag { @@ -76,30 +197,11 @@ class Flag final : public flags_internal::CommandLineFlag { const flags_internal::InitialValGenFunc initial_value_gen) : flags_internal::CommandLineFlag( name, flags_internal::HelpText::FromFunctionPointer(help_gen), - filename, &flags_internal::FlagOps, marshalling_op, - initial_value_gen, - /*def=*/nullptr, - /*cur=*/nullptr), - atomic_(flags_internal::AtomicInit()), - callback_(nullptr) {} + filename), + impl_(&flags_internal::FlagOps, marshalling_op, initial_value_gen) {} T Get() const { - // Implementation notes: - // - // We are wrapping a union around the value of `T` to serve three purposes: - // - // 1. `U.value` has correct size and alignment for a value of type `T` - // 2. The `U.value` constructor is not invoked since U's constructor does - // not - // do it explicitly. - // 3. The `U.value` destructor is invoked since U's destructor does it - // explicitly. This makes `U` a kind of RAII wrapper around non default - // constructible value of T, which is destructed when we leave the - // scope. We do need to destroy U.value, which is constructed by - // CommandLineFlag::Read even though we left it in a moved-from state - // after std::move. - // - // All of this serves to avoid requiring `T` being default constructible. + // See implementation notes in CommandLineFlag::Get(). union U { T value; U() {} @@ -107,89 +209,70 @@ class Flag final : public flags_internal::CommandLineFlag { }; U u; - Read(&u.value, &flags_internal::FlagOps); + impl_.Read(*this, &u.value, &flags_internal::FlagOps); return std::move(u.value); } - bool AtomicGet(T* v) const { - const int64_t r = atomic_.load(std::memory_order_acquire); - if (r != flags_internal::AtomicInit()) { - std::memcpy(v, &r, sizeof(T)); - return true; - } - - return false; - } + bool AtomicGet(T* v) const { return impl_.AtomicGet(v); } - void Set(const T& v) { Write(&v, &flags_internal::FlagOps); } + void Set(const T& v) { impl_.Write(*this, &v, &flags_internal::FlagOps); } void SetCallback(const flags_internal::FlagCallback mutation_callback) { - absl::MutexLock l(InitFlagIfNecessary()); - - callback_ = mutation_callback; - - InvokeCallback(); + impl_.SetCallback(mutation_callback); } - private: - friend class FlagState; - - void Destroy() const override { - // Values are heap allocated for Abseil Flags. - if (cur_) Delete(op_, cur_); - if (def_) Delete(op_, def_); - - delete locks_; + // CommandLineFlag interface + bool IsModified() const override { return impl_.IsModified(); } + bool IsSpecifiedOnCommandLine() const override { + return impl_.IsSpecifiedOnCommandLine(); } + std::string DefaultValue() const override { return impl_.DefaultValue(); } + std::string CurrentValue() const override { return impl_.CurrentValue(); } - void StoreAtomic() override { - if (sizeof(T) <= sizeof(int64_t)) { - int64_t t = 0; - std::memcpy(&t, cur_, (std::min)(sizeof(T), sizeof(int64_t))); - atomic_.store(t, std::memory_order_release); - } + bool ValidateInputValue(absl::string_view value) const override { + return impl_.ValidateInputValue(value); } // Interfaces to save and restore flags to/from persistent state. // Returns current flag state or nullptr if flag does not support // saving and restoring a state. std::unique_ptr SaveState() override { - T curr_value = Get(); - - absl::MutexLock l(InitFlagIfNecessary()); - - return absl::make_unique>( - this, std::move(curr_value), modified_, on_command_line_, counter_); + return impl_.SaveState(this); } // Restores the flag state to the supplied state object. If there is // nothing to restore returns false. Otherwise returns true. bool RestoreState(const flags_internal::FlagState& flag_state) { - if (MutationCounter() == flag_state.counter_) return false; - - Set(flag_state.cur_value_); + return impl_.RestoreState(*this, &flag_state.cur_value_, + flag_state.modified_, flag_state.on_command_line_, + flag_state.counter_); + } - // Race condition here? This should disappear once we move the rest of the - // flag's data into Flag's internals. + bool SetFromString(absl::string_view value, + flags_internal::FlagSettingMode set_mode, + flags_internal::ValueSource source, + std::string* error) override { + return impl_.SetFromString(*this, value, set_mode, source, error); + } - absl::MutexLock l(InitFlagIfNecessary()); - modified_ = flag_state.modified_; - on_command_line_ = flag_state.on_command_line_; - return true; + void CheckDefaultValueParsingRoundtrip() const override { + impl_.CheckDefaultValueParsingRoundtrip(*this); } - // Interfaces to overate on callbacks. - void InvokeCallback() override - ABSL_EXCLUSIVE_LOCKS_REQUIRED(locks_->primary_mu) { - flags_internal::InvokeCallback(&locks_->primary_mu, &locks_->callback_mu, - callback_); + private: + friend class FlagState; + + void Destroy() const override { impl_.Destroy(); } + + void Read(void* dst) const override { + impl_.Read(*this, dst, &flags_internal::FlagOps); + } + flags_internal::FlagOpFn TypeId() const override { + return &flags_internal::FlagOps; } // Flag's data - // For some types, a copy of the current value is kept in an atomically - // accessible field. - std::atomic atomic_; - FlagCallback callback_; // Mutation callback + FlagImpl impl_; }; template diff --git a/absl/flags/internal/registry.cc b/absl/flags/internal/registry.cc index 6b2564d1..ae7671a9 100644 --- a/absl/flags/internal/registry.cc +++ b/absl/flags/internal/registry.cc @@ -118,7 +118,7 @@ void FlagRegistry::RegisterFlag(CommandLineFlag* flag) { (flag->IsRetired() ? old_flag->Filename() : flag->Filename()), "'."), true); - } else if (flag->op_ != old_flag->op_) { + } else if (flag->TypeId() != old_flag->TypeId()) { flags_internal::ReportUsageError( absl::StrCat("Flag '", flag->Name(), "' was defined more than once but with " @@ -275,38 +275,49 @@ namespace { class RetiredFlagObj final : public flags_internal::CommandLineFlag { public: - constexpr RetiredFlagObj(const char* name, FlagOpFn ops, - FlagMarshallingOpFn marshalling_ops) + constexpr RetiredFlagObj(const char* name, FlagOpFn ops) : flags_internal::CommandLineFlag( name, flags_internal::HelpText::FromStaticCString(nullptr), - /*filename=*/"RETIRED", ops, marshalling_ops, - /*initial_value_gen=*/nullptr, - /*def=*/nullptr, - /*cur=*/nullptr) {} + /*filename=*/"RETIRED"), + op_(ops) {} private: - bool IsRetired() const override { return true; } - void Destroy() const override { // Values are heap allocated for Retired Flags. - if (cur_) Delete(op_, cur_); - if (def_) Delete(op_, def_); - - if (locks_) delete locks_; - delete this; } + flags_internal::FlagOpFn TypeId() const override { return op_; } + bool IsRetired() const override { return true; } + bool IsModified() const override { return false; } + bool IsSpecifiedOnCommandLine() const override { return false; } + std::string DefaultValue() const override { return ""; } + std::string CurrentValue() const override { return ""; } + + // Any input is valid + bool ValidateInputValue(absl::string_view) const override { return true; } + std::unique_ptr SaveState() override { return nullptr; } + + bool SetFromString(absl::string_view, flags_internal::FlagSettingMode, + flags_internal::ValueSource, std::string*) override { + return false; + } + + void CheckDefaultValueParsingRoundtrip() const override {} + + void Read(void*) const override {} + + // Data members + const FlagOpFn op_; }; } // namespace -bool Retire(const char* name, FlagOpFn ops, - FlagMarshallingOpFn marshalling_ops) { - auto* flag = new flags_internal::RetiredFlagObj(name, ops, marshalling_ops); +bool Retire(const char* name, FlagOpFn ops) { + auto* flag = new flags_internal::RetiredFlagObj(name, ops); FlagRegistry::GlobalRegistry()->RegisterFlag(flag); return true; } diff --git a/absl/flags/internal/registry.h b/absl/flags/internal/registry.h index eb134a9f..1889f3a0 100644 --- a/absl/flags/internal/registry.h +++ b/absl/flags/internal/registry.h @@ -76,14 +76,12 @@ bool RegisterCommandLineFlag(CommandLineFlag*); // // Retire flag with name "name" and type indicated by ops. -bool Retire(const char* name, FlagOpFn ops, - FlagMarshallingOpFn marshalling_ops); +bool Retire(const char* name, FlagOpFn ops); // Registered a retired flag with name 'flag_name' and type 'T'. template inline bool RetiredFlag(const char* flag_name) { - return flags_internal::Retire(flag_name, flags_internal::FlagOps, - flags_internal::FlagMarshallingOps); + return flags_internal::Retire(flag_name, flags_internal::FlagOps); } // If the flag is retired, returns true and indicates in |*type_is_bool| diff --git a/absl/random/internal/iostream_state_saver_test.cc b/absl/random/internal/iostream_state_saver_test.cc index 722766d0..7bb8ad95 100644 --- a/absl/random/internal/iostream_state_saver_test.cc +++ b/absl/random/internal/iostream_state_saver_test.cc @@ -196,8 +196,8 @@ TEST(IOStreamStateSaver, RoundTripFloats) { EXPECT_EQ(-d, StreamRoundTrip(-d)); // Avoid undefined behavior (overflow/underflow). - if (d <= std::numeric_limits::max() && - d >= std::numeric_limits::lowest()) { + if (f <= static_cast(std::numeric_limits::max()) && + f >= static_cast(std::numeric_limits::lowest())) { int64_t x = static_cast(f); EXPECT_EQ(x, StreamRoundTrip(x)); } @@ -264,8 +264,8 @@ TEST(IOStreamStateSaver, RoundTripDoubles) { } // Avoid undefined behavior (overflow/underflow). - if (d <= std::numeric_limits::max() && - d >= std::numeric_limits::lowest()) { + if (d <= static_cast(std::numeric_limits::max()) && + d >= static_cast(std::numeric_limits::lowest())) { int64_t x = static_cast(d); EXPECT_EQ(x, StreamRoundTrip(x)); } diff --git a/absl/strings/internal/resize_uninitialized.h b/absl/strings/internal/resize_uninitialized.h index 469962b2..0f5e964d 100644 --- a/absl/strings/internal/resize_uninitialized.h +++ b/absl/strings/internal/resize_uninitialized.h @@ -35,8 +35,7 @@ struct ResizeUninitializedTraits { static void Resize(string_type* s, size_t new_size) { s->resize(new_size); } }; -// __resize_default_init is provided by libc++ >= 8.0 and by Google's internal -// ::string implementation. +// __resize_default_init is provided by libc++ >= 8.0 template struct ResizeUninitializedTraits< string_type, absl::void_t() diff --git a/ci/cmake_install_test.sh b/ci/cmake_install_test.sh index 03eb0432..6a3c19bd 100755 --- a/ci/cmake_install_test.sh +++ b/ci/cmake_install_test.sh @@ -28,6 +28,5 @@ time docker run \ --rm \ -e CFLAGS="-Werror" \ -e CXXFLAGS="-Werror" \ - gcr.io/google.com/absl-177019/linux_gcc-latest:20190703 \ + gcr.io/google.com/absl-177019/linux_gcc-latest:20191018 \ /bin/bash CMake/install_test_project/test.sh $@ - diff --git a/ci/linux_clang-latest_libcxx_asan_bazel.sh b/ci/linux_clang-latest_libcxx_asan_bazel.sh index cc8c695a..fa38ff20 100755 --- a/ci/linux_clang-latest_libcxx_asan_bazel.sh +++ b/ci/linux_clang-latest_libcxx_asan_bazel.sh @@ -36,7 +36,7 @@ if [ -z ${EXCEPTIONS_MODE:-} ]; then EXCEPTIONS_MODE="-fno-exceptions -fexceptions" fi -readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/linux_clang-latest:20190813" +readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/linux_clang-latest:20191018" # USE_BAZEL_CACHE=1 only works on Kokoro. # Without access to the credentials this won't work. diff --git a/ci/linux_clang-latest_libcxx_bazel.sh b/ci/linux_clang-latest_libcxx_bazel.sh index 5849a84e..c3319a1b 100755 --- a/ci/linux_clang-latest_libcxx_bazel.sh +++ b/ci/linux_clang-latest_libcxx_bazel.sh @@ -36,7 +36,7 @@ if [ -z ${EXCEPTIONS_MODE:-} ]; then EXCEPTIONS_MODE="-fno-exceptions -fexceptions" fi -readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/linux_clang-latest:20190813" +readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/linux_clang-latest:20191018" # USE_BAZEL_CACHE=1 only works on Kokoro. # Without access to the credentials this won't work. diff --git a/ci/linux_clang-latest_libcxx_tsan_bazel.sh b/ci/linux_clang-latest_libcxx_tsan_bazel.sh index 8721ada2..200541b9 100755 --- a/ci/linux_clang-latest_libcxx_tsan_bazel.sh +++ b/ci/linux_clang-latest_libcxx_tsan_bazel.sh @@ -36,7 +36,7 @@ if [ -z ${EXCEPTIONS_MODE:-} ]; then EXCEPTIONS_MODE="-fno-exceptions -fexceptions" fi -readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/linux_clang-latest:20190813" +readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/linux_clang-latest:20191018" # USE_BAZEL_CACHE=1 only works on Kokoro. # Without access to the credentials this won't work. diff --git a/ci/linux_clang-latest_libstdcxx_bazel.sh b/ci/linux_clang-latest_libstdcxx_bazel.sh index 78ce0b32..5ea18373 100755 --- a/ci/linux_clang-latest_libstdcxx_bazel.sh +++ b/ci/linux_clang-latest_libstdcxx_bazel.sh @@ -36,7 +36,7 @@ if [ -z ${EXCEPTIONS_MODE:-} ]; then EXCEPTIONS_MODE="-fno-exceptions -fexceptions" fi -readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/linux_clang-latest:20190813" +readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/linux_clang-latest:20191018" # USE_BAZEL_CACHE=1 only works on Kokoro. # Without access to the credentials this won't work. diff --git a/ci/linux_gcc-4.9_libstdcxx_bazel.sh b/ci/linux_gcc-4.9_libstdcxx_bazel.sh index c3b936bc..f8102cc0 100755 --- a/ci/linux_gcc-4.9_libstdcxx_bazel.sh +++ b/ci/linux_gcc-4.9_libstdcxx_bazel.sh @@ -36,7 +36,7 @@ if [ -z ${EXCEPTIONS_MODE:-} ]; then EXCEPTIONS_MODE="-fno-exceptions -fexceptions" fi -readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/linux_gcc-4.9:20190702" +readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/linux_gcc-4.9:20191018" # USE_BAZEL_CACHE=1 only works on Kokoro. # Without access to the credentials this won't work. diff --git a/ci/linux_gcc-latest_libstdcxx_bazel.sh b/ci/linux_gcc-latest_libstdcxx_bazel.sh index 10f6be21..a6efa197 100755 --- a/ci/linux_gcc-latest_libstdcxx_bazel.sh +++ b/ci/linux_gcc-latest_libstdcxx_bazel.sh @@ -36,7 +36,7 @@ if [ -z ${EXCEPTIONS_MODE:-} ]; then EXCEPTIONS_MODE="-fno-exceptions -fexceptions" fi -readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/linux_gcc-latest:20190703" +readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/linux_gcc-latest:20191018" # USE_BAZEL_CACHE=1 only works on Kokoro. # Without access to the credentials this won't work. diff --git a/ci/linux_gcc-latest_libstdcxx_cmake.sh b/ci/linux_gcc-latest_libstdcxx_cmake.sh index 3e831c14..ec328748 100755 --- a/ci/linux_gcc-latest_libstdcxx_cmake.sh +++ b/ci/linux_gcc-latest_libstdcxx_cmake.sh @@ -47,7 +47,7 @@ for std in ${ABSL_CMAKE_CXX_STANDARDS}; do --rm \ -e CFLAGS="-Werror" \ -e CXXFLAGS="-Werror" \ - gcr.io/google.com/absl-177019/linux_gcc-latest:20190703 \ + gcr.io/google.com/absl-177019/linux_gcc-latest:20191018 \ /bin/bash -c " cd /buildfs && \ cmake /abseil-cpp \ -- cgit v1.2.3 From 8a9ef3c5da2a9064dda0ac3c61b43b87c12c50b8 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Mon, 21 Dec 2020 04:44:34 -0800 Subject: Export of internal Abseil changes -- b95862447354428f62ae1627cf526e42ca0b7a9d by Christian Blichmann : Minor cleanups: * Sorting using declarations * Changing the format of a NOLINT statement PiperOrigin-RevId: 348448885 -- 954a4375fb09267e55dfda345605b9aca54998b0 by Abseil Team : Enable some more Emscripten tests. Requires setting -s PRINTF_LONG_DOUBLE=1 in a recent build. PiperOrigin-RevId: 348043610 GitOrigin-RevId: b95862447354428f62ae1627cf526e42ca0b7a9d Change-Id: I517c94a5fd0feb9b99823dc8552d28fa598723fe --- README.md | 2 +- absl/base/BUILD.bazel | 4 +++- absl/base/internal/low_level_alloc_test.cc | 19 +++++++++++++++++++ absl/random/exponential_distribution_test.cc | 4 ---- absl/random/gaussian_distribution_test.cc | 5 +---- absl/random/internal/iostream_state_saver_test.cc | 5 +++-- absl/random/uniform_real_distribution_test.cc | 4 ---- absl/strings/numbers.h | 8 -------- absl/strings/numbers_test.cc | 4 ++-- 9 files changed, 29 insertions(+), 26 deletions(-) (limited to 'absl/random/internal/iostream_state_saver_test.cc') diff --git a/README.md b/README.md index 95be3257..f1802349 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,7 @@ Abseil contains the following C++ library components: * [`numeric`](absl/numeric/)
The `numeric` library contains C++11-compatible 128-bit integers. * [`status`](absl/status/) -
The `status` library contains abstractions for error handling, specifically +
The `status` contains abstractions for error handling, specifically `absl::Status` and `absl::StatusOr`. * [`strings`](absl/strings/)
The `strings` library contains a variety of strings routines and diff --git a/absl/base/BUILD.bazel b/absl/base/BUILD.bazel index 244aa8cc..5d67a507 100644 --- a/absl/base/BUILD.bazel +++ b/absl/base/BUILD.bazel @@ -551,7 +551,9 @@ cc_test( srcs = ["internal/low_level_alloc_test.cc"], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, - tags = ["no_test_ios_x86_64"], + tags = [ + "no_test_ios_x86_64", + ], deps = [ ":malloc_internal", "//absl/container:node_hash_map", diff --git a/absl/base/internal/low_level_alloc_test.cc b/absl/base/internal/low_level_alloc_test.cc index 2f2eaffa..31abb888 100644 --- a/absl/base/internal/low_level_alloc_test.cc +++ b/absl/base/internal/low_level_alloc_test.cc @@ -21,6 +21,10 @@ #include #include +#ifdef __EMSCRIPTEN__ +#include +#endif + #include "absl/container/node_hash_map.h" namespace absl { @@ -158,5 +162,20 @@ ABSL_NAMESPACE_END int main(int argc, char *argv[]) { // The actual test runs in the global constructor of `before_main`. printf("PASS\n"); +#ifdef __EMSCRIPTEN__ + // clang-format off +// This is JS here. Don't try to format it. + MAIN_THREAD_EM_ASM({ + if (ENVIRONMENT_IS_WEB) { + if (typeof TEST_FINISH === 'function') { + TEST_FINISH($0); + } else { + console.error('Attempted to exit with status ' + $0); + console.error('But TEST_FINSIHED is not a function.'); + } + } + }, 0); +// clang-format on +#endif return 0; } diff --git a/absl/random/exponential_distribution_test.cc b/absl/random/exponential_distribution_test.cc index 8e9e69b6..5a8afde5 100644 --- a/absl/random/exponential_distribution_test.cc +++ b/absl/random/exponential_distribution_test.cc @@ -47,11 +47,7 @@ using absl::random_internal::kChiSquared; template class ExponentialDistributionTypedTest : public ::testing::Test {}; -#if defined(__EMSCRIPTEN__) -using RealTypes = ::testing::Types; -#else using RealTypes = ::testing::Types; -#endif // defined(__EMSCRIPTEN__) TYPED_TEST_CASE(ExponentialDistributionTypedTest, RealTypes); TYPED_TEST(ExponentialDistributionTypedTest, SerializeTest) { diff --git a/absl/random/gaussian_distribution_test.cc b/absl/random/gaussian_distribution_test.cc index 02ac578a..2aa7caf4 100644 --- a/absl/random/gaussian_distribution_test.cc +++ b/absl/random/gaussian_distribution_test.cc @@ -130,15 +130,12 @@ TYPED_TEST(GaussianDistributionInterfaceTest, SerializeTest) { ss >> after; #if defined(__powerpc64__) || defined(__PPC64__) || defined(__powerpc__) || \ - defined(__ppc__) || defined(__PPC__) || defined(__EMSCRIPTEN__) + defined(__ppc__) || defined(__PPC__) if (std::is_same::value) { // Roundtripping floating point values requires sufficient precision // to reconstruct the exact value. It turns out that long double // has some errors doing this on ppc, particularly for values // near {1.0 +/- epsilon}. - // - // Emscripten is even worse, implementing long double as a 128-bit - // type, but shipping with a strtold() that doesn't support that. if (mean <= std::numeric_limits::max() && mean >= std::numeric_limits::lowest()) { EXPECT_EQ(static_cast(before.mean()), diff --git a/absl/random/internal/iostream_state_saver_test.cc b/absl/random/internal/iostream_state_saver_test.cc index 7bb8ad95..6e66266c 100644 --- a/absl/random/internal/iostream_state_saver_test.cc +++ b/absl/random/internal/iostream_state_saver_test.cc @@ -14,6 +14,9 @@ #include "absl/random/internal/iostream_state_saver.h" +#include +#include + #include #include @@ -272,7 +275,6 @@ TEST(IOStreamStateSaver, RoundTripDoubles) { } } -#if !defined(__EMSCRIPTEN__) TEST(IOStreamStateSaver, RoundTripLongDoubles) { // Technically, C++ only guarantees that long double is at least as large as a // double. Practically it varies from 64-bits to 128-bits. @@ -350,7 +352,6 @@ TEST(IOStreamStateSaver, RoundTripLongDoubles) { } } } -#endif // !defined(__EMSCRIPTEN__) TEST(StrToDTest, DoubleMin) { const char kV[] = "2.22507385850720138e-308"; diff --git a/absl/random/uniform_real_distribution_test.cc b/absl/random/uniform_real_distribution_test.cc index be107cdd..8cf874d6 100644 --- a/absl/random/uniform_real_distribution_test.cc +++ b/absl/random/uniform_real_distribution_test.cc @@ -55,11 +55,7 @@ namespace { template class UniformRealDistributionTest : public ::testing::Test {}; -#if defined(__EMSCRIPTEN__) -using RealTypes = ::testing::Types; -#else using RealTypes = ::testing::Types; -#endif // defined(__EMSCRIPTEN__) TYPED_TEST_SUITE(UniformRealDistributionTest, RealTypes); diff --git a/absl/strings/numbers.h b/absl/strings/numbers.h index 7966e250..ffc738fa 100644 --- a/absl/strings/numbers.h +++ b/absl/strings/numbers.h @@ -1,4 +1,3 @@ -// // Copyright 2017 The Abseil Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -245,13 +244,6 @@ inline size_t FastHexToBufferZeroPad16(uint64_t val, char* out) { } // namespace numbers_internal -// SimpleAtoi() -// -// Converts a string to an integer, using `safe_strto?()` functions for actual -// parsing, returning `true` if successful. The `safe_strto?()` functions apply -// strict checking; the string must be a base-10 integer, optionally followed or -// preceded by ASCII whitespace, with a value in the range of the corresponding -// integer type. template ABSL_MUST_USE_RESULT bool SimpleAtoi(absl::string_view str, int_type* out) { return numbers_internal::safe_strtoi_base(str, out, 10); diff --git a/absl/strings/numbers_test.cc b/absl/strings/numbers_test.cc index 4ab67fb6..27616bf8 100644 --- a/absl/strings/numbers_test.cc +++ b/absl/strings/numbers_test.cc @@ -46,6 +46,7 @@ namespace { +using absl::SimpleAtoi; using absl::numbers_internal::kSixDigitsToBufferSize; using absl::numbers_internal::safe_strto32_base; using absl::numbers_internal::safe_strto64_base; @@ -55,7 +56,6 @@ using absl::numbers_internal::SixDigitsToBuffer; using absl::strings_internal::Itoa; using absl::strings_internal::strtouint32_test_cases; using absl::strings_internal::strtouint64_test_cases; -using absl::SimpleAtoi; using testing::Eq; using testing::MatchesRegex; @@ -380,7 +380,7 @@ TEST(NumbersTest, Atoi) { VerifySimpleAtoiGood(42, 42); VerifySimpleAtoiGood(42, 42); VerifySimpleAtoiGood(-42, -42); - VerifySimpleAtoiGood(-42, -42); // NOLINT(runtime/int) + VerifySimpleAtoiGood(-42, -42); // NOLINT: runtime-int VerifySimpleAtoiGood(42, 42); VerifySimpleAtoiGood(42, 42); VerifySimpleAtoiGood(42, 42); -- cgit v1.2.3