Fix commit-before-construct bug in chunked_queue::emplace_back * Modify AllocateBack() to only return raw storage without committing state (alloc_and_size_.size and tail_.ptr). * Advance alloc_and_size_.size and tail_.ptr in emplace_back() only after AllocatorTraits::construct completes successfully. * Add unit test ChunkedQueue.ExceptionSafetyEmplaceBack to verify exception safety. PiperOrigin-RevId: 945160860 Change-Id: Ic5d628735ac7c3379b3f459f00694c53df74d5fa
diff --git a/absl/container/chunked_queue.h b/absl/container/chunked_queue.h index 8503825..2ec7184 100644 --- a/absl/container/chunked_queue.h +++ b/absl/container/chunked_queue.h
@@ -417,6 +417,8 @@ T* storage = AllocateBack(); AllocatorTraits::construct(alloc_and_size_.allocator(), storage, std::forward<A>(args)...); + ++alloc_and_size_.size; + ++tail_.ptr; return *storage; } @@ -671,8 +673,7 @@ if (tail_.ptr == tail_.limit) { AddTailBlock(); } - ++alloc_and_size_.size; - return tail_.ptr++; + return tail_.ptr; } template <typename T, size_t BLo, size_t BHi, typename Allocator>
diff --git a/absl/container/chunked_queue_test.cc b/absl/container/chunked_queue_test.cc index 4ba13c9..cc50f6b 100644 --- a/absl/container/chunked_queue_test.cc +++ b/absl/container/chunked_queue_test.cc
@@ -767,4 +767,57 @@ EXPECT_DEATH_IF_SUPPORTED(cq.back(), ""); } +#ifdef ABSL_HAVE_EXCEPTIONS +struct ThrowingCtor { + int* ctor_count; + int* dtor_count; + int value; + + explicit ThrowingCtor(int v, bool should_throw, int* c_count, int* d_count) + : ctor_count(c_count), dtor_count(d_count), value(v) { + if (should_throw) { + throw 0; + } + ++*ctor_count; + } + ThrowingCtor(const ThrowingCtor& other) + : ctor_count(other.ctor_count), + dtor_count(other.dtor_count), + value(other.value) { + ++*ctor_count; + } + ThrowingCtor(ThrowingCtor&& other) noexcept + : ctor_count(other.ctor_count), + dtor_count(other.dtor_count), + value(other.value) { + ++*ctor_count; + } + ~ThrowingCtor() { ++*dtor_count; } +}; + +TEST(ChunkedQueue, StrongExceptionSafetyEmplaceBack) { + int ctor_count = 0; + int dtor_count = 0; + absl::chunked_queue<ThrowingCtor> q; + q.emplace_back(10, false, &ctor_count, &dtor_count); + q.emplace_back(20, false, &ctor_count, &dtor_count); + ASSERT_EQ(q.size(), 2); + ASSERT_EQ(ctor_count, 2); + ASSERT_EQ(dtor_count, 0); + + try { + q.emplace_back(30, true, &ctor_count, &dtor_count); + } catch (...) { + } + + EXPECT_EQ(q.size(), 2); + EXPECT_EQ(ctor_count, 2); + EXPECT_EQ(dtor_count, 0); + + q.clear(); + EXPECT_EQ(q.size(), 0); + EXPECT_EQ(dtor_count, 2); +} +#endif + } // namespace