@@ -2894,22 +2894,22 @@ void BlockManager::releasePrefixBlocks(GenerationRequest& sequence, SizeType32 n
28942894 // today (gated by should_store_blocks: not is_vswa in the executor and
28952895 // beamWidth == 1 assertion in WindowBlockManager::releasePrefixBlocks).
28962896 //
2897+ auto const windowSize = mWindowBlockManagers .cbegin ()->first ;
28972898 // Snapshot the counter before iterating so that every WindowBlockManager
28982899 // releases the same range. Without this, the first manager would advance
2899- // the shared mNumFrontBlocksRemoved counter and subsequent managers would
2900- // see the counter already at the target, skipping their own blocks.
2901- SizeType32 const startIdx = sequence.getNumFrontBlocksRemoved ();
2900+ // the single-window front-block counter and subsequent managers would see
2901+ // the counter already at the target, skipping their own blocks.
2902+ SizeType32 const startIdx = sequence.getNumFrontBlocksRemoved (windowSize );
29022903 for (auto & [_, manager] : mWindowBlockManagers )
29032904 {
29042905 manager.releasePrefixBlocks (sequence, startIdx, numBlocks);
29052906 }
2906- // Advance the shared counter once, after all managers have released.
2907+ // Advance the single-window counter once, after all managers have released.
29072908 // Uses incrementNumFrontBlocksRemoved (counter-only) instead of
2908- // removeFrontBlock so the intent is explicit and we do not depend on
2909- // removeFrontBlock ignoring its windowSize argument.
2910- while (sequence.getNumFrontBlocksRemoved () < numBlocks)
2909+ // removeFrontBlock so the intent is explicit.
2910+ while (sequence.getNumFrontBlocksRemoved (windowSize) < numBlocks)
29112911 {
2912- sequence.incrementNumFrontBlocksRemoved ();
2912+ sequence.incrementNumFrontBlocksRemoved (windowSize );
29132913 }
29142914}
29152915
@@ -3727,23 +3727,30 @@ void WindowBlockManager::releasePrefixBlocks(GenerationRequest& sequence, SizeTy
37273727 auto & allocatedBlocks = mAllocatedBlocksPerSeq .at (requestId);
37283728 SizeType32 const target = std::min (numBlocks, static_cast <SizeType32>(allocatedBlocks.size ()));
37293729
3730- // Release blocks in range [startIdx, target). The shared
3731- // mNumFrontBlocksRemoved counter is advanced by BlockManager after
3730+ // Release blocks in range [startIdx, target). The single-window
3731+ // front-block counter is advanced by BlockManager after
37323732 // all WindowBlockManagers have processed the same range.
37333733 for (SizeType32 blockIdx = startIdx; blockIdx < target; ++blockIdx)
37343734 {
37353735 auto & block = allocatedBlocks.at (blockIdx);
3736+ auto releasedBlock = block;
37363737
37373738 TLLM_LOG_DEBUG (" %s::releasePrefixBlocks - Releasing block %d from sequence %lu" , mLogPrefix .c_str (),
3738- block ->getBlockId (), requestId);
3739+ releasedBlock ->getBlockId (), requestId);
37393740
3740- if (block->hasRefs ())
3741+ // Replace the sequence slot with a placeholder, matching detachFrontBlock().
3742+ // removeSequence later walks allocatedBlocks in releaseBlocks(); leaving the
3743+ // real block here would release it a second time and corrupt the eviction
3744+ // policy's free-block count.
3745+ block = KVCacheBlock::createPlaceholder ();
3746+
3747+ if (releasedBlock->hasRefs ())
37413748 {
3742- block ->decRefCount ();
3749+ releasedBlock ->decRefCount ();
37433750 }
3744- if (!block ->hasRefs ())
3751+ if (!releasedBlock ->hasRefs ())
37453752 {
3746- mEvictionPolicy ->releaseBlock (block );
3753+ mEvictionPolicy ->releaseBlock (releasedBlock );
37473754 }
37483755 }
37493756}
@@ -3926,8 +3933,8 @@ std::optional<KVCacheBlock::IdType> KVCacheManager::removeSequence(
39263933
39273934void KVCacheManager::releasePrefixBlocks (RequestIdType requestId, SizeType32 numBlocks)
39283935{
3929- // Hard precondition: BlockManager::releasePrefixBlocks advances the shared
3930- // mNumFrontBlocksRemoved counter to numBlocks for every WindowBlockManager,
3936+ // Hard precondition: BlockManager::releasePrefixBlocks advances the
3937+ // single-window front-block counter to numBlocks for every WindowBlockManager,
39313938 // even when a window has fewer than numBlocks allocated. Under variable
39323939 // sliding window attention (VSWA), that would cause WindowBlockManager::
39333940 // releaseBlocks (called during removeSequence) to underrun rbegin() and
0 commit comments