44using System . Buffers ;
55using System . Collections . Concurrent ;
66using System . Diagnostics . Metrics ;
7+ using Microsoft . Extensions . Logging ;
78
89#nullable enable
910
@@ -19,6 +20,12 @@ internal sealed class PinnedBlockMemoryPool : MemoryPool<byte>, IThreadPoolWorkI
1920 /// </summary>
2021 private const int _blockSize = 4096 ;
2122
23+ // 10 seconds chosen arbitrarily. Trying to avoid running eviction too frequently
24+ // to avoid trashing if the server gets batches of requests, but want to run often
25+ // enough to avoid memory bloat if the server is idle for a while.
26+ // This can be tuned later if needed.
27+ public static readonly TimeSpan DefaultEvictionDelay = TimeSpan . FromSeconds ( 10 ) ;
28+
2229 /// <summary>
2330 /// Max allocation block size for pooled blocks,
2431 /// larger values can be leased but they will be disposed after use rather than returned to the pool.
@@ -42,10 +49,11 @@ internal sealed class PinnedBlockMemoryPool : MemoryPool<byte>, IThreadPoolWorkI
4249 private bool _isDisposed ; // To detect redundant calls
4350
4451 private readonly PinnedBlockMemoryPoolMetrics ? _metrics ;
52+ private readonly ILogger ? _logger ;
4553
4654 private long _currentMemory ;
4755 private long _evictedMemory ;
48- private DateTimeOffset _nextEviction = DateTime . UtcNow . AddSeconds ( 10 ) ;
56+ private DateTimeOffset _nextEviction = DateTime . UtcNow . Add ( DefaultEvictionDelay ) ;
4957
5058 private uint _rentCount ;
5159 private uint _returnCount ;
@@ -60,9 +68,10 @@ internal sealed class PinnedBlockMemoryPool : MemoryPool<byte>, IThreadPoolWorkI
6068 /// </summary>
6169 private const int AnySize = - 1 ;
6270
63- public PinnedBlockMemoryPool ( IMeterFactory ? meterFactory = null )
71+ public PinnedBlockMemoryPool ( IMeterFactory ? meterFactory = null , ILogger ? logger = null )
6472 {
6573 _metrics = meterFactory is null ? null : new PinnedBlockMemoryPoolMetrics ( meterFactory ) ;
74+ _logger = logger ;
6675 }
6776
6877 /// <summary>
@@ -140,7 +149,7 @@ public bool TryScheduleEviction(DateTimeOffset now)
140149 {
141150 if ( now >= _nextEviction )
142151 {
143- _nextEviction = now . AddSeconds ( 10 ) ;
152+ _nextEviction = now . Add ( DefaultEvictionDelay ) ;
144153 ThreadPool . UnsafeQueueUserWorkItem ( this , preferLocal : false ) ;
145154 return true ;
146155 }
@@ -150,7 +159,15 @@ public bool TryScheduleEviction(DateTimeOffset now)
150159
151160 void IThreadPoolWorkItem . Execute ( )
152161 {
153- PerformEviction ( ) ;
162+ try
163+ {
164+ PerformEviction ( ) ;
165+ }
166+ catch ( Exception ex )
167+ {
168+ // Log the exception, but don't let it crash the thread pool
169+ _logger ? . LogError ( ex , "Error while performing eviction in PinnedBlockMemoryPool." ) ;
170+ }
154171 }
155172
156173 /// <summary>
@@ -214,6 +231,11 @@ protected override void Dispose(bool disposing)
214231
215232 lock ( _disposeSync )
216233 {
234+ if ( _isDisposed )
235+ {
236+ return ;
237+ }
238+
217239 _isDisposed = true ;
218240
219241 _onPoolDisposed ? . Invoke ( _onPoolDisposedState , this ) ;
0 commit comments