diff options
-rw-r--r-- | absl/base/BUILD.bazel | 1 | ||||
-rw-r--r-- | absl/base/CMakeLists.txt | 1 | ||||
-rw-r--r-- | absl/base/call_once_test.cc | 4 | ||||
-rw-r--r-- | absl/base/const_init.h | 73 | ||||
-rw-r--r-- | absl/container/BUILD.bazel | 2 | ||||
-rw-r--r-- | absl/container/CMakeLists.txt | 2 | ||||
-rw-r--r-- | absl/container/internal/compressed_tuple.h | 28 | ||||
-rw-r--r-- | absl/container/internal/compressed_tuple_test.cc | 43 | ||||
-rw-r--r-- | absl/numeric/int128.h | 21 | ||||
-rw-r--r-- | absl/numeric/int128_have_intrinsic.inc | 2 | ||||
-rw-r--r-- | absl/numeric/int128_no_intrinsic.inc | 2 | ||||
-rw-r--r-- | absl/strings/internal/pow10_helper.h | 6 | ||||
-rw-r--r-- | absl/synchronization/CMakeLists.txt | 268 | ||||
-rw-r--r-- | absl/synchronization/internal/mutex_nonprod.inc | 3 | ||||
-rw-r--r-- | absl/synchronization/lifetime_test.cc | 48 | ||||
-rw-r--r-- | absl/synchronization/mutex.h | 22 | ||||
-rw-r--r-- | absl/time/CMakeLists.txt | 161 |
17 files changed, 484 insertions, 203 deletions
diff --git a/absl/base/BUILD.bazel b/absl/base/BUILD.bazel index 1c31211722c2..2717df0c93a1 100644 --- a/absl/base/BUILD.bazel +++ b/absl/base/BUILD.bazel @@ -67,6 +67,7 @@ cc_library( name = "core_headers", hdrs = [ "attributes.h", + "const_init.h", "macros.h", "optimization.h", "port.h", diff --git a/absl/base/CMakeLists.txt b/absl/base/CMakeLists.txt index 212dd0836284..5178d2bd6eb8 100644 --- a/absl/base/CMakeLists.txt +++ b/absl/base/CMakeLists.txt @@ -60,6 +60,7 @@ absl_cc_library( core_headers HDRS "attributes.h" + "const_init.h" "macros.h" "optimization.h" "port.h" diff --git a/absl/base/call_once_test.cc b/absl/base/call_once_test.cc index aa7eb95db262..183b92efa329 100644 --- a/absl/base/call_once_test.cc +++ b/absl/base/call_once_test.cc @@ -19,6 +19,7 @@ #include "gtest/gtest.h" #include "absl/base/attributes.h" +#include "absl/base/const_init.h" #include "absl/base/thread_annotations.h" #include "absl/synchronization/mutex.h" @@ -26,7 +27,8 @@ namespace absl { namespace { absl::once_flag once; -Mutex counters_mu; + +ABSL_CONST_INIT Mutex counters_mu(absl::kConstInit); int running_thread_count GUARDED_BY(counters_mu) = 0; int call_once_invoke_count GUARDED_BY(counters_mu) = 0; diff --git a/absl/base/const_init.h b/absl/base/const_init.h new file mode 100644 index 000000000000..fc88b2672e23 --- /dev/null +++ b/absl/base/const_init.h @@ -0,0 +1,73 @@ +// 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 +// +// http://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. +// +// ----------------------------------------------------------------------------- +// kConstInit +// ----------------------------------------------------------------------------- +// +// A constructor tag used to mark an object as safe for use as a global +// variable, avoiding the usual lifetime issues that can affect globals. + +#ifndef ABSL_BASE_CONST_INIT_H_ +#define ABSL_BASE_CONST_INIT_H_ + +// In general, objects with static storage duration (such as global variables) +// can trigger tricky object lifetime situations. Attempting to access them +// from the constructors or destructors of other global objects can result in +// undefined behavior, unless their constructors and destructors are designed +// with this issue in mind. +// +// The normal way to deal with this issue in C++11 is to use constant +// initialization and trivial destructors. +// +// Constant initialization is guaranteed to occur before any other code +// executes. Constructors that are declared 'constexpr' are eligible for +// constant initialization. You can annotate a variable declaration with the +// ABSL_CONST_INIT macro to express this intent. For compilers that support +// it, this annotation will cause a compilation error for declarations that +// aren't subject to constant initialization (perhaps because a runtime value +// was passed as a constructor argument). +// +// On program shutdown, lifetime issues can be avoided on global objects by +// ensuring that they contain trivial destructors. A class has a trivial +// destructor unless it has a user-defined destructor, a virtual method or base +// class, or a data member or base class with a non-trivial destructor of its +// own. Objects with static storage duration and a trivial destructor are not +// cleaned up on program shutdown, and are thus safe to access from other code +// running during shutdown. +// +// For a few core Abseil classes, we make a best effort to allow for safe global +// instances, even though these classes have non-trivial destructors. These +// objects can be created with the absl::kConstInit tag. For example: +// ABSL_CONST_INIT absl::Mutex global_mutex(absl::kConstInit); +// +// The line above declares a global variable of type absl::Mutex which can be +// accessed at any point during startup or shutdown. global_mutex's destructor +// will still run, but will not invalidate the object. Note that C++ specifies +// that accessing an object after its destructor has run results in undefined +// behavior, but this pattern works on the toolchains we support. +// +// The absl::kConstInit tag should only be used to define objects with static +// or thread_local storage duration. +// + +namespace absl { + +enum ConstInitType { + kConstInit, +}; + +} // namespace absl + +#endif // ABSL_BASE_CONST_INIT_H_ diff --git a/absl/container/BUILD.bazel b/absl/container/BUILD.bazel index d0789923c34e..66f7c9565ba2 100644 --- a/absl/container/BUILD.bazel +++ b/absl/container/BUILD.bazel @@ -41,6 +41,8 @@ cc_test( copts = ABSL_TEST_COPTS, deps = [ ":compressed_tuple", + "//absl/memory", + "//absl/utility", "@com_google_googletest//:gtest_main", ], ) diff --git a/absl/container/CMakeLists.txt b/absl/container/CMakeLists.txt index 8605facc2e2d..3c2735ffcf2e 100644 --- a/absl/container/CMakeLists.txt +++ b/absl/container/CMakeLists.txt @@ -44,6 +44,8 @@ absl_cc_test( "internal/compressed_tuple_test.cc" DEPS absl::compressed_tuple + absl::memory + absl::utility gmock_main ) diff --git a/absl/container/internal/compressed_tuple.h b/absl/container/internal/compressed_tuple.h index cc52614f5b37..b883ae2657ed 100644 --- a/absl/container/internal/compressed_tuple.h +++ b/absl/container/internal/compressed_tuple.h @@ -89,8 +89,10 @@ struct Storage { T value; constexpr Storage() = default; explicit constexpr Storage(T&& v) : value(absl::forward<T>(v)) {} - constexpr const T& get() const { return value; } - T& get() { return value; } + constexpr const T& get() const& { return value; } + T& get() & { return value; } + constexpr const T&& get() const&& { return absl::move(*this).value; } + T&& get() && { return std::move(*this).value; } }; template <typename D, size_t I> @@ -99,8 +101,10 @@ struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC Storage<D, I, true> using T = internal_compressed_tuple::ElemT<D, I>; constexpr Storage() = default; explicit constexpr Storage(T&& v) : T(absl::forward<T>(v)) {} - constexpr const T& get() const { return *this; } - T& get() { return *this; } + constexpr const T& get() const& { return *this; } + T& get() & { return *this; } + constexpr const T&& get() const&& { return absl::move(*this); } + T&& get() && { return std::move(*this); } }; template <typename D, typename I> @@ -152,14 +156,26 @@ class ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTuple : CompressedTuple::CompressedTupleImpl(absl::forward<Ts>(base)...) {} template <int I> - ElemT<I>& get() { + ElemT<I>& get() & { return internal_compressed_tuple::Storage<CompressedTuple, I>::get(); } template <int I> - constexpr const ElemT<I>& get() const { + constexpr const ElemT<I>& get() const& { return internal_compressed_tuple::Storage<CompressedTuple, I>::get(); } + + template <int I> + ElemT<I>&& get() && { + return std::move(*this) + .internal_compressed_tuple::template Storage<CompressedTuple, I>::get(); + } + + template <int I> + constexpr const ElemT<I>&& get() const&& { + return absl::move(*this) + .internal_compressed_tuple::template Storage<CompressedTuple, I>::get(); + } }; // Explicit specialization for a zero-element tuple diff --git a/absl/container/internal/compressed_tuple_test.cc b/absl/container/internal/compressed_tuple_test.cc index 45030c675ee1..04ead100aa13 100644 --- a/absl/container/internal/compressed_tuple_test.cc +++ b/absl/container/internal/compressed_tuple_test.cc @@ -14,17 +14,25 @@ #include "absl/container/internal/compressed_tuple.h" +#include <memory> #include <string> #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "absl/memory/memory.h" +#include "absl/utility/utility.h" namespace absl { namespace container_internal { namespace { +enum class CallType { kConstRef, kConstMove }; + template <int> -struct Empty {}; +struct Empty { + constexpr CallType value() const& { return CallType::kConstRef; } + constexpr CallType value() const&& { return CallType::kConstMove; } +}; template <typename T> struct NotEmpty { @@ -140,15 +148,44 @@ TEST(CompressedTupleTest, NoElements) { EXPECT_TRUE(std::is_empty<CompressedTuple<>>::value); } +TEST(CompressedTupleTest, MoveOnlyElements) { + CompressedTuple<std::unique_ptr<std::string>> str_tup( + absl::make_unique<std::string>("str")); + + CompressedTuple<CompressedTuple<std::unique_ptr<std::string>>, + std::unique_ptr<int>> + x(std::move(str_tup), absl::make_unique<int>(5)); + + EXPECT_EQ(*x.get<0>().get<0>(), "str"); + EXPECT_EQ(*x.get<1>(), 5); + + std::unique_ptr<std::string> x0 = std::move(x.get<0>()).get<0>(); + std::unique_ptr<int> x1 = std::move(x).get<1>(); + + EXPECT_EQ(*x0, "str"); + EXPECT_EQ(*x1, 5); +} + TEST(CompressedTupleTest, Constexpr) { - constexpr CompressedTuple<int, double, CompressedTuple<int>> x( - 7, 1.25, CompressedTuple<int>(5)); + constexpr CompressedTuple<int, double, CompressedTuple<int>, Empty<0>> x( + 7, 1.25, CompressedTuple<int>(5), {}); constexpr int x0 = x.get<0>(); constexpr double x1 = x.get<1>(); constexpr int x2 = x.get<2>().get<0>(); + constexpr CallType x3 = x.get<3>().value(); + EXPECT_EQ(x0, 7); EXPECT_EQ(x1, 1.25); EXPECT_EQ(x2, 5); + EXPECT_EQ(x3, CallType::kConstRef); + +#if defined(__clang__) + // An apparent bug in earlier versions of gcc claims these are ambiguous. + constexpr int x2m = absl::move(x.get<2>()).get<0>(); + constexpr CallType x3m = absl::move(x).get<3>().value(); + EXPECT_EQ(x2m, 5); + EXPECT_EQ(x3m, CallType::kConstMove); +#endif } #if defined(__clang__) || defined(__GNUC__) diff --git a/absl/numeric/int128.h b/absl/numeric/int128.h index f9c83cafec40..9c36c57116c6 100644 --- a/absl/numeric/int128.h +++ b/absl/numeric/int128.h @@ -37,10 +37,19 @@ #include "absl/base/macros.h" #include "absl/base/port.h" -#if defined(_MSC_VER) && defined(_WIN64) +#if defined(_MSC_VER) +// In very old versions of MSVC and when the /Zc:wchar_t flag is off, wchar_t is +// a typedef for unsigned short. Otherwise wchar_t is mapped to the __wchar_t +// builtin type. We need to make sure not to define operator wchar_t() +// alongside operator unsigned short() in these instances. +#define ABSL_INTERNAL_WCHAR_T __wchar_t +#if defined(_WIN64) #include <intrin.h> #pragma intrinsic(_umul128) -#endif // defined(_MSC_VER) && defined(_WIN64) +#endif // defined(_WIN64) +#else // defined(_MSC_VER) +#define ABSL_INTERNAL_WCHAR_T wchar_t +#endif // defined(_MSC_VER) namespace absl { @@ -131,7 +140,7 @@ class constexpr explicit operator unsigned char() const; constexpr explicit operator char16_t() const; constexpr explicit operator char32_t() const; - constexpr explicit operator wchar_t() const; + constexpr explicit operator ABSL_INTERNAL_WCHAR_T() const; constexpr explicit operator short() const; // NOLINT(runtime/int) // NOLINTNEXTLINE(runtime/int) constexpr explicit operator unsigned short() const; @@ -468,8 +477,8 @@ constexpr uint128::operator char32_t() const { return static_cast<char32_t>(lo_); } -constexpr uint128::operator wchar_t() const { - return static_cast<wchar_t>(lo_); +constexpr uint128::operator ABSL_INTERNAL_WCHAR_T() const { + return static_cast<ABSL_INTERNAL_WCHAR_T>(lo_); } // NOLINTNEXTLINE(runtime/int) @@ -719,4 +728,6 @@ inline uint128& uint128::operator--() { } // namespace absl +#undef ABSL_INTERNAL_WCHAR_T + #endif // ABSL_NUMERIC_INT128_H_ diff --git a/absl/numeric/int128_have_intrinsic.inc b/absl/numeric/int128_have_intrinsic.inc index ee2a093018d9..0c8164a5444e 100644 --- a/absl/numeric/int128_have_intrinsic.inc +++ b/absl/numeric/int128_have_intrinsic.inc @@ -15,4 +15,4 @@ // This file contains :int128 implementation details that depend on internal // representation when ABSL_HAVE_INTRINSIC_INT128 is defined. This file is -// included by int128.h. +// included by int128.h and relies on ABSL_INTERNAL_WCHAR_T being defined. diff --git a/absl/numeric/int128_no_intrinsic.inc b/absl/numeric/int128_no_intrinsic.inc index 0d0b3cfdeb12..08d68ac3ea58 100644 --- a/absl/numeric/int128_no_intrinsic.inc +++ b/absl/numeric/int128_no_intrinsic.inc @@ -15,4 +15,4 @@ // This file contains :int128 implementation details that depend on internal // representation when ABSL_HAVE_INTRINSIC_INT128 is *not* defined. This file -// is included by int128.h. +// is included by int128.h and relies on ABSL_INTERNAL_WCHAR_T being defined. diff --git a/absl/strings/internal/pow10_helper.h b/absl/strings/internal/pow10_helper.h index 39c153928de2..fe7e735a30bb 100644 --- a/absl/strings/internal/pow10_helper.h +++ b/absl/strings/internal/pow10_helper.h @@ -17,8 +17,8 @@ // precise values are computed across the full range of doubles. We can't rely // on the pow() function, because not all standard libraries ship a version // that is precise. -#ifndef ABSL_STRINGS_POW10_HELPER_H_ -#define ABSL_STRINGS_POW10_HELPER_H_ +#ifndef ABSL_STRINGS_INTERNAL_POW10_HELPER_H_ +#define ABSL_STRINGS_INTERNAL_POW10_HELPER_H_ #include <vector> @@ -33,4 +33,4 @@ double Pow10(int exp); } // namespace strings_internal } // namespace absl -#endif // ABSL_STRINGS_POW10_HELPER_H_ +#endif // ABSL_STRINGS_INTERNAL_POW10_HELPER_H_ diff --git a/absl/synchronization/CMakeLists.txt b/absl/synchronization/CMakeLists.txt index de0d7b7d4500..cb77b685647f 100644 --- a/absl/synchronization/CMakeLists.txt +++ b/absl/synchronization/CMakeLists.txt @@ -14,142 +14,182 @@ # limitations under the License. # -list(APPEND SYNCHRONIZATION_PUBLIC_HEADERS - "barrier.h" - "blocking_counter.h" - "mutex.h" - "notification.h" +absl_cc_library( + NAME + graphcycles_internal + HDRS + "internal/graphcycles.h" + SRCS + "internal/graphcycles.cc" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::base + absl::base_internal + absl::core_headers + absl::malloc_internal ) - -list(APPEND SYNCHRONIZATION_INTERNAL_HEADERS - "internal/create_thread_identity.h" - "internal/graphcycles.h" - "internal/kernel_timeout.h" - "internal/per_thread_sem.h" - "internal/thread_pool.h" - "internal/waiter.h" -) - - - -# synchronization library -list(APPEND SYNCHRONIZATION_SRC - "barrier.cc" - "blocking_counter.cc" - "internal/create_thread_identity.cc" - "internal/per_thread_sem.cc" - "internal/waiter.cc" - "internal/graphcycles.cc" - "notification.cc" - "mutex.cc" -) - -set(SYNCHRONIZATION_PUBLIC_LIBRARIES absl::base absl::stacktrace absl::symbolize absl::time) - -absl_library( - TARGET - absl_synchronization - SOURCES - ${SYNCHRONIZATION_SRC} - PUBLIC_LIBRARIES - ${SYNCHRONIZATION_PUBLIC_LIBRARIES} - EXPORT_NAME +absl_cc_library( + NAME synchronization + HDRS + "barrier.h" + "blocking_counter.h" + "internal/create_thread_identity.h" + "internal/kernel_timeout.h" + "internal/mutex_nonprod.inc" + "internal/per_thread_sem.h" + "internal/waiter.h" + "mutex.h" + "notification.h" + SRCS + "barrier.cc" + "blocking_counter.cc" + "internal/create_thread_identity.cc" + "internal/per_thread_sem.cc" + "internal/waiter.cc" + "notification.cc" + "mutex.cc" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::graphcycles_internal + absl::base + absl::base_internal + absl::config + absl::core_headers + absl::dynamic_annotations + absl::malloc_internal + absl::stacktrace + absl::symbolize + absl::time + PUBLIC ) - -# -## TESTS -# - - -# test barrier_test -set(BARRIER_TEST_SRC "barrier_test.cc") -set(BARRIER_TEST_PUBLIC_LIBRARIES absl::synchronization) - -absl_test( - TARGET +absl_cc_test( + NAME barrier_test - SOURCES - ${BARRIER_TEST_SRC} - PUBLIC_LIBRARIES - ${BARRIER_TEST_PUBLIC_LIBRARIES} + SRCS + "barrier_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::synchronization + absl::time + gmock_main ) - -# test blocking_counter_test -set(BLOCKING_COUNTER_TEST_SRC "blocking_counter_test.cc") -set(BLOCKING_COUNTER_TEST_PUBLIC_LIBRARIES absl::synchronization) - -absl_test( - TARGET +absl_cc_test( + NAME blocking_counter_test - SOURCES - ${BLOCKING_COUNTER_TEST_SRC} - PUBLIC_LIBRARIES - ${BLOCKING_COUNTER_TEST_PUBLIC_LIBRARIES} + SRCS + "blocking_counter_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::synchronization + absl::time + gmock_main ) - -# test graphcycles_test -set(GRAPHCYCLES_TEST_SRC "internal/graphcycles_test.cc") -set(GRAPHCYCLES_TEST_PUBLIC_LIBRARIES absl::synchronization) - -absl_test( - TARGET +absl_cc_test( + NAME graphcycles_test - SOURCES - ${GRAPHCYCLES_TEST_SRC} - PUBLIC_LIBRARIES - ${GRAPHCYCLES_TEST_PUBLIC_LIBRARIES} + SRCS + "internal/graphcycles_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::graphcycles_internal + absl::base + absl::core_headers + gmock_main ) +absl_cc_library( + NAME + thread_pool + HDRS + "internal/thread_pool.h" + DEPS + absl::synchronization + absl::core_headers + TESTONLY +) -# test mutex_test -set(MUTEX_TEST_SRC "mutex_test.cc") -set(MUTEX_TEST_PUBLIC_LIBRARIES absl::synchronization) - -absl_test( - TARGET +absl_cc_test( + NAME mutex_test - SOURCES - ${MUTEX_TEST_SRC} - PUBLIC_LIBRARIES - ${MUTEX_TEST_PUBLIC_LIBRARIES} + SRCS + "mutex_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::synchronization + absl::thread_pool + absl::base + absl::core_headers + absl::memory + absl::time + gmock_main ) - -# test notification_test -set(NOTIFICATION_TEST_SRC "notification_test.cc") -set(NOTIFICATION_TEST_PUBLIC_LIBRARIES absl::synchronization) - -absl_test( - TARGET +absl_cc_test( + NAME notification_test - SOURCES - ${NOTIFICATION_TEST_SRC} - PUBLIC_LIBRARIES - ${NOTIFICATION_TEST_PUBLIC_LIBRARIES} + SRCS + "notification_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::synchronization + absl::time + gmock_main ) - -# test per_thread_sem_test_common -set(PER_THREAD_SEM_TEST_COMMON_SRC "internal/per_thread_sem_test.cc") -set(PER_THREAD_SEM_TEST_COMMON_PUBLIC_LIBRARIES absl::synchronization absl::strings) - -absl_test( - TARGET +absl_cc_library( + NAME per_thread_sem_test_common - SOURCES - ${PER_THREAD_SEM_TEST_COMMON_SRC} - PUBLIC_LIBRARIES - ${PER_THREAD_SEM_TEST_COMMON_PUBLIC_LIBRARIES} + SRCS + "internal/per_thread_sem_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::synchronization + absl::base + absl::strings + absl::time + gmock + TESTONLY ) +absl_cc_test( + NAME + per_thread_sem_test + SRCS + "internal/per_thread_sem_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::per_thread_sem_test_common + absl::synchronization + absl::base + absl::strings + absl::time + gmock_main +) - - - - - +absl_cc_test( + NAME + lifetime_test + SRCS + "lifetime_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::synchronization + absl::base + absl::core_headers + Threads::Threads +) diff --git a/absl/synchronization/internal/mutex_nonprod.inc b/absl/synchronization/internal/mutex_nonprod.inc index 0aab3d1314e6..b8d5af79ad38 100644 --- a/absl/synchronization/internal/mutex_nonprod.inc +++ b/absl/synchronization/internal/mutex_nonprod.inc @@ -214,6 +214,9 @@ class SynchronizationStorage { // stack) should use this constructor. explicit SynchronizationStorage(base_internal::LinkerInitialized) {} + constexpr explicit SynchronizationStorage(absl::ConstInitType) + : is_dynamic_(false), once_(), space_{{0}} {} + SynchronizationStorage(SynchronizationStorage&) = delete; SynchronizationStorage& operator=(SynchronizationStorage&) = delete; diff --git a/absl/synchronization/lifetime_test.cc b/absl/synchronization/lifetime_test.cc index b7360c29016b..8b168e21a316 100644 --- a/absl/synchronization/lifetime_test.cc +++ b/absl/synchronization/lifetime_test.cc @@ -17,6 +17,7 @@ #include <type_traits> #include "absl/base/attributes.h" +#include "absl/base/const_init.h" #include "absl/base/internal/raw_logging.h" #include "absl/base/thread_annotations.h" #include "absl/synchronization/mutex.h" @@ -95,6 +96,10 @@ void TestLocals() { RunTests(&mutex, &condvar); } +// Normal kConstInit usage +ABSL_CONST_INIT absl::Mutex const_init_mutex(absl::kConstInit); +void TestConstInitGlobal() { RunTests(&const_init_mutex, nullptr); } + // Global variables during start and termination // // In a translation unit, static storage duration variables are initialized in @@ -117,10 +122,53 @@ class OnDestruction { Function fn_; }; +// kConstInit +// Test early usage. (Declaration comes first; definitions must appear after +// the test runner.) +extern absl::Mutex early_const_init_mutex; +// (Normally I'd write this +[], to make the cast-to-function-pointer explicit, +// but in some MSVC setups we support, lambdas provide conversion operators to +// different flavors of function pointers, making this trick ambiguous.) +OnConstruction test_early_const_init([] { + RunTests(&early_const_init_mutex, nullptr); +}); +// This definition appears before test_early_const_init, but it should be +// initialized first (due to constant initialization). Test that the object +// actually works when constructed this way. +ABSL_CONST_INIT absl::Mutex early_const_init_mutex(absl::kConstInit); + +// Furthermore, test that the const-init c'tor doesn't stomp over the state of +// a Mutex. Really, this is a test that the platform under test correctly +// supports C++11 constant initialization. (The constant-initialization +// constructors of globals "happen at link time"; memory is pre-initialized, +// before the constructors of either grab_lock or check_still_locked are run.) +extern absl::Mutex const_init_sanity_mutex; +OnConstruction grab_lock([]() NO_THREAD_SAFETY_ANALYSIS { + const_init_sanity_mutex.Lock(); +}); +ABSL_CONST_INIT absl::Mutex const_init_sanity_mutex(absl::kConstInit); +OnConstruction check_still_locked([]() NO_THREAD_SAFETY_ANALYSIS { + const_init_sanity_mutex.AssertHeld(); + const_init_sanity_mutex.Unlock(); +}); + +// Test shutdown usage. (Declarations come first; definitions must appear after +// the test runner.) +extern absl::Mutex late_const_init_mutex; +// OnDestruction is being used here as a global variable, even though it has a +// non-trivial destructor. This is against the style guide. We're violating +// that rule here to check that the exception we allow for kConstInit is safe. +// NOLINTNEXTLINE +OnDestruction test_late_const_init([] { + RunTests(&late_const_init_mutex, nullptr); +}); +ABSL_CONST_INIT absl::Mutex late_const_init_mutex(absl::kConstInit); + } // namespace int main() { TestLocals(); + TestConstInitGlobal(); // Explicitly call exit(0) here, to make it clear that we intend for the // above global object destructors to run. std::exit(0); diff --git a/absl/synchronization/mutex.h b/absl/synchronization/mutex.h index aeef3c95978c..4b65e92cb3d0 100644 --- a/absl/synchronization/mutex.h +++ b/absl/synchronization/mutex.h @@ -61,6 +61,7 @@ #include <cstdint> #include <string> +#include "absl/base/const_init.h" #include "absl/base/internal/identity.h" #include "absl/base/internal/low_level_alloc.h" #include "absl/base/internal/thread_identity.h" @@ -136,7 +137,26 @@ struct SynchWaitParams; class LOCKABLE Mutex { public: + // Creates a `Mutex` that is not held by anyone. This constructor is + // typically used for Mutexes allocated on the heap or the stack. + // + // To create `Mutex` instances with static storage duration + // (e.g. a namespace-scoped or global variable), see + // `Mutex::Mutex(absl::kConstInit)` below instead. Mutex(); + + // Creates a mutex with static storage duration. A global variable + // constructed this way avoids the lifetime issues that can occur on program + // startup and shutdown. (See absl/base/const_init.h.) + // + // For Mutexes allocated on the heap and stack, instead use the default + // constructor, which can interact more fully with the thread sanitizer. + // + // Example usage: + // namespace foo { + // ABSL_CONST_INIT Mutex mu(absl::kConstInit); + // } + explicit constexpr Mutex(absl::ConstInitType); ~Mutex(); // Mutex::Lock() @@ -879,10 +899,12 @@ class SCOPED_LOCKABLE ReleasableMutexLock { }; #ifdef ABSL_INTERNAL_USE_NONPROD_MUTEX +inline constexpr Mutex::Mutex(absl::ConstInitType) : impl_(absl::kConstInit) {} #else inline Mutex::Mutex() : mu_(0) { ABSL_TSAN_MUTEX_CREATE(this, __tsan_mutex_not_static); } +inline constexpr Mutex::Mutex(absl::ConstInitType) : mu_(0) {} inline CondVar::CondVar() : cv_(0) {} #endif diff --git a/absl/time/CMakeLists.txt b/absl/time/CMakeLists.txt index 53216cda055b..db60e712c940 100644 --- a/absl/time/CMakeLists.txt +++ b/absl/time/CMakeLists.txt @@ -14,85 +14,108 @@ # limitations under the License. # -list(APPEND TIME_PUBLIC_HEADERS - "civil_time.h" - "clock.h" - "time.h" -) - - -list(APPEND TIME_INTERNAL_HEADERS - "internal/test_util.h" - "internal/cctz/include/cctz/civil_time.h" - "internal/cctz/include/cctz/civil_time_detail.h" - "internal/cctz/include/cctz/time_zone.h" - "internal/cctz/include/cctz/zone_info_source.h" -) - -list(APPEND TIME_SRC +absl_cc_library( + NAME + time + HDRS + "civil_time.h" + "clock.h" + "time.h" + SRCS "civil_time.cc" - "time.cc" "clock.cc" "duration.cc" "format.cc" - "internal/cctz/src/civil_time_detail.cc" - "internal/cctz/src/time_zone_fixed.cc" - "internal/cctz/src/time_zone_fixed.h" - "internal/cctz/src/time_zone_format.cc" - "internal/cctz/src/time_zone_if.cc" - "internal/cctz/src/time_zone_if.h" - "internal/cctz/src/time_zone_impl.cc" - "internal/cctz/src/time_zone_impl.h" - "internal/cctz/src/time_zone_info.cc" - "internal/cctz/src/time_zone_info.h" - "internal/cctz/src/time_zone_libc.cc" - "internal/cctz/src/time_zone_libc.h" - "internal/cctz/src/time_zone_lookup.cc" - "internal/cctz/src/time_zone_posix.cc" - "internal/cctz/src/time_zone_posix.h" - "internal/cctz/src/tzfile.h" - "internal/cctz/src/zone_info_source.cc" - ${TIME_PUBLIC_HEADERS} - ${TIME_INTERNAL_HEADERS} + "internal/get_current_time_chrono.inc" + "internal/get_current_time_posix.inc" + "time.cc" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::base + absl::core_headers + absl::int128 + absl::strings + absl::civil_time + absl::time_zone + PUBLIC ) -set(TIME_PUBLIC_LIBRARIES absl::base absl::stacktrace absl::int128 absl::strings) -absl_library( - TARGET - absl_time - SOURCES - ${TIME_SRC} - PUBLIC_LIBRARIES - ${TIME_PUBLIC_LIBRARIES} - EXPORT_NAME - time +absl_cc_library( + NAME + civil_time + HDRS + "internal/cctz/include/cctz/civil_time.h" + "internal/cctz/include/cctz/civil_time_detail.h" + SRCS + "internal/cctz/src/civil_time_detail.cc" + COPTS + ${ABSL_DEFAULT_COPTS} ) +absl_cc_library( + NAME + time_zone + HDRS + "internal/cctz/include/cctz/time_zone.h" + "internal/cctz/include/cctz/zone_info_source.h" + SRCS + "internal/cctz/src/time_zone_fixed.cc" + "internal/cctz/src/time_zone_fixed.h" + "internal/cctz/src/time_zone_format.cc" + "internal/cctz/src/time_zone_if.cc" + "internal/cctz/src/time_zone_if.h" + "internal/cctz/src/time_zone_impl.cc" + "internal/cctz/src/time_zone_impl.h" + "internal/cctz/src/time_zone_info.cc" + "internal/cctz/src/time_zone_info.h" + "internal/cctz/src/time_zone_libc.cc" + "internal/cctz/src/time_zone_libc.h" + "internal/cctz/src/time_zone_lookup.cc" + "internal/cctz/src/time_zone_posix.cc" + "internal/cctz/src/time_zone_posix.h" + "internal/cctz/src/tzfile.h" + "internal/cctz/src/zone_info_source.cc" + COPTS + ${ABSL_DEFAULT_COPTS} +) - -# -## TESTS -# - -# test time_test -list(APPEND TIME_TEST_SRC - "civil_time_test.cc" - "time_test.cc" - "clock_test.cc" - "duration_test.cc" - "format_test.cc" - "time_test.cc" - "time_zone_test.cc" - "internal/test_util.cc" +absl_cc_library( + NAME + test_util + HDRS + "internal/test_util.h" + SRCS + "internal/test_util.cc" + "internal/zoneinfo.inc" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::time + absl::base + absl::time_zone + gmock + TESTONLY ) -set(TIME_TEST_PUBLIC_LIBRARIES absl::time) -absl_test( - TARGET +absl_cc_test( + NAME time_test - SOURCES - ${TIME_TEST_SRC} - PUBLIC_LIBRARIES - ${TIME_TEST_PUBLIC_LIBRARIES} + SRCS + "civil_time_test.cc" + "clock_test.cc" + "duration_test.cc" + "format_test.cc" + "time_test.cc" + "time_zone_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::test_util + absl::time + absl::base + absl::config + absl::core_headers + absl::time_zone + gmock_main ) - |