diff --git a/src/corelib/tools/qduplicatetracker_p.h b/src/corelib/tools/qduplicatetracker_p.h index 3e29ede3701..4740047356b 100644 --- a/src/corelib/tools/qduplicatetracker_p.h +++ b/src/corelib/tools/qduplicatetracker_p.h @@ -45,7 +45,8 @@ class QDuplicateTracker { char buffer[bufferSize(Prealloc)]; std::pmr::monotonic_buffer_resource res{buffer, sizeof buffer}; - std::pmr::unordered_set> set{Prealloc, &res}; + using Set = std::pmr::unordered_set>; + Set set{Prealloc, &res}; #else class Set : public QSet { qsizetype setSize = 0; @@ -120,7 +121,20 @@ public: void clear() { +#ifdef __cpp_lib_memory_resource + // The birth defect of std::unordered_set is that both the nodes as + // well as the bucket array are allocated from the same allocator, so + // if we want to reclaim memory from the freed nodes, we also need to + // reclaim the memory for the bucket array. + + set = Set(); // release all memory in `res` (clear() doesn't, and swap() is UB!) + res.release(); // restore to initial state (buffer, sizeof buffer) + // m_b_r can't reuse buffers, anyway + // now that `res` is reset to the initial state, also reset `set`: + set = Set{Prealloc, &res}; +#else set.clear(); +#endif // __cpp_lib_memory_resource } }; diff --git a/tests/auto/corelib/tools/qduplicatetracker/tst_qduplicatetracker.cpp b/tests/auto/corelib/tools/qduplicatetracker/tst_qduplicatetracker.cpp index f370fcf6814..ef8c36e9c55 100644 --- a/tests/auto/corelib/tools/qduplicatetracker/tst_qduplicatetracker.cpp +++ b/tests/auto/corelib/tools/qduplicatetracker/tst_qduplicatetracker.cpp @@ -96,13 +96,15 @@ void tst_QDuplicateTracker::clear() QVERIFY(!tracker.hasSeen(1)); QVERIFY(tracker.hasSeen(1)); - tracker.clear(); - QVERIFY(!tracker.contains(0)); - QVERIFY(!tracker.hasSeen(0)); - QVERIFY(tracker.hasSeen(0)); - QVERIFY(!tracker.hasSeen(1)); - QVERIFY(tracker.hasSeen(1)); - QVERIFY(tracker.contains(1)); + for (int i = 0; i < 100; ++i) { + tracker.clear(); + QVERIFY(!tracker.contains(0)); + QVERIFY(!tracker.hasSeen(0)); + QVERIFY(tracker.hasSeen(0)); + QVERIFY(!tracker.hasSeen(1)); + QVERIFY(tracker.hasSeen(1)); + QVERIFY(tracker.contains(1)); + } } void tst_QDuplicateTracker::appendTo()