in src/Elastic.Transport/Components/TransportClient/HandlerTracking/RequestDataHttpClientFactory.cs [175:233]
private void CleanupTimer_Tick()
{
// Stop any pending timers, we'll restart the timer if there's anything left to process after cleanup.
//
// With the scheme we're using it's possible we could end up with some redundant cleanup operations.
// This is expected and fine.
//
// An alternative would be to take a lock during the whole cleanup process. This isn't ideal because it
// would result in threads executing ExpiryTimer_Tick as they would need to block on cleanup to figure out
// whether we need to start the timer.
StopCleanupTimer();
if (!Monitor.TryEnter(_cleanupActiveLock))
{
// We don't want to run a concurrent cleanup cycle. This can happen if the cleanup cycle takes
// a long time for some reason. Since we're running user code inside Dispose, it's definitely
// possible.
//
// If we end up in that position, just make sure the timer gets started again. It should be cheap
// to run a 'no-op' cleanup.
StartCleanupTimer();
return;
}
try
{
var initialCount = _expiredHandlers.Count;
for (var i = 0; i < initialCount; i++)
{
// Since we're the only one removing from _expired, TryDequeue must always succeed.
_expiredHandlers.TryDequeue(out var entry);
Debug.Assert(entry != null, "Entry was null, we should always get an entry back from TryDequeue");
if (entry.CanDispose)
{
try
{
entry.InnerHandler.Dispose();
}
catch (Exception)
{
// ignored (ignored in HttpClientFactory too)
}
}
// If the entry is still live, put it back in the queue so we can process it
// during the next cleanup cycle.
else
_expiredHandlers.Enqueue(entry);
}
}
finally
{
Monitor.Exit(_cleanupActiveLock);
}
// We didn't totally empty the cleanup queue, try again later.
if (_expiredHandlers.Count > 0) StartCleanupTimer();
}