about summary refs log tree commit diff
path: root/absl/base/internal/exception_safety_testing.h
diff options
context:
space:
mode:
Diffstat (limited to 'absl/base/internal/exception_safety_testing.h')
-rw-r--r--absl/base/internal/exception_safety_testing.h82
1 files changed, 49 insertions, 33 deletions
diff --git a/absl/base/internal/exception_safety_testing.h b/absl/base/internal/exception_safety_testing.h
index 48a292b3e1af..c014fb30fc95 100644
--- a/absl/base/internal/exception_safety_testing.h
+++ b/absl/base/internal/exception_safety_testing.h
@@ -37,8 +37,6 @@
 
 namespace absl {
 
-struct ConstructorTracker;
-
 // A configuration enum for Throwing*.  Operations whose flags are set will
 // throw, everything else won't.  This isn't meant to be exhaustive, more flags
 // can always be made in the future.
@@ -105,6 +103,8 @@ void MaybeThrow(absl::string_view msg, bool throw_bad_alloc = false);
 testing::AssertionResult FailureMessage(const TestException& e,
                                         int countdown) noexcept;
 
+class ConstructorTracker;
+
 class TrackedObject {
  public:
   TrackedObject(const TrackedObject&) = delete;
@@ -112,26 +112,56 @@ class TrackedObject {
 
  protected:
   explicit TrackedObject(const char* child_ctor) {
-    if (!GetAllocs().emplace(this, child_ctor).second) {
+    if (!GetInstanceMap().emplace(this, child_ctor).second) {
       ADD_FAILURE() << "Object at address " << static_cast<void*>(this)
                     << " re-constructed in ctor " << child_ctor;
     }
   }
 
-  static std::unordered_map<TrackedObject*, absl::string_view>& GetAllocs() {
-    static auto* m =
-        new std::unordered_map<TrackedObject*, absl::string_view>();
-    return *m;
-  }
-
   ~TrackedObject() noexcept {
-    if (GetAllocs().erase(this) == 0) {
+    if (GetInstanceMap().erase(this) == 0) {
       ADD_FAILURE() << "Object at address " << static_cast<void*>(this)
                     << " destroyed improperly";
     }
   }
 
-  friend struct ::absl::ConstructorTracker;
+ private:
+  using InstanceMap = std::unordered_map<TrackedObject*, absl::string_view>;
+  static InstanceMap& GetInstanceMap() {
+    static auto* instance_map = new InstanceMap();
+    return *instance_map;
+  }
+
+  friend class ConstructorTracker;
+};
+
+// Inspects the constructions and destructions of anything inheriting from
+// TrackedObject. This allows us to safely "leak" TrackedObjects, as
+// ConstructorTracker will destroy everything left over in its destructor.
+class ConstructorTracker {
+ public:
+  explicit ConstructorTracker(int c)
+      : init_count_(c), init_instances_(TrackedObject::GetInstanceMap()) {}
+  ~ConstructorTracker() {
+    auto& cur_instances = TrackedObject::GetInstanceMap();
+    for (auto it = cur_instances.begin(); it != cur_instances.end();) {
+      if (init_instances_.count(it->first) == 0) {
+        ADD_FAILURE() << "Object at address " << static_cast<void*>(it->first)
+                      << " constructed from " << it->second
+                      << " where the exception countdown was set to "
+                      << init_count_ << " was not destroyed";
+        // Erasing an item inside an unordered_map invalidates the existing
+        // iterator. A new one is returned for iteration to continue.
+        it = cur_instances.erase(it);
+      } else {
+        ++it;
+      }
+    }
+  }
+
+ private:
+  int init_count_;
+  TrackedObject::InstanceMap init_instances_;
 };
 
 template <typename Factory, typename Operation, typename Invariant>
@@ -707,37 +737,21 @@ class ThrowingAllocator : private exceptions_internal::TrackedObject {
 template <typename T, NoThrow Throws>
 int ThrowingAllocator<T, Throws>::next_id_ = 0;
 
-// Inspects the constructions and destructions of anything inheriting from
-// TrackedObject.  Place this as a member variable in a test fixture to ensure
-// that every ThrowingValue was constructed and destroyed correctly.  This also
-// allows us to safely "leak" TrackedObjects, as ConstructorTracker will destroy
-// everything left over in its destructor.
-struct ConstructorTracker {
-  ConstructorTracker() = default;
-  ~ConstructorTracker() {
-    auto& allocs = exceptions_internal::TrackedObject::GetAllocs();
-    for (const auto& kv : allocs) {
-      ADD_FAILURE() << "Object at address " << static_cast<void*>(kv.first)
-                    << " constructed from " << kv.second << " not destroyed";
-    }
-    allocs.clear();
-  }
-};
-
 // Tests for resource leaks by attempting to construct a T using args repeatedly
 // until successful, using the countdown method.  Side effects can then be
-// tested for resource leaks.  If a ConstructorTracker is present in the test
-// fixture, then this will also test that memory resources are not leaked as
-// long as T allocates TrackedObjects.
+// tested for resource leaks.
 template <typename T, typename... Args>
-T TestThrowingCtor(Args&&... args) {
+void TestThrowingCtor(Args&&... args) {
   struct Cleanup {
     ~Cleanup() { exceptions_internal::UnsetCountdown(); }
   } c;
   for (int count = 0;; ++count) {
+    exceptions_internal::ConstructorTracker ct(count);
     exceptions_internal::SetCountdown(count);
     try {
-      return T(std::forward<Args>(args)...);
+      T temp(std::forward<Args>(args)...);
+      static_cast<void>(temp);
+      break;
     } catch (const exceptions_internal::TestException&) {
     }
   }
@@ -934,6 +948,8 @@ class ExceptionSafetyTester {
     // 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 =
           TestAllInvariantsAtCountdown(factory_, selected_operation, count,