diff --git a/src/main/java/stormpot/AllocationController.java b/src/main/java/stormpot/AllocationController.java index ded66fa4..751923b9 100644 --- a/src/main/java/stormpot/AllocationController.java +++ b/src/main/java/stormpot/AllocationController.java @@ -54,4 +54,14 @@ abstract class AllocationController { * @see ManagedPool#getLeakedObjectsCount() */ abstract long countLeakedObjects(); + + /** + * @see ManagedPool#getCurrentAllocatedCount() + */ + abstract int allocatedSize(); + + /** + * @see ManagedPool#getCurrentInUseCount() + */ + abstract int inUse(); } diff --git a/src/main/java/stormpot/BAllocThread.java b/src/main/java/stormpot/BAllocThread.java index 6cb344ce..ec128898 100644 --- a/src/main/java/stormpot/BAllocThread.java +++ b/src/main/java/stormpot/BAllocThread.java @@ -390,4 +390,20 @@ long countLeakedObjects() { void offerDeadSlot(BSlot slot) { dead.offer(slot); } + + int allocatedSize() { + return size; + } + + int inUse() { + int inUse = 0; + int liveSize = 0; + for (BSlot slot: live) { + liveSize++; + if (slot.isClaimedOrThreadLocal()) { + inUse++; + } + } + return size - liveSize + inUse; + } } diff --git a/src/main/java/stormpot/BSlot.java b/src/main/java/stormpot/BSlot.java index 55ccb4cf..b4a9cce0 100644 --- a/src/main/java/stormpot/BSlot.java +++ b/src/main/java/stormpot/BSlot.java @@ -124,6 +124,11 @@ boolean isLive() { boolean isClaimed() { return get() == CLAIMED; } + + boolean isClaimedOrThreadLocal() { + int state = get(); + return state == CLAIMED || state == TLR_CLAIMED; + } void incrementClaims() { claims++; diff --git a/src/main/java/stormpot/BlazePool.java b/src/main/java/stormpot/BlazePool.java index f753b466..ffcb6006 100644 --- a/src/main/java/stormpot/BlazePool.java +++ b/src/main/java/stormpot/BlazePool.java @@ -377,4 +377,14 @@ public double getDeallocationLatencyPercentile(double percentile) { public long getLeakedObjectsCount() { return allocator.countLeakedObjects(); } + + @Override + public int getCurrentAllocatedCount() { + return allocator.allocatedSize(); + } + + @Override + public int getCurrentInUseCount() { + return allocator.inUse(); + } } diff --git a/src/main/java/stormpot/DirectAllocationController.java b/src/main/java/stormpot/DirectAllocationController.java index d979cd60..a8128d55 100644 --- a/src/main/java/stormpot/DirectAllocationController.java +++ b/src/main/java/stormpot/DirectAllocationController.java @@ -116,4 +116,22 @@ long getFailedAllocationCount() { long countLeakedObjects() { return -1; } + + @Override + public int allocatedSize() { + return size; + } + + @Override + int inUse() { + int inUse = 0; + int liveSize = 0; + for (BSlot slot: live) { + liveSize++; + if (slot.isClaimedOrThreadLocal()) { + inUse++; + } + } + return size - liveSize + inUse; + } } diff --git a/src/main/java/stormpot/InlineAllocationController.java b/src/main/java/stormpot/InlineAllocationController.java index 5f33b347..13321a96 100644 --- a/src/main/java/stormpot/InlineAllocationController.java +++ b/src/main/java/stormpot/InlineAllocationController.java @@ -362,4 +362,22 @@ private void recordObjectLifetimeSample(long nanoseconds) { metricsRecorder.recordObjectLifetimeSampleMillis(milliseconds); } } + + @Override + public int allocatedSize() { + return live.size() - poisonedSlots.get(); + } + + @Override + int inUse() { + int inUse = 0; + int liveSize = 0; + for (BSlot slot: live) { + liveSize++; + if (slot.isClaimedOrThreadLocal()) { + inUse++; + } + } + return size - liveSize + inUse; + } } diff --git a/src/main/java/stormpot/ManagedPool.java b/src/main/java/stormpot/ManagedPool.java index 01045d35..739472ef 100644 --- a/src/main/java/stormpot/ManagedPool.java +++ b/src/main/java/stormpot/ManagedPool.java @@ -16,6 +16,7 @@ package stormpot; import javax.management.MXBean; +import java.util.concurrent.ConcurrentLinkedQueue; /** * This is the JMX management interface for Stormpot object pools. @@ -181,4 +182,36 @@ public interface ManagedPool { * the pool. */ double getDeallocationLatencyPercentile(double percentile); + + /** + * 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 {@code -1}. + * + * Unlike {@link #getAllocationCount()}, this returns only the number of objects currently in the pool, + * which typically would be the same as {@link #getTargetSize()}. + * + * @return The current approximate number of allocated objects. + */ + default int getCurrentAllocatedCount() { + return -1; + } + + /** + * 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 {@link ConcurrentLinkedQueue#iterator()}. + * + * The default implementation of this interface methods returns {@code -1}. + * + * @return number of objects currently in use + */ + default int getCurrentInUseCount() { + return -1; + } } diff --git a/src/main/java/stormpot/ThreadedAllocationController.java b/src/main/java/stormpot/ThreadedAllocationController.java index 087f67f7..5ddf71a9 100644 --- a/src/main/java/stormpot/ThreadedAllocationController.java +++ b/src/main/java/stormpot/ThreadedAllocationController.java @@ -67,4 +67,14 @@ public long getFailedAllocationCount() { public long countLeakedObjects() { return allocator.countLeakedObjects(); } + + @Override + public int allocatedSize() { + return allocator.allocatedSize(); + } + + @Override + public int inUse() { + return allocator.inUse(); + } } diff --git a/src/test/java/blackbox/AbstractPoolTest.java b/src/test/java/blackbox/AbstractPoolTest.java index 2f92287f..ea04c15e 100644 --- a/src/test/java/blackbox/AbstractPoolTest.java +++ b/src/test/java/blackbox/AbstractPoolTest.java @@ -913,6 +913,18 @@ void managedPoolMustGivePoolState() throws Exception { assertTrue(managedPool.isShutDown()); } + @Test + void managedPoolMustGiveNumberOfAllocatedAndInUseObjects() throws Exception { + createOneObjectPool(); + pool.claim(longTimeout).release(); // Ensure the single object is allocated + ManagedPool managedPool = pool.getManagedPool(); + assertThat(managedPool.getCurrentAllocatedCount()).isOne(); + assertThat(managedPool.getCurrentInUseCount()).isZero(); + T obj = pool.claim(longTimeout); + assertThat(managedPool.getCurrentInUseCount()).isOne(); + obj.release(); + } + @ParameterizedTest @EnumSource(Taps.class) void applyMustThrowOnNullTimeout(Taps taps) {