private ResourceWrapperV2 <TObject> CreateWrapper(Context context, TKey key)
        {
            var lazy = new AsyncLazy <TObject>(async() =>
            {
                Counter[ResourcePoolV2Counters.ResourceInitializationAttempts].Increment();

                try
                {
                    var result = await CreateInstanceAsync(context, key);
                    Counter[ResourcePoolV2Counters.ResourceInitializationSuccesses].Increment();
                    return(result);
                }
                catch
                {
                    Counter[ResourcePoolV2Counters.ResourceInitializationFailures].Increment();
                    throw;
                }
            });

            var wrapperId = Guid.NewGuid();
            var wrapper   = new ResourceWrapperV2 <TObject>(wrapperId, lazy, _clock.UtcNow, _disposeCancellationTokenSource.Token);

            Counter[ResourcePoolV2Counters.CreatedResources].Increment();
            context.Info($"Created wrapper with id {wrapperId}");
            return(wrapper);
        }
        private void ReleaseWrapper(Context context, TKey key, ResourceWrapperV2 <TObject> wrapper)
        {
            lock (_resourcesLock)
            {
                _resources.Remove(key);
            }

            wrapper.CancelOngoingOperations(context);
            _shutdownQueue.Enqueue(wrapper);

            Counter[ResourcePoolV2Counters.ReleasedResources].Increment();
        }
        private async Task ReleaseWrapperAsync(Context context, ResourceWrapperV2 <TObject> wrapper)
        {
            Contract.Requires(wrapper.ShutdownToken.IsCancellationRequested);

            try
            {
                var lazyValueTask = wrapper.LazyValue;

                // When running GC on Dispose, it is possible for this method to be called with an uninitialized or
                // faulted.
                if (!wrapper.IsValueCreated || lazyValueTask.IsFaulted)
                {
                    // We will still dispose in the finally block
                    return;
                }

                Counter[ResourcePoolV2Counters.ShutdownAttempts].Increment();
                var instance = await lazyValueTask;
                var result   = await instance.ShutdownAsync(context);

                if (result)
                {
                    Counter[ResourcePoolV2Counters.ShutdownSuccesses].Increment();
                }
                else
                {
                    Counter[ResourcePoolV2Counters.ShutdownFailures].Increment();
                }
            }
            catch (Exception exception)
            {
                Counter[ResourcePoolV2Counters.ShutdownExceptions].Increment();
                _tracer.Error(context, $"Unexpected exception during `{nameof(ResourcePoolV2<TKey, TObject>)}` shutdown: {exception}");
            }
            finally
            {
                wrapper.Dispose(context);
            }
        }
        private async Task ReleaseWrapperAsync(Context context, ResourceWrapperV2 <TObject> wrapper)
        {
            Contract.Requires(wrapper.ShutdownToken.IsCancellationRequested);

            try
            {
                var lazyValueTask = wrapper.LazyValue;
                if (lazyValueTask.IsFaulted)
                {
                    // We will still dispose in the finally block
                    return;
                }

                Counter[ResourcePoolV2Counters.ShutdownAttempts].Increment();
                var instance = await lazyValueTask;
                var result   = await instance.ShutdownAsync(context);

                if (result)
                {
                    Counter[ResourcePoolV2Counters.ShutdownSuccesses].Increment();
                }
                else
                {
                    Counter[ResourcePoolV2Counters.ShutdownFailures].Increment();
                }
            }
            catch (Exception exception)
            {
                Counter[ResourcePoolV2Counters.ShutdownExceptions].Increment();
                context.Error($"Unexpected exception during `{nameof(ResourcePoolV2<TKey, TObject>)}` shutdown: {exception}");
            }
            finally
            {
                wrapper.Dispose(context);
            }
        }