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