private void CleanupTimer_Tick()

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();
	}