about summary refs log tree commit diff
path: root/absl/base
diff options
context:
space:
mode:
Diffstat (limited to 'absl/base')
-rw-r--r--absl/base/CMakeLists.txt96
-rw-r--r--absl/base/casts.h2
-rw-r--r--absl/base/exception_safety_testing_test.cc12
-rw-r--r--absl/base/internal/endian.h25
-rw-r--r--absl/base/internal/exception_safety_testing.cc4
-rw-r--r--absl/base/internal/exception_safety_testing.h277
-rw-r--r--absl/base/macros.h10
-rw-r--r--absl/base/thread_annotations.h12
8 files changed, 224 insertions, 214 deletions
diff --git a/absl/base/CMakeLists.txt b/absl/base/CMakeLists.txt
index 04a6eb31955b..202003e60e60 100644
--- a/absl/base/CMakeLists.txt
+++ b/absl/base/CMakeLists.txt
@@ -78,54 +78,44 @@ absl_library(
     ${BASE_SRC}
   PUBLIC_LIBRARIES
     absl_dynamic_annotations
-    absl_spinlock_wait
+    absl_internal_spinlock_wait
   EXPORT_NAME
     base
 )
 
-# throw delegate library
-set(THROW_DELEGATE_SRC "internal/throw_delegate.cc")
-
-absl_library(
-  TARGET
-    absl_throw_delegate
-  SOURCES
-    ${THROW_DELEGATE_SRC}
-  PUBLIC_LIBRARIES
-    ${THROW_DELEGATE_PUBLIC_LIBRARIES}
-  PRIVATE_COMPILE_FLAGS
-    ${ABSL_EXCEPTIONS_FLAG}
-  EXPORT_NAME
+absl_cc_library(
+  NAME
     throw_delegate
+  SRCS
+    "internal/throw_delegate.cc"
+  HDRS
+    "internal/throw_delegate.h"
+  COPTS
+    ${ABSL_EXCEPTIONS_FLAG}
+  DEPS
+    absl::base
 )
 
-if(BUILD_TESTING)
-  # exception-safety testing library
-  set(EXCEPTION_SAFETY_TESTING_SRC
+
+# exception-safety testing library
+absl_cc_library(
+  NAME
+    exception_safety_testing
+  HDRS
     "internal/exception_safety_testing.h"
+  SRCS
     "internal/exception_safety_testing.cc"
-  )
-  set(EXCEPTION_SAFETY_TESTING_PUBLIC_LIBRARIES
-    ${ABSL_TEST_COMMON_LIBRARIES}
+  COPTS
+    ${ABSL_EXCEPTIONS_FLAG}
+  DEPS
     absl::base
     absl::memory
     absl::meta
     absl::strings
     absl::optional
     gtest
-  )
-
-absl_library(
-  TARGET
-    absl_base_internal_exception_safety_testing
-  SOURCES
-    ${EXCEPTION_SAFETY_TESTING_SRC}
-  PUBLIC_LIBRARIES
-    ${EXCEPTION_SAFETY_TESTING_PUBLIC_LIBRARIES}
-  PRIVATE_COMPILE_FLAGS
-    ${ABSL_EXCEPTIONS_FLAG}
+  TESTONLY
 )
-endif()
 
 
 # dynamic_annotations library
@@ -138,29 +128,25 @@ absl_library(
     ${DYNAMIC_ANNOTATIONS_SRC}
 )
 
-
-# spinlock_wait library
-set(SPINLOCK_WAIT_SRC "internal/spinlock_wait.cc")
-
-absl_library(
-  TARGET
-    absl_spinlock_wait
-  SOURCES
-    ${SPINLOCK_WAIT_SRC}
-)
-
-
-# malloc_internal library
-list(APPEND MALLOC_INTERNAL_SRC
-  "internal/low_level_alloc.cc"
+absl_cc_library(
+  NAME
+    spinlock_wait
+  SRCS
+    "internal/spinlock_wait.cc"
+  HDRS
+    "internal/scheduling_mode.h"
+    "internal/spinlock_wait.h"
 )
 
-absl_library(
-  TARGET
-    absl_malloc_internal
-  SOURCES
-    ${MALLOC_INTERNAL_SRC}
-  PUBLIC_LIBRARIES
+absl_cc_library(
+  NAME
+    malloc_internal
+  SRCS
+    "internal/low_level_alloc.cc"
+  HDRS
+    "internal/direct_mmap.h"
+    "internal/low_level_alloc.h"
+  DEPS
     absl_dynamic_annotations
 )
 
@@ -211,7 +197,7 @@ absl_test(
 
 # test absl_throw_delegate_test
 set(THROW_DELEGATE_TEST_SRC "throw_delegate_test.cc")
-set(THROW_DELEGATE_TEST_PUBLIC_LIBRARIES absl::base absl_throw_delegate)
+set(THROW_DELEGATE_TEST_PUBLIC_LIBRARIES absl::base absl_internal_throw_delegate)
 
 absl_test(
   TARGET
@@ -368,7 +354,7 @@ absl_test(
 set(EXCEPTION_SAFETY_TESTING_TEST_SRC "exception_safety_testing_test.cc")
 set(EXCEPTION_SAFETY_TESTING_TEST_PUBLIC_LIBRARIES
   absl::base
-  absl_base_internal_exception_safety_testing
+  absl_internal_exception_safety_testing
   absl::memory
   absl::meta
   absl::strings
diff --git a/absl/base/casts.h b/absl/base/casts.h
index 20fd34da7010..1eef6a616d6b 100644
--- a/absl/base/casts.h
+++ b/absl/base/casts.h
@@ -105,7 +105,7 @@ struct is_bitcastable
 //
 // Such implicit cast chaining may be useful within template logic.
 template <typename To>
-inline To implicit_cast(typename absl::internal::identity_t<To> to) {
+constexpr To implicit_cast(typename absl::internal::identity_t<To> to) {
   return to;
 }
 
diff --git a/absl/base/exception_safety_testing_test.cc b/absl/base/exception_safety_testing_test.cc
index 106bc34b00f7..7518264d2ec5 100644
--- a/absl/base/exception_safety_testing_test.cc
+++ b/absl/base/exception_safety_testing_test.cc
@@ -770,6 +770,18 @@ TEST(ExceptionCheckTest, ModifyingChecker) {
                   .Test(invoker));
 }
 
+TEST(ExceptionSafetyTesterTest, ResetsCountdown) {
+  auto test =
+      testing::MakeExceptionSafetyTester()
+          .WithInitialValue(ThrowingValue<>())
+          .WithContracts([](ThrowingValue<>*) { return AssertionSuccess(); })
+          .WithOperation([](ThrowingValue<>*) {});
+  ASSERT_TRUE(test.Test());
+  // If the countdown isn't reset because there were no exceptions thrown, then
+  // this will fail with a termination from an unhandled exception
+  EXPECT_TRUE(test.Test());
+}
+
 struct NonCopyable : public NonNegative {
   NonCopyable(const NonCopyable&) = delete;
   NonCopyable() : NonNegative{0} {}
diff --git a/absl/base/internal/endian.h b/absl/base/internal/endian.h
index edc10f10a5aa..d5dc51adb56a 100644
--- a/absl/base/internal/endian.h
+++ b/absl/base/internal/endian.h
@@ -82,14 +82,14 @@ inline uint64_t gbswap_64(uint64_t host_int) {
 #elif defined(__GLIBC__)
   return bswap_64(host_int);
 #else
-  return (((x & uint64_t{(0xFF}) << 56) |
-          ((x & uint64_t{(0xFF00}) << 40) |
-          ((x & uint64_t{(0xFF0000}) << 24) |
-          ((x & uint64_t{(0xFF000000}) << 8) |
-          ((x & uint64_t{(0xFF00000000}) >> 8) |
-          ((x & uint64_t{(0xFF0000000000}) >> 24) |
-          ((x & uint64_t{(0xFF000000000000}) >> 40) |
-          ((x & uint64_t{(0xFF00000000000000}) >> 56));
+  return (((host_int & uint64_t{0xFF}) << 56) |
+          ((host_int & uint64_t{0xFF00}) << 40) |
+          ((host_int & uint64_t{0xFF0000}) << 24) |
+          ((host_int & uint64_t{0xFF000000}) << 8) |
+          ((host_int & uint64_t{0xFF00000000}) >> 8) |
+          ((host_int & uint64_t{0xFF0000000000}) >> 24) |
+          ((host_int & uint64_t{0xFF000000000000}) >> 40) |
+          ((host_int & uint64_t{0xFF00000000000000}) >> 56));
 #endif  // bswap_64
 }
 
@@ -97,8 +97,10 @@ inline uint32_t gbswap_32(uint32_t host_int) {
 #if defined(__GLIBC__)
   return bswap_32(host_int);
 #else
-  return (((x & 0xFF) << 24) | ((x & 0xFF00) << 8) | ((x & 0xFF0000) >> 8) |
-          ((x & 0xFF000000) >> 24));
+  return (((host_int & uint32_t{0xFF}) << 24) |
+          ((host_int & uint32_t{0xFF00}) << 8) |
+          ((host_int & uint32_t{0xFF0000}) >> 8) |
+          ((host_int & uint32_t{0xFF000000}) >> 24));
 #endif
 }
 
@@ -106,7 +108,8 @@ inline uint16_t gbswap_16(uint16_t host_int) {
 #if defined(__GLIBC__)
   return bswap_16(host_int);
 #else
-  return uint16_t{((x & 0xFF) << 8) | ((x & 0xFF00) >> 8)};
+  return (((host_int & uint16_t{0xFF}) << 8) |
+          ((host_int & uint16_t{0xFF00}) >> 8));
 #endif
 }
 
diff --git a/absl/base/internal/exception_safety_testing.cc b/absl/base/internal/exception_safety_testing.cc
index f1d081f7e50d..8207b7d7b9ac 100644
--- a/absl/base/internal/exception_safety_testing.cc
+++ b/absl/base/internal/exception_safety_testing.cc
@@ -23,6 +23,10 @@ exceptions_internal::NoThrowTag nothrow_ctor;
 
 exceptions_internal::StrongGuaranteeTagType strong_guarantee;
 
+exceptions_internal::ExceptionSafetyTestBuilder<> MakeExceptionSafetyTester() {
+  return {};
+}
+
 namespace exceptions_internal {
 
 int countdown = -1;
diff --git a/absl/base/internal/exception_safety_testing.h b/absl/base/internal/exception_safety_testing.h
index 5665a1b67db2..0ecc4177d2d5 100644
--- a/absl/base/internal/exception_safety_testing.h
+++ b/absl/base/internal/exception_safety_testing.h
@@ -127,10 +127,8 @@ class ConstructorTracker {
       void* address = it.first;
       TrackedAddress& tracked_address = it.second;
       if (tracked_address.is_alive) {
-        ADD_FAILURE() << "Object at address " << address
-                      << " with countdown of " << countdown_
-                      << " was not destroyed [" << tracked_address.description
-                      << "]";
+        ADD_FAILURE() << ErrorMessage(address, tracked_address.description,
+                                      countdown_, "Object was not destroyed.");
       }
     }
   }
@@ -141,11 +139,11 @@ class ConstructorTracker {
     TrackedAddress& tracked_address =
         current_tracker_instance_->address_map_[address];
     if (tracked_address.is_alive) {
-      ADD_FAILURE() << "Object at address " << address << " with countdown of "
-                    << current_tracker_instance_->countdown_
-                    << " was re-constructed. Previously: ["
-                    << tracked_address.description << "] Now: [" << description
-                    << "]";
+      ADD_FAILURE() << ErrorMessage(
+          address, tracked_address.description,
+          current_tracker_instance_->countdown_,
+          "Object was re-constructed. Current object was constructed by " +
+              description);
     }
     tracked_address = {true, std::move(description)};
   }
@@ -159,10 +157,9 @@ class ConstructorTracker {
 
     TrackedAddress& tracked_address = it->second;
     if (!tracked_address.is_alive) {
-      ADD_FAILURE() << "Object at address " << address << " with countdown of "
-                    << current_tracker_instance_->countdown_
-                    << " was re-destroyed or created prior to construction "
-                    << "tracking [" << tracked_address.description << "]";
+      ADD_FAILURE() << ErrorMessage(address, tracked_address.description,
+                                    current_tracker_instance_->countdown_,
+                                    "Object was re-destroyed.");
     }
     tracked_address.is_alive = false;
   }
@@ -172,6 +169,16 @@ class ConstructorTracker {
     return current_tracker_instance_ != nullptr;
   }
 
+  static std::string ErrorMessage(void* address, const std::string& address_description,
+                             int countdown, const std::string& error_description) {
+    return absl::Substitute(
+        "With coundtown at $0:\n"
+        "  $1\n"
+        "  Object originally constructed by $2\n"
+        "  Object address: $3\n",
+        countdown, error_description, address_description, address);
+  }
+
   std::unordered_map<void*, TrackedAddress> address_map_;
   int countdown_;
 
@@ -190,70 +197,6 @@ class TrackedObject {
 
   ~TrackedObject() noexcept { ConstructorTracker::ObjectDestructed(this); }
 };
-
-template <typename Factory, typename Operation, typename Contract>
-absl::optional<testing::AssertionResult> TestSingleContractAtCountdownImpl(
-    const Factory& factory, const Operation& operation, int count,
-    const Contract& contract) {
-  auto t_ptr = factory();
-  absl::optional<testing::AssertionResult> current_res;
-  SetCountdown(count);
-  try {
-    operation(t_ptr.get());
-  } catch (const exceptions_internal::TestException& e) {
-    current_res.emplace(contract(t_ptr.get()));
-    if (!current_res.value()) {
-      *current_res << e.what() << " failed contract check";
-    }
-  }
-  UnsetCountdown();
-  return current_res;
-}
-
-template <typename Factory, typename Operation>
-absl::optional<testing::AssertionResult> TestSingleContractAtCountdownImpl(
-    const Factory& factory, const Operation& operation, int count,
-    StrongGuaranteeTagType) {
-  using TPtr = typename decltype(factory())::pointer;
-  auto t_is_strong = [&](TPtr t) { return *t == *factory(); };
-  return TestSingleContractAtCountdownImpl(factory, operation, count,
-                                           t_is_strong);
-}
-
-template <typename Factory, typename Operation, typename Contract>
-int TestSingleContractAtCountdown(
-    const Factory& factory, const Operation& operation, int count,
-    const Contract& contract,
-    absl::optional<testing::AssertionResult>* reduced_res) {
-  // If reduced_res is empty, it means the current call to
-  // TestSingleContractAtCountdown(...) is the first test being run so we do
-  // want to run it. Alternatively, if it's not empty (meaning a previous test
-  // has run) we want to check if it passed. If the previous test did pass, we
-  // want to contine running tests so we do want to run the current one. If it
-  // failed, we want to short circuit so as not to overwrite the AssertionResult
-  // output. If that's the case, we do not run the current test and instead we
-  // simply return.
-  if (!reduced_res->has_value() || reduced_res->value()) {
-    *reduced_res =
-        TestSingleContractAtCountdownImpl(factory, operation, count, contract);
-  }
-  return 0;
-}
-
-template <typename Factory, typename Operation, typename... Contracts>
-inline absl::optional<testing::AssertionResult> TestAllContractsAtCountdown(
-    const Factory& factory, const Operation& operation, int count,
-    const Contracts&... contracts) {
-  absl::optional<testing::AssertionResult> reduced_res;
-
-  // Run each checker, short circuiting after the first failure
-  int dummy[] = {
-      0, (TestSingleContractAtCountdown(factory, operation, count, contracts,
-                                        &reduced_res))...};
-  static_cast<void>(dummy);
-  return reduced_res;
-}
-
 }  // namespace exceptions_internal
 
 extern exceptions_internal::NoThrowTag nothrow_ctor;
@@ -773,7 +716,7 @@ class ThrowingAllocator : private exceptions_internal::TrackedObject {
   }
 
   size_type max_size() const noexcept {
-    return std::numeric_limits<difference_type>::max() / sizeof(value_type);
+    return (std::numeric_limits<difference_type>::max)() / sizeof(value_type);
   }
 
   ThrowingAllocator select_on_container_copy_construction() noexcept(
@@ -871,7 +814,7 @@ testing::AssertionResult TestNothrowOp(const Operation& operation) {
 
 namespace exceptions_internal {
 
-// Dummy struct for ExceptionSafetyTester<> partial state.
+// Dummy struct for ExceptionSafetyTestBuilder<> partial state.
 struct UninitializedT {};
 
 template <typename T>
@@ -893,20 +836,97 @@ using EnableIfTestable = typename absl::enable_if_t<
 
 template <typename Factory = UninitializedT,
           typename Operation = UninitializedT, typename... Contracts>
-class ExceptionSafetyTester;
+class ExceptionSafetyTestBuilder;
 
 }  // namespace exceptions_internal
 
-exceptions_internal::ExceptionSafetyTester<> MakeExceptionSafetyTester();
+/*
+ * Constructs an empty ExceptionSafetyTestBuilder. All
+ * ExceptionSafetyTestBuilder objects are immutable and all With[thing] mutation
+ * methods return new instances of ExceptionSafetyTestBuilder.
+ *
+ * In order to test a T for exception safety, a factory for that T, a testable
+ * operation, and at least one contract callback returning an assertion
+ * result must be applied using the respective methods.
+ */
+exceptions_internal::ExceptionSafetyTestBuilder<> MakeExceptionSafetyTester();
 
 namespace exceptions_internal {
+template <typename T>
+struct IsUniquePtr : std::false_type {};
+
+template <typename T, typename D>
+struct IsUniquePtr<std::unique_ptr<T, D>> : std::true_type {};
+
+template <typename Factory>
+struct FactoryPtrTypeHelper {
+  using type = decltype(std::declval<const Factory&>()());
+
+  static_assert(IsUniquePtr<type>::value, "Factories must return a unique_ptr");
+};
+
+template <typename Factory>
+using FactoryPtrType = typename FactoryPtrTypeHelper<Factory>::type;
+
+template <typename Factory>
+using FactoryElementType = typename FactoryPtrType<Factory>::element_type;
+
+template <typename T>
+class ExceptionSafetyTest {
+  using Factory = std::function<std::unique_ptr<T>()>;
+  using Operation = std::function<void(T*)>;
+  using Contract = std::function<AssertionResult(T*)>;
+
+ public:
+  template <typename... Contracts>
+  explicit ExceptionSafetyTest(const Factory& f, const Operation& op,
+                               const Contracts&... contracts)
+      : factory_(f), operation_(op), contracts_{WrapContract(contracts)...} {}
+
+  AssertionResult Test() const {
+    for (int count = 0;; ++count) {
+      exceptions_internal::ConstructorTracker ct(count);
+
+      for (const auto& contract : contracts_) {
+        auto t_ptr = factory_();
+        try {
+          SetCountdown(count);
+          operation_(t_ptr.get());
+          // Unset for the case that the operation throws no exceptions, which
+          // would leave the countdown set and break the *next* exception safety
+          // test after this one.
+          UnsetCountdown();
+          return AssertionSuccess();
+        } catch (const exceptions_internal::TestException& e) {
+          if (!contract(t_ptr.get())) {
+            return AssertionFailure() << e.what() << " failed contract check";
+          }
+        }
+      }
+    }
+  }
+
+ private:
+  template <typename ContractFn>
+  Contract WrapContract(const ContractFn& contract) {
+    return [contract](T* t_ptr) { return AssertionResult(contract(t_ptr)); };
+  }
+
+  Contract WrapContract(StrongGuaranteeTagType) {
+    return [this](T* t_ptr) { return AssertionResult(*factory_() == *t_ptr); };
+  }
+
+  Factory factory_;
+  Operation operation_;
+  std::vector<Contract> contracts_;
+};
 
 /*
  * Builds a tester object that tests if performing a operation on a T follows
  * exception safety guarantees. Verification is done via contract assertion
  * callbacks applied to T instances post-throw.
  *
- * Template parameters for ExceptionSafetyTester:
+ * Template parameters for ExceptionSafetyTestBuilder:
  *
  * - Factory: The factory object (passed in via tester.WithFactory(...) or
  *   tester.WithInitialValue(...)) must be invocable with the signature
@@ -933,13 +953,13 @@ namespace exceptions_internal {
  *   please.
  */
 template <typename Factory, typename Operation, typename... Contracts>
-class ExceptionSafetyTester {
+class ExceptionSafetyTestBuilder {
  public:
   /*
-   * Returns a new ExceptionSafetyTester with an included T factory based on the
-   * provided T instance. The existing factory will not be included in the newly
-   * created tester instance. The created factory returns a new T instance by
-   * copy-constructing the provided const T& t.
+   * Returns a new ExceptionSafetyTestBuilder with an included T factory based
+   * on the provided T instance. The existing factory will not be included in
+   * the newly created tester instance. The created factory returns a new T
+   * instance by copy-constructing the provided const T& t.
    *
    * Preconditions for tester.WithInitialValue(const T& t):
    *
@@ -948,41 +968,41 @@ class ExceptionSafetyTester {
    *   tester.WithFactory(...).
    */
   template <typename T>
-  ExceptionSafetyTester<DefaultFactory<T>, Operation, Contracts...>
+  ExceptionSafetyTestBuilder<DefaultFactory<T>, Operation, Contracts...>
   WithInitialValue(const T& t) const {
     return WithFactory(DefaultFactory<T>(t));
   }
 
   /*
-   * Returns a new ExceptionSafetyTester with the provided T factory included.
-   * The existing factory will not be included in the newly-created tester
-   * instance. This method is intended for use with types lacking a copy
+   * Returns a new ExceptionSafetyTestBuilder with the provided T factory
+   * included. The existing factory will not be included in the newly-created
+   * tester instance. This method is intended for use with types lacking a copy
    * constructor. Types that can be copy-constructed should instead use the
    * method tester.WithInitialValue(...).
    */
   template <typename NewFactory>
-  ExceptionSafetyTester<absl::decay_t<NewFactory>, Operation, Contracts...>
+  ExceptionSafetyTestBuilder<absl::decay_t<NewFactory>, Operation, Contracts...>
   WithFactory(const NewFactory& new_factory) const {
     return {new_factory, operation_, contracts_};
   }
 
   /*
-   * Returns a new ExceptionSafetyTester with the provided testable operation
-   * included. The existing operation will not be included in the newly created
-   * tester.
+   * Returns a new ExceptionSafetyTestBuilder with the provided testable
+   * operation included. The existing operation will not be included in the
+   * newly created tester.
    */
   template <typename NewOperation>
-  ExceptionSafetyTester<Factory, absl::decay_t<NewOperation>, Contracts...>
+  ExceptionSafetyTestBuilder<Factory, absl::decay_t<NewOperation>, Contracts...>
   WithOperation(const NewOperation& new_operation) const {
     return {factory_, new_operation, contracts_};
   }
 
   /*
-   * Returns a new ExceptionSafetyTester with the provided MoreContracts...
+   * Returns a new ExceptionSafetyTestBuilder with the provided MoreContracts...
    * combined with the Contracts... that were already included in the instance
    * on which the method was called. Contracts... cannot be removed or replaced
-   * once added to an ExceptionSafetyTester instance. A fresh object must be
-   * created in order to get an empty Contracts... list.
+   * once added to an ExceptionSafetyTestBuilder instance. A fresh object must
+   * be created in order to get an empty Contracts... list.
    *
    * In addition to passing in custom contract assertion callbacks, this method
    * accepts `testing::strong_guarantee` as an argument which checks T instances
@@ -991,8 +1011,8 @@ class ExceptionSafetyTester {
    * properly rolled back.
    */
   template <typename... MoreContracts>
-  ExceptionSafetyTester<Factory, Operation, Contracts...,
-                        absl::decay_t<MoreContracts>...>
+  ExceptionSafetyTestBuilder<Factory, Operation, Contracts...,
+                             absl::decay_t<MoreContracts>...>
   WithContracts(const MoreContracts&... more_contracts) const {
     return {
         factory_, operation_,
@@ -1039,48 +1059,27 @@ class ExceptionSafetyTester {
       typename LazyOperation = Operation,
       typename = EnableIfTestable<sizeof...(Contracts), Factory, LazyOperation>>
   testing::AssertionResult Test() const {
-    return TestImpl(operation_, absl::index_sequence_for<Contracts...>());
+    return Test(operation_);
   }
 
  private:
   template <typename, typename, typename...>
-  friend class ExceptionSafetyTester;
+  friend class ExceptionSafetyTestBuilder;
 
-  friend ExceptionSafetyTester<> testing::MakeExceptionSafetyTester();
+  friend ExceptionSafetyTestBuilder<> testing::MakeExceptionSafetyTester();
 
-  ExceptionSafetyTester() {}
+  ExceptionSafetyTestBuilder() {}
 
-  ExceptionSafetyTester(const Factory& f, const Operation& o,
-                        const std::tuple<Contracts...>& i)
+  ExceptionSafetyTestBuilder(const Factory& f, const Operation& o,
+                             const std::tuple<Contracts...>& i)
       : factory_(f), operation_(o), contracts_(i) {}
 
   template <typename SelectedOperation, size_t... Indices>
-  testing::AssertionResult TestImpl(const SelectedOperation& selected_operation,
+  testing::AssertionResult TestImpl(SelectedOperation selected_operation,
                                     absl::index_sequence<Indices...>) const {
-    // Starting from 0 and counting upwards until one of the exit conditions is
-    // hit...
-    for (int count = 0;; ++count) {
-      exceptions_internal::ConstructorTracker ct(count);
-
-      // Run the full exception safety test algorithm for the current countdown
-      auto reduced_res =
-          TestAllContractsAtCountdown(factory_, selected_operation, count,
-                                      std::get<Indices>(contracts_)...);
-      // If there is no value in the optional, no contracts were run because no
-      // exception was thrown. This means that the test is complete and the loop
-      // can exit successfully.
-      if (!reduced_res.has_value()) {
-        return testing::AssertionSuccess();
-      }
-      // If the optional is not empty and the value is falsy, an contract check
-      // failed so the test must exit to propegate the failure.
-      if (!reduced_res.value()) {
-        return reduced_res.value();
-      }
-      // If the optional is not empty and the value is not falsy, it means
-      // exceptions were thrown but the contracts passed so the test must
-      // continue to run.
-    }
+    return ExceptionSafetyTest<FactoryElementType<Factory>>(
+               factory_, selected_operation, std::get<Indices>(contracts_)...)
+        .Test();
   }
 
   Factory factory_;
@@ -1090,20 +1089,6 @@ class ExceptionSafetyTester {
 
 }  // namespace exceptions_internal
 
-/*
- * Constructs an empty ExceptionSafetyTester. All ExceptionSafetyTester
- * objects are immutable and all With[thing] mutation methods return new
- * instances of ExceptionSafetyTester.
- *
- * In order to test a T for exception safety, a factory for that T, a testable
- * operation, and at least one contract callback returning an assertion
- * result must be applied using the respective methods.
- */
-inline exceptions_internal::ExceptionSafetyTester<>
-MakeExceptionSafetyTester() {
-  return {};
-}
-
 }  // namespace testing
 
 #endif  // ABSL_BASE_INTERNAL_EXCEPTION_SAFETY_TESTING_H_
diff --git a/absl/base/macros.h b/absl/base/macros.h
index ca3d5edb6536..9e7ab375eed8 100644
--- a/absl/base/macros.h
+++ b/absl/base/macros.h
@@ -199,4 +199,14 @@ enum LinkerInitialized {
                              : [] { assert(false && #expr); }())  // NOLINT
 #endif
 
+#ifdef ABSL_HAVE_EXCEPTIONS
+#define ABSL_INTERNAL_TRY try
+#define ABSL_INTERNAL_CATCH_ANY catch (...)
+#define ABSL_INTERNAL_RETHROW do { throw; } while (false)
+#else  // ABSL_HAVE_EXCEPTIONS
+#define ABSL_INTERNAL_TRY if (true)
+#define ABSL_INTERNAL_CATCH_ANY else if (false)
+#define ABSL_INTERNAL_RETHROW do {} while (false)
+#endif  // ABSL_HAVE_EXCEPTIONS
+
 #endif  // ABSL_BASE_MACROS_H_
diff --git a/absl/base/thread_annotations.h b/absl/base/thread_annotations.h
index fbb2797b825f..2241ace4a478 100644
--- a/absl/base/thread_annotations.h
+++ b/absl/base/thread_annotations.h
@@ -108,13 +108,23 @@
 // The mutex is expected to be held both on entry to, and exit from, the
 // function.
 //
+// An exclusive lock allows read-write access to the guarded data member(s), and
+// only one thread can acquire a lock exclusively at any one time. A shared lock
+// allows read-only access, and any number of threads can acquire a shared lock
+// concurrently.
+//
+// Generally, non-const methods should be annotated with
+// EXCLUSIVE_LOCKS_REQUIRED, while const methods should be annotated with
+// SHARED_LOCKS_REQUIRED.
+//
 // Example:
 //
 //   Mutex mu1, mu2;
 //   int a GUARDED_BY(mu1);
 //   int b GUARDED_BY(mu2);
 //
-//   void foo() EXCLUSIVE_LOCKS_REQUIRED(mu1, mu2) { ... };
+//   void foo() EXCLUSIVE_LOCKS_REQUIRED(mu1, mu2) { ... }
+//   void bar() const SHARED_LOCKS_REQUIRED(mu1, mu2) { ... }
 #define EXCLUSIVE_LOCKS_REQUIRED(...) \
   THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(__VA_ARGS__))