/// <summary> /// Releases a <paramref name="pooledValue"/> back into the pool. /// </summary> /// <param name="pooledValue">The pooled value.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns></returns> public void ReleasePooledValue(Pooled <TValue> pooledValue, CancellationToken cancellationToken = new CancellationToken()) { if (IsDisposed || IsDisposing) { throw new ObjectDisposedException(this.GetType().Name); } if (pooledValue == null) { throw new ArgumentNullException(nameof(pooledValue)); } if (pooledValue.OwningPool != this) { throw new ArgumentOutOfRangeException(nameof(pooledValue), "Only pooled values managed by this pool can be released back into the pool."); } if (pooledValue.HasBeenReleasedBackToPool) { throw new ArgumentOutOfRangeException(nameof(pooledValue), "Pooled values that have already been released back and returned to the pool cannot be released a second time."); } if (pooledValue.HasBeenDetachedFromPool) { throw new ArgumentOutOfRangeException(nameof(pooledValue), "Detached pooled values can no longer be released and returned back into the pool."); } // else cancellationToken.ThrowIfCancellationRequested(); PooledInstances.Enqueue(pooledValue.Value); RaisePropertyChanged(nameof(AvailableInstancesCount)); pooledValue.HasBeenReleasedBackToPool = true; }
/// <summary> /// Increases the total size of the pool by the amount specified. /// </summary> /// <param name="increaseBy">The amount of values to increase the pool by.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns></returns> public async Task IncreasePoolSizeAsync(int increaseBy = 1, CancellationToken cancellationToken = default(CancellationToken)) { if (IsDisposed || IsDisposing) { throw new ObjectDisposedException(this.GetType().Name); } if (increaseBy < 0) { throw new ArgumentOutOfRangeException(nameof(increaseBy)); } for (int i = 0; i < increaseBy; i++) { cancellationToken.ThrowIfCancellationRequested(); var instance = await Task.Run(() => InstanceBuilder.Invoke(cancellationToken), cancellationToken).ConfigureAwait(false); await Task.Run(() => { PooledInstances.Enqueue(instance); }, cancellationToken).ConfigureAwait(false); Interlocked.Increment(ref _totalInstancesCount); RaisePropertyChanged(nameof(TotalInstancesCount)); } }
/// <summary> /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// </summary> public void Dispose() { if (IsDisposing || IsDisposed) { return; } try { IsDisposing = true; if (PooledInstances.IsEmpty) { return; } while (PooledInstances.IsEmpty == false) { TValue value; while (PooledInstances.TryDequeue(out value) == false) { var valueAsIDisposable = value as IDisposable; valueAsIDisposable?.Dispose(); if (PooledInstances.IsEmpty) { break; } } } } finally { IsDisposed = true; IsDisposing = false; PooledInstances = null; InstanceBuilder = null; GC.SuppressFinalize(this); } }
/// <summary> /// Decreases the size of the pool by the amount of instances specified. /// This only has an effect if there are any instances currently available to be /// acquired, this has no effect on already and currently acquired instances. /// </summary> /// <param name="decreaseBy">The amount of values to decrease the pool by.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns></returns> /// <exception cref="System.ArgumentOutOfRangeException"> /// Cannot decrease the amount of (available) pooled items by less than 0 /// or /// Cannot decrease the amount of (available) pooled items by more than what's available. /// </exception> public async Task DecreaseAvailablePoolSizeAsync(int decreaseBy = 1, CancellationToken cancellationToken = default(CancellationToken)) { if (IsDisposed || IsDisposing) { throw new ObjectDisposedException(this.GetType().Name); } if (decreaseBy < 0) { throw new ArgumentOutOfRangeException(nameof(decreaseBy), "Cannot decrease the amount of (available) pooled items by less than 0"); } if (decreaseBy > PooledInstances.Count) { throw new ArgumentOutOfRangeException(nameof(decreaseBy), "Cannot decrease the amount of (available) pooled items by more than what's available."); } for (int i = 0; i < decreaseBy; i++) { cancellationToken.ThrowIfCancellationRequested(); if (PooledInstances.IsEmpty) { return; // nothing more to do - the queue is empty } TValue dequeuedValue; while (PooledInstances.IsEmpty == false && PooledInstances.TryDequeue(out dequeuedValue) == false) { cancellationToken.ThrowIfCancellationRequested(); await Task.Yield(); } Interlocked.Decrement(ref _totalInstancesCount); RaisePropertyChanged(nameof(TotalInstancesCount)); } }
/// <summary> /// Acquires the next available pooled value. /// </summary> /// <param name="pooledValueAcquisitionMode">The pooled value acquisition mode.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns></returns> public async Task <Pooled <TValue> > AcquirePooledValueAsync( PooledValueAcquisitionMode pooledValueAcquisitionMode = PooledValueAcquisitionMode.AvailableInstanceOrDefaultValue, CancellationToken cancellationToken = default(CancellationToken)) { if (IsDisposed || IsDisposing) { throw new ObjectDisposedException(this.GetType().Name); } TValue value = default(TValue); // check whether we actually currently have any instances left if (PooledInstances.IsEmpty) { if (pooledValueAcquisitionMode == PooledValueAcquisitionMode.AvailableInstanceOrDefaultValue) { return(default(Pooled <TValue>)); } else if (pooledValueAcquisitionMode == PooledValueAcquisitionMode.AvailableInstanceOrCreateNewOne) { try { value = await Task.Run(() => InstanceBuilder.Invoke(cancellationToken), cancellationToken).ConfigureAwait(false); } catch (TargetInvocationException targetInvocationException) { targetInvocationException.InnerException.ThrowIfNotNull(); } } else { while (PooledInstances.IsEmpty || PooledInstances.TryDequeue(out value) == false) { cancellationToken.ThrowIfCancellationRequested(); await Task.Yield(); } } } else { // try to retrieve the next available / queued value if (PooledInstances.TryDequeue(out value) == false) { if (pooledValueAcquisitionMode == PooledValueAcquisitionMode.AvailableInstanceOrDefaultValue) { return(default(Pooled <TValue>)); } else if (pooledValueAcquisitionMode == PooledValueAcquisitionMode.AvailableInstanceOrCreateNewOne) // build a new one { try { value = await Task.Run(() => InstanceBuilder.Invoke(cancellationToken), cancellationToken).ConfigureAwait(false); } catch (TargetInvocationException targetInvocationException) { targetInvocationException.InnerException.ThrowIfNotNull(); } } else { // keep trying while (PooledInstances.TryDequeue(out value) == false) { cancellationToken.ThrowIfCancellationRequested(); await Task.Yield(); } } } } RaisePropertyChanged(nameof(AvailableInstancesCount)); cancellationToken.ThrowIfCancellationRequested(); return(new Pooled <TValue>(value, this)); }