11/*
2- * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+ * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
33 * SPDX-License-Identifier: Apache-2.0
44 *
55 * Licensed under the Apache License, Version 2.0 (the "License");
2121#include " tensorrt_llm/common/logger.h"
2222#include " tensorrt_llm/common/opUtils.h"
2323
24+ #include < chrono>
25+ #include < exception>
2426#include < mutex>
2527
2628namespace tensorrt_llm ::batch_manager
2729{
2830
31+ namespace
32+ {
33+
34+ char const * bufferKindName (BufferKind kind)
35+ {
36+ switch (kind)
37+ {
38+ case BufferKind::kKV : return " kv" ;
39+ case BufferKind::kKV_INDEXER : return " kv_indexer" ;
40+ case BufferKind::kRNN : return " rnn" ;
41+ }
42+ return " unknown" ;
43+ }
44+
45+ } // namespace
46+
2947void BufferIndexHolder::release () noexcept
3048{
3149 if (!mHeld || mMgr == nullptr )
@@ -50,6 +68,30 @@ void BufferIndexHolder::release() noexcept
5068 mHeld = false ;
5169}
5270
71+ void BufferIndexHolder::poison () noexcept
72+ {
73+ if (!mHeld || mMgr == nullptr )
74+ {
75+ return ;
76+ }
77+ try
78+ {
79+ if (mIsRecv )
80+ {
81+ mMgr ->poisonBufferIndexForRecv (mIndex );
82+ }
83+ else
84+ {
85+ mMgr ->poisonBufferIndexForSend (mIndex );
86+ }
87+ }
88+ catch (...)
89+ {
90+ // noexcept: poison is a fail-closed best effort from exception paths.
91+ }
92+ mHeld = false ;
93+ }
94+
5395BaseTransBufferManager::BaseTransBufferManager (
5496 size_t transferBufferSize, nvinfer1::DataType dataType, std::optional<size_t > maxNumTokens)
5597 : mDataType {dataType}
@@ -78,26 +120,40 @@ BaseTransBufferManager::BaseTransBufferManager(
78120 allocateBuffer ();
79121}
80122
81- std::optional<int > BaseTransBufferManager::assignBufferIndexForSend ()
123+ std::optional<int > BaseTransBufferManager::assignBufferIndexForSend (
124+ std::atomic<bool > const * perRequestCancel, int64_t waitSliceMs)
82125{
83- return assignBufferIndex (mConcurrenceSendResource , mSendBufferCount , mOnlyUseDynamicBuffer );
126+ return assignBufferIndex (
127+ mConcurrenceSendResource , mSendBufferCount , mOnlyUseDynamicBuffer , perRequestCancel, waitSliceMs);
84128}
85129
86130void BaseTransBufferManager::freeBufferIndexForSend (std::optional<int > bufferId)
87131{
88132 freeBufferIndex (mConcurrenceSendResource , bufferId, mSendBufferCount , mOnlyUseDynamicBuffer );
89133}
90134
91- std::optional<int > BaseTransBufferManager::assignBufferIndexForRecv ()
135+ void BaseTransBufferManager::poisonBufferIndexForSend (std::optional<int > bufferId) noexcept
136+ {
137+ poisonBufferIndex (mConcurrenceSendResource , bufferId, mSendBufferCount , mOnlyUseDynamicBuffer , " send" );
138+ }
139+
140+ std::optional<int > BaseTransBufferManager::assignBufferIndexForRecv (
141+ std::atomic<bool > const * perRequestCancel, int64_t waitSliceMs)
92142{
93- return assignBufferIndex (mConcurrenceRecvResource , mRecvBufferCount , mOnlyUseDynamicBuffer );
143+ return assignBufferIndex (
144+ mConcurrenceRecvResource , mRecvBufferCount , mOnlyUseDynamicBuffer , perRequestCancel, waitSliceMs);
94145}
95146
96147void BaseTransBufferManager::freeBufferIndexForRecv (std::optional<int > bufferId)
97148{
98149 freeBufferIndex (mConcurrenceRecvResource , bufferId, mRecvBufferCount , mOnlyUseDynamicBuffer );
99150}
100151
152+ void BaseTransBufferManager::poisonBufferIndexForRecv (std::optional<int > bufferId) noexcept
153+ {
154+ poisonBufferIndex (mConcurrenceRecvResource , bufferId, mRecvBufferCount , mOnlyUseDynamicBuffer , " recv" );
155+ }
156+
101157std::tuple<std::vector<runtime::ITensor::SharedPtr>, size_t , bool > BaseTransBufferManager::getOrAllocateSendBuffers (
102158 std::optional<int > bufferId, int targetNum, std::vector<size_t > const & requestedNumberOfElements,
103159 runtime::BufferManager const & bufferManagerToUse)
@@ -249,16 +305,44 @@ void BaseTransBufferManager::allocateBuffer()
249305 }
250306}
251307
252- std::optional<int > BaseTransBufferManager::assignBufferIndex (
253- ConcurrenceResource& resource, size_t bufferCount, bool onlyUseDynamicBuffer )
308+ std::optional<int > BaseTransBufferManager::assignBufferIndex (ConcurrenceResource& resource, size_t bufferCount,
309+ bool onlyUseDynamicBuffer, std::atomic< bool > const * perRequestCancel, int64_t waitSliceMs )
254310{
311+ auto const isCancelled = [perRequestCancel]()
312+ { return perRequestCancel != nullptr && perRequestCancel->load (std::memory_order_relaxed); };
313+ if (isCancelled ())
314+ {
315+ TLLM_THROW (" Cache transfer buffer acquisition cancelled" );
316+ }
255317 if (onlyUseDynamicBuffer)
256318 {
319+ TLLM_CHECK_WITH_INFO (!resource.mPoisoned .load (std::memory_order_relaxed),
320+ " Cannot assign dynamic cache transfer buffer kind=%s because the transfer buffer pool is poisoned" ,
321+ bufferKindName (getBufferKind ()));
257322 return std::nullopt ;
258323 }
259324 std::unique_lock lk (resource.mBuffersMutex );
260- resource.mBuffersCV .wait (
261- lk, [&resource, bufferCount]() { return static_cast <size_t >(resource.mConcurrence ) < bufferCount; });
325+ auto const predicate = [&resource, bufferCount]()
326+ {
327+ return resource.mPoisoned .load (std::memory_order_relaxed)
328+ || static_cast <size_t >(resource.mConcurrence ) < bufferCount;
329+ };
330+ auto const slice = std::chrono::milliseconds{waitSliceMs};
331+ while (!predicate ())
332+ {
333+ resource.mBuffersCV .wait_for (lk, slice);
334+ if (isCancelled ())
335+ {
336+ TLLM_THROW (" Cache transfer buffer acquisition cancelled" );
337+ }
338+ }
339+ if (isCancelled ())
340+ {
341+ TLLM_THROW (" Cache transfer buffer acquisition cancelled" );
342+ }
343+ TLLM_CHECK_WITH_INFO (!resource.mPoisoned .load (std::memory_order_relaxed),
344+ " Cannot assign cache transfer buffer kind=%s because the transfer buffer pool is poisoned" ,
345+ bufferKindName (getBufferKind ()));
262346 int bufferId = -1 ;
263347 for (size_t i = 0 ; i < bufferCount; i++)
264348 {
@@ -288,13 +372,61 @@ void BaseTransBufferManager::freeBufferIndex(
288372 TLLM_CHECK (static_cast <size_t >(bufferId.value ()) < bufferCount);
289373 {
290374 std::scoped_lock lk (resource.mBuffersMutex );
375+ if (resource.mBufferIndexFlag [bufferId.value ()] == 2 )
376+ {
377+ TLLM_LOG_ERROR (" Refusing to free poisoned cache transfer buffer kind=%s index=%d" ,
378+ bufferKindName (getBufferKind ()), bufferId.value ());
379+ return ;
380+ }
291381 resource.mBufferIndexFlag [bufferId.value ()] = 0 ;
292382 }
293383 resource.mConcurrence --;
294384 resource.mBuffersCV .notify_one ();
295385 }
296386}
297387
388+ void BaseTransBufferManager::poisonBufferIndex (ConcurrenceResource& resource, std::optional<int > bufferId,
389+ size_t bufferCount, bool onlyUseDynamicBuffer, char const * direction) noexcept
390+ {
391+ resource.mPoisoned .store (true , std::memory_order_relaxed);
392+
393+ if (onlyUseDynamicBuffer)
394+ {
395+ TLLM_LOG_ERROR (" Poisoned dynamic %s cache transfer buffer kind=%s; process restart is required" , direction,
396+ bufferKindName (getBufferKind ()));
397+ resource.mBuffersCV .notify_all ();
398+ return ;
399+ }
400+
401+ try
402+ {
403+ if (bufferId.has_value ())
404+ {
405+ TLLM_CHECK (static_cast <size_t >(bufferId.value ()) < bufferCount);
406+ {
407+ std::scoped_lock lk (resource.mBuffersMutex );
408+ if (resource.mBufferIndexFlag [bufferId.value ()] == 1 )
409+ {
410+ resource.mBufferIndexFlag [bufferId.value ()] = 2 ;
411+ }
412+ }
413+ }
414+ TLLM_LOG_ERROR (" Poisoned %s cache transfer buffer kind=%s index=%d; process restart is required" , direction,
415+ bufferKindName (getBufferKind ()), bufferId.value_or (-1 ));
416+ }
417+ catch (std::exception const & e)
418+ {
419+ TLLM_LOG_ERROR (" Exception while poisoning %s cache transfer buffer kind=%s index=%d: %s" , direction,
420+ bufferKindName (getBufferKind ()), bufferId.value_or (-1 ), e.what ());
421+ }
422+ catch (...)
423+ {
424+ TLLM_LOG_ERROR (" Unknown exception while poisoning %s cache transfer buffer kind=%s index=%d" , direction,
425+ bufferKindName (getBufferKind ()), bufferId.value_or (-1 ));
426+ }
427+ resource.mBuffersCV .notify_all ();
428+ }
429+
298430size_t BaseTransBufferManager::getRecvBufferCount ()
299431{
300432 return mRecvBufferCount ;
0 commit comments