Module stormpot

Class BlazePool<T extends Poolable>

java.lang.Object
stormpot.internal.BlazePool<T>
Type Parameters:
T - The type of Poolable managed by this pool.
All Implemented Interfaces:
ManagedPool, Pool<T>, PoolTap<T>

public final class BlazePool<T extends Poolable> extends Object implements Pool<T>, ManagedPool
BlazePool is a highly optimised Pool implementation that consists of a queues of Poolable instances, the access to which is made faster with clever use of ThreadLocals.

Object allocation always happens in a dedicated thread, off-loading the cost of allocating the pooled objects. This leads to reduced deviation in the times it takes claim method to complete, provided the pool is not depleted.

BlazePool optimises for the case where the same threads need to claim and release objects over and over again. On the other hand, if the releasing thread tends to differ from the claiming thread, then the major optimisation in BlazePool is defeated, and performance regresses to a slow-path that is limited by contention on a blocking queue.

Author:
Chris Vest
  • Constructor Details

  • Method Details

    • claim

      public T claim(Timeout timeout) throws PoolException, InterruptedException
      Description copied from interface: PoolTap
      Claim the exclusive rights until released, to an object in the pool. Possibly waiting up to the specified amount of time, as given by the provided Timeout instance, for one to become available if the pool has been depleted. If the timeout elapses before an object can be claimed, then null is returned instead. The timeout will be honoured even if the Allocators allocate methods blocks forever. If the given timeout has a zero or negative value, then the method will not wait.

      If the current thread has already one or more objects currently claimed, then a distinct object will be returned, if one is or becomes available. This means that it is possible for a single thread to deplete the pool, if it so desires. However, doing so is inherently deadlock prone, so avoid claiming more than one object at a time per thread, if at all possible.

      This method may throw a PoolException if the pool have trouble allocating objects. That is, if its assigned Allocator throws exceptions from its allocate method, or returns null.

      An InterruptedException will be thrown if the thread has its interrupted flag set upon entry to this method, or is interrupted while waiting. The interrupted flag on the thread will be cleared after this, as per the general contract of interruptible methods.

      If the pool has been shut down, then an IllegalStateException will be thrown when this method is called. Likewise, if we are waiting for an object to become available, and someone shuts the pool down.

      Here's an example code snippet, where an object is claimed, printed to System.out, and then released back to the pool:

      Poolable obj = pool.claim(TIMEOUT);
      if (obj != null) {
        try {
          System.out.println(obj);
        } finally {
          obj.release();
        }
      }
      

      Memory effects:

      • The release of an object happens-before any subsequent claim or deallocation of that object, and,
      • The allocation of an object happens-before any claim of that object.
      Specified by:
      claim in interface PoolTap<T extends Poolable>
      Parameters:
      timeout - The timeout of the maximum permitted time-slice to wait for an object to become available. A timeout with a value of zero or less, means that the call will do no waiting, preferring instead to return early if no objects are available.
      Returns:
      An object of the Poolable subtype T to which the exclusive rights have been claimed, or null if the timeout period elapsed before an object became available.
      Throws:
      PoolException - If an object allocation failed because the Allocator threw an exception from its allocate method, or returned null, or the expiration check threw an exception.
      InterruptedException - if the current thread is interrupted upon entry, or becomes interrupted while waiting.
    • tryClaim

      public T tryClaim() throws PoolException
      Description copied from interface: PoolTap
      Returns an object from the pool if the pool contains at least one valid object, otherwise returns null. This method will first try to return cached object if available. If no locally cached object is found, it will go through objects in the pool and return the first ready to claim object. If all the objects in the pool is drained, then null will be returned.
      Specified by:
      tryClaim in interface PoolTap<T extends Poolable>
      Returns:
      an object from the pool if the pool contains at least one valid object, otherwise returns null.
      Throws:
      PoolException - If an object allocation failed because the Allocator threw an exception from its allocate method, or returned null, or the expiration check threw an exception.
    • shutdown

      public Completion shutdown()
      Description copied from interface: Pool
      Initiate the shutdown process on this pool, and return a Completion instance representing the shutdown procedure.

      The shutdown process is asynchronous, and the shutdown method is guaranteed to not wait for any claimed Poolables to be released.

      The shutdown process cannot complete before all Poolables are released back into the pool and deallocated, and all internal resources (such as threads, for instance) have been released as well. Only when all of these things have been taken care of, does the await methods of the Completion return.

      Once the shutdown process has been initiated, that is, as soon as this method is called, the pool can no longer be used and all calls to PoolTap.claim(Timeout) will throw an IllegalStateException. Threads that are already waiting for objects in the claim method, will also wake up and receive an IllegalStateException.

      All objects that are already claimed when this method is called, will continue to function until they are released.

      The shutdown process is guaranteed to never deallocate objects that are currently claimed. Their deallocation will wait until they are released.

      Specified by:
      shutdown in interface Pool<T extends Poolable>
      Returns:
      A Completion instance that represents the shutdown process.
    • switchAllocator

      public Completion switchAllocator(Allocator<T> replacementAllocator)
      Description copied from interface: Pool
      Initiate a switch from the current allocator to the given allocator.

      This will cause all current objects to be deallocated by the old allocator, and reallocated by the new allocator. The objects will be replaced one at a time.

      The returned completion will complete when all objects have been replaced, or if the pool shuts down. If another switch is initiated before the completion of a prior switch, then the prior completion will complete when there are no more objects left from the allocator that was current at the time the prior completion was initiated.

      Direct pools, created with the Pool.of(Object[]) method, do not support switching allocators, and will instead throw an UnsupportedOperationException from this method.

      If this pool has already been shut down, then this method immediately returns a completed completion instance.

      Specified by:
      switchAllocator in interface Pool<T extends Poolable>
      Parameters:
      replacementAllocator - The new allocator.
      Returns:
      A Completion instance that represents the switch process.
    • setTargetSize

      public void setTargetSize(long size)
      Description copied from interface: Pool
      Set the target size for this pool. The pool will strive to keep this many objects allocated at any one time.

      If the new target size is greater than the old one, the pool will allocate more objects until it reaches the target size. If, on the other hand, the new target size is less than the old one, the pool will deallocate more and allocate less, until the new target size is reached.

      No guarantees are made about when the pool actually reaches the target size. In fact, it may never happen as the target size can be changed as often as one sees fit.

      Pools that do not support a size less than 0 (which would deviate from the standard configuration space) will throw an IllegalArgumentException if passed -1 or less.

      Pools that do not support online resizing will throw an UnsupportedOperationException.

      Specified by:
      setTargetSize in interface ManagedPool
      Specified by:
      setTargetSize in interface Pool<T extends Poolable>
      Parameters:
      size - The new target size of the pool.
      See Also:
    • getTargetSize

      public long getTargetSize()
      Description copied from interface: Pool
      Get the currently configured target size of the pool. Note that this is not the number of objects currently allocated by the pool - only the number of allocations the pool strives to keep alive.
      Specified by:
      getTargetSize in interface ManagedPool
      Specified by:
      getTargetSize in interface Pool<T extends Poolable>
      Returns:
      The current target size of this pool.
      See Also:
    • getManagedPool

      public ManagedPool getManagedPool()
      Description copied from interface: Pool
      Get the ManagedPool instance that represents this pool.
      Specified by:
      getManagedPool in interface Pool<T extends Poolable>
      Returns:
      The ManagedPool instance for this pool.
    • getThreadSafeTap

      public PoolTap<T> getThreadSafeTap()
      Description copied from interface: Pool
      Get a thread-safe PoolTap implementation for this pool, which can be freely shared among multiple threads.

      If the pool tap will be accessed by virtual threads, then Pool.getVirtualThreadSafeTap() should be used instead.

      Specified by:
      getThreadSafeTap in interface Pool<T extends Poolable>
      Returns:
      A thread-safe PoolTap.
    • getVirtualThreadSafeTap

      public PoolTap<T> getVirtualThreadSafeTap()
      Description copied from interface: Pool
      Get a thread-safe PoolTap implementation for this pool, which can be freely shared among multiple threads, including virtual threads.

      The implementation of this PoolTap avoids the use of ThreadLocal variables, locking mechanisms, and other API calls that are poorly supported by, or disruptive to, virtual threads.

      Specified by:
      getVirtualThreadSafeTap in interface Pool<T extends Poolable>
      Returns:
      A thread-safe PoolTap.
    • getSingleThreadedTap

      public PoolTap<T> getSingleThreadedTap()
      Description copied from interface: Pool
      Get a PoolTap that only support access by one thread at a time. In other words, where PoolTap.claim(Timeout) cannot be called concurrently in multiple threads *on the same tap*.

      The pool itself will still be thread-safe, but each thread that wishes to access the pool via a thread-local tap, must have their own tap instance.

      It is a use error to access these pool taps concurrently from multiple threads, or to transfer them from one thread to another without safe publication.

      Specified by:
      getSingleThreadedTap in interface Pool<T extends Poolable>
      Returns:
      A thread-local PoolTap.
    • getAllocationCount

      public long getAllocationCount()
      Description copied from interface: ManagedPool
      Return the number of objects the pool has allocated since it was created.
      Specified by:
      getAllocationCount in interface ManagedPool
      Returns:
      The number of Poolable objects ever created by this pool.
    • getFailedAllocationCount

      public long getFailedAllocationCount()
      Description copied from interface: ManagedPool
      Return the number of allocations that has failed, either because the allocator threw an exception or because it returned null, since the pool was created.
      Specified by:
      getFailedAllocationCount in interface ManagedPool
      Returns:
      The number of allocations that have failed for one reason or another.
    • isShutDown

      public boolean isShutDown()
      Description copied from interface: ManagedPool
      Returns true if the shutdown process has been started on this pool, false otherwise. This method does not reveal whether the shutdown process has completed.
      Specified by:
      isShutDown in interface ManagedPool
      Returns:
      true if Pool.shutdown() has been called on this pool.
    • getObjectLifetimePercentile

      public double getObjectLifetimePercentile(double percentile)
      Description copied from interface: ManagedPool
      Get the approximate object lifetime, in milliseconds, for the given percentile/quantile.
      Specified by:
      getObjectLifetimePercentile in interface ManagedPool
      Parameters:
      percentile - The percentile to get, as a decimal, e.g. a number between 0.0 and 1.0.
      Returns:
      The approximate object lifetime in milliseconds, for the given percentile, or Double.NaN if no MetricsRecorder has been configured for the pool.
      See Also:
    • getAllocationLatencyPercentile

      public double getAllocationLatencyPercentile(double percentile)
      Description copied from interface: ManagedPool
      Get the approximate Poolable object allocation latency value, in milliseconds, of the given percentile/quantile of the values recorded so far.
      Specified by:
      getAllocationLatencyPercentile in interface ManagedPool
      Parameters:
      percentile - The percentile to get, as a decimal, e.g. a number between 0.0 and 1.0.
      Returns:
      The approximate latency for allocations in milliseconds, for the given percentile, or Double.NaN if no MetricsRecorder has been configured for the pool.
      See Also:
    • getAllocationFailureLatencyPercentile

      public double getAllocationFailureLatencyPercentile(double percentile)
      Description copied from interface: ManagedPool
      Get the approximate latency value, in milliseconds, for failed allocation latencies within the given percentile/quantile of what has been recorded so far.
      Specified by:
      getAllocationFailureLatencyPercentile in interface ManagedPool
      Parameters:
      percentile - The percentile to get, as a decimal, e.g. a number between 0.0 and 1.0.
      Returns:
      The approximate latency for failed allocations in milliseconds, for the given percentile, or Double.NaN if no MetricsRecorder has been configured for the pool.
      See Also:
    • getReallocationLatencyPercentile

      public double getReallocationLatencyPercentile(double percentile)
      Description copied from interface: ManagedPool
      Get the approximate latency value, in milliseconds, for reallocation latencies within the given percentile/quantile of what has been recorded so far.
      Specified by:
      getReallocationLatencyPercentile in interface ManagedPool
      Parameters:
      percentile - The percentile to get, as a decimal, e.g. a number between 0.0 and 1.0.
      Returns:
      The approximate latency for reallocations in milliseconds, for the given percentile, or Double.NaN if no MetricsRecorder has been configured for the pool.
      See Also:
    • getReallocationFailureLatencyPercentile

      public double getReallocationFailureLatencyPercentile(double percentile)
      Description copied from interface: ManagedPool
      Get the approximate latency value, in milliseconds, for failed reallocation latencies within the given percentile/quantile of what has been recorded so far.
      Specified by:
      getReallocationFailureLatencyPercentile in interface ManagedPool
      Parameters:
      percentile - The percentile to get, as a decimal, e.g. a number between 0.0 and 1.0.
      Returns:
      The approximate latency for failed reallocations in milliseconds, for the given percentile, or Double.NaN if no MetricsRecorder has been configured for the pool.
      See Also:
    • getDeallocationLatencyPercentile

      public double getDeallocationLatencyPercentile(double percentile)
      Description copied from interface: ManagedPool
      Get the approximate latency value, in milliseconds, for deallocation latencies within the given percentile/quantile of what has been recorded so far.
      Specified by:
      getDeallocationLatencyPercentile in interface ManagedPool
      Parameters:
      percentile - The percentile to get, as a decimal, e.g. a number between 0.0 and 1.0.
      Returns:
      The approximate latency for deallocations, regardless of whether the throw exceptions or not, in milliseconds for the given percentile, or Double.NaN if no MetricsRecorder has been configured for the pool.
      See Also:
    • getLeakedObjectsCount

      public long getLeakedObjectsCount()
      Description copied from interface: ManagedPool
      If the pool is capable of precise object leak detection, this method will return the number of object leaks that have been detected, and prevented, since the pool was created. If the pool does not support precise object leak detection, then this method returns -1.

      There are two kinds of leaks: One where the application forgets to release an object back to the pool, but keeps a strong reference to the object, and another where the application not only forgets to release the object, but also looses the reference to the object, making it eligible for garbage collection. The precise leak detector will only count leaks of the latter kind, where the leaked object has been garbage collected.

      Specified by:
      getLeakedObjectsCount in interface ManagedPool
      Returns:
      The number of objects leaked from the users of this pool, since the pool was created, or -1 if the pool does not implement precise leak detection.
    • getCurrentAllocatedCount

      public long getCurrentAllocatedCount()
      Description copied from interface: ManagedPool
      Get the approximate number of currently allocated objects.

      This operation has time complexity O(poolSize) to count the number of slots.

      The default implementation of this interface methods returns -1.

      Unlike ManagedPool.getAllocationCount(), this returns only the number of objects currently in the pool, which typically would be the same as ManagedPool.getTargetSize().

      Specified by:
      getCurrentAllocatedCount in interface ManagedPool
      Returns:
      The current approximate number of allocated objects.
    • getCurrentInUseCount

      public long getCurrentInUseCount()
      Description copied from interface: ManagedPool
      Get the approximate number of objects currently in use.

      This operation has time complexity O(poolSize) to check each slot.

      The counting operation is "weakly consistent", in a similar sense to ConcurrentLinkedQueue.iterator().

      The default implementation of this interface methods returns -1.

      Specified by:
      getCurrentInUseCount in interface ManagedPool
      Returns:
      number of objects currently in use