@@ -136,6 +136,18 @@ struct LargeTraits : public MallocTrackingTraits
136136 static const size_t IMPLICIT_INITIAL_INDEX_SIZE = 128 ;
137137};
138138
139+ struct SmallImplicitIndexTraits : public MallocTrackingTraits
140+ {
141+ static const size_t BLOCK_SIZE = 4 ;
142+ static const size_t IMPLICIT_INITIAL_INDEX_SIZE = 4 ;
143+ };
144+
145+ struct LargerImplicitIndexTraits : public MallocTrackingTraits
146+ {
147+ static const size_t BLOCK_SIZE = 4 ;
148+ static const size_t IMPLICIT_INITIAL_INDEX_SIZE = 16 ;
149+ };
150+
139151// Note: Not thread safe!
140152struct Foo
141153{
@@ -375,6 +387,7 @@ class ConcurrentQueueTests : public TestClass<ConcurrentQueueTests>
375387
376388 REGISTER_TEST (explicit_strings_threaded);
377389 REGISTER_TEST (large_traits);
390+ REGISTER_TEST (implicit_producer_index_limit);
378391 }
379392
380393 bool postTest (bool testSucceeded) override
@@ -5031,6 +5044,68 @@ class ConcurrentQueueTests : public TestClass<ConcurrentQueueTests>
50315044 return true ;
50325045 }
50335046
5047+ bool implicit_producer_index_limit ()
5048+ {
5049+ // Issue #418: try_enqueue() fails around BLOCK_SIZE * IMPLICIT_INITIAL_INDEX_SIZE
5050+ // elements even when blocks have been pre-allocated, because the block index
5051+ // (not the blocks themselves) needs to grow, which requires allocation that
5052+ // try_enqueue refuses to do.
5053+
5054+ // SmallImplicitIndexTraits: BLOCK_SIZE=4, IMPLICIT_INITIAL_INDEX_SIZE=4, limit=16
5055+ // LargerImplicitIndexTraits: BLOCK_SIZE=4, IMPLICIT_INITIAL_INDEX_SIZE=16, limit=64
5056+
5057+ {
5058+ // Demonstrate the limit: try_enqueue fails at BLOCK_SIZE * IMPLICIT_INITIAL_INDEX_SIZE
5059+ const int limit = (int )(SmallImplicitIndexTraits::BLOCK_SIZE * SmallImplicitIndexTraits::IMPLICIT_INITIAL_INDEX_SIZE);
5060+ ConcurrentQueue<int , SmallImplicitIndexTraits> q (limit + 64 ); // Pre-allocate plenty of blocks
5061+
5062+ int successCount = 0 ;
5063+ for (int i = 0 ; i < limit + 64 ; ++i) {
5064+ if (!q.try_enqueue (i))
5065+ break ;
5066+ ++successCount;
5067+ }
5068+ // try_enqueue should stop succeeding at the index limit
5069+ ASSERT_OR_FAIL (successCount == limit);
5070+ }
5071+
5072+ {
5073+ // Fix #1: enqueue() (which can allocate) works past the limit
5074+ const int limit = (int )(SmallImplicitIndexTraits::BLOCK_SIZE * SmallImplicitIndexTraits::IMPLICIT_INITIAL_INDEX_SIZE);
5075+ ConcurrentQueue<int , SmallImplicitIndexTraits> q (limit + 64 );
5076+
5077+ for (int i = 0 ; i < limit + 64 ; ++i) {
5078+ ASSERT_OR_FAIL (q.enqueue (i));
5079+ }
5080+ // Verify all elements are dequeued correctly
5081+ int item;
5082+ for (int i = 0 ; i < limit + 64 ; ++i) {
5083+ ASSERT_OR_FAIL (q.try_dequeue (item));
5084+ ASSERT_OR_FAIL (item == i);
5085+ }
5086+ ASSERT_OR_FAIL (!q.try_dequeue (item));
5087+ }
5088+
5089+ {
5090+ // Fix #2: larger IMPLICIT_INITIAL_INDEX_SIZE allows more try_enqueue calls
5091+ const int small_limit = (int )(SmallImplicitIndexTraits::BLOCK_SIZE * SmallImplicitIndexTraits::IMPLICIT_INITIAL_INDEX_SIZE); // 16
5092+ const int large_limit = (int )(LargerImplicitIndexTraits::BLOCK_SIZE * LargerImplicitIndexTraits::IMPLICIT_INITIAL_INDEX_SIZE); // 64
5093+ ConcurrentQueue<int , LargerImplicitIndexTraits> q (large_limit + 64 );
5094+
5095+ int successCount = 0 ;
5096+ for (int i = 0 ; i < large_limit + 64 ; ++i) {
5097+ if (!q.try_enqueue (i))
5098+ break ;
5099+ ++successCount;
5100+ }
5101+ // Should succeed well past the old small limit
5102+ ASSERT_OR_FAIL (successCount > small_limit);
5103+ ASSERT_OR_FAIL (successCount == large_limit);
5104+ }
5105+
5106+ return true ;
5107+ }
5108+
50345109 bool large_traits ()
50355110 {
50365111 union Elem { uint32_t x; char dummy[156 ]; };
0 commit comments