@@ -1654,3 +1654,65 @@ TEST(MultiStock, ContinueOnCancelIdleReuse)
16541654
16551655 ASSERT_EQ (foo.destroyed , 1 );
16561656}
1657+
1658+ TEST (MultiStock, ReentrantGetFromReadyHandler)
1659+ {
1660+ Instance instance{2 };
1661+
1662+ Partition foo{instance, " foo" };
1663+
1664+ struct ReentrantLease final : StockGetHandler {
1665+ Partition &partition;
1666+ bool did_reenter = false ;
1667+
1668+ CancellablePointer get_cancel_ptr;
1669+ MyInnerStockItem *item = nullptr ;
1670+
1671+ explicit ReentrantLease (Partition &_partition) noexcept
1672+ :partition(_partition) {}
1673+
1674+ ~ReentrantLease () noexcept {
1675+ if (get_cancel_ptr)
1676+ get_cancel_ptr.Cancel ();
1677+ else if (item != nullptr )
1678+ item->Put (PutAction::REUSE );
1679+ }
1680+
1681+ void OnStockItemReady (StockItem &_item) noexcept override {
1682+ get_cancel_ptr = nullptr ;
1683+ item = (MyInnerStockItem *)&_item;
1684+
1685+ if (!did_reenter) {
1686+ did_reenter = true ;
1687+ partition.Get ();
1688+ }
1689+ }
1690+
1691+ void OnStockItemError (std::exception_ptr) noexcept override {
1692+ get_cancel_ptr = nullptr ;
1693+ }
1694+ };
1695+
1696+ /* Queue one normal create, then put the reentrant waiter behind
1697+ it. This makes the reentrant waiter a real waiter even though
1698+ the MultiStock still has room for one more outer item. */
1699+ foo.defer_create = true ;
1700+ foo.Get (1 );
1701+
1702+ ReentrantLease lease{foo};
1703+ instance.multi_stock .Get (StockKey{foo.key }, ToNopPointer (&foo), 2 ,
1704+ lease, lease.get_cancel_ptr );
1705+
1706+ ASSERT_TRUE (lease.get_cancel_ptr );
1707+
1708+ /* Complete the pending create, then let the reentrant handler's
1709+ nested Get() create another outer item synchronously. */
1710+ foo.defer_create = false ;
1711+ instance.RunSome ();
1712+
1713+ ASSERT_TRUE (lease.did_reenter );
1714+ ASSERT_NE (lease.item , nullptr );
1715+ ASSERT_EQ (foo.factory_created , 2 );
1716+ ASSERT_EQ (foo.waiting , 0 );
1717+ ASSERT_EQ (foo.ready , 2 );
1718+ }
0 commit comments