Skip to content

Commit

Permalink
sweep the entries list when the max usage count is changed
Browse files Browse the repository at this point in the history
Signed-off-by: Ludovic Orban <lorban@bitronix.be>
  • Loading branch information
lorban committed Dec 1, 2020
1 parent 0ff1bfd commit 44e6f4a
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 7 deletions.
50 changes: 43 additions & 7 deletions jetty-util/src/main/java/org/eclipse/jetty/util/Pool.java
Expand Up @@ -29,6 +29,7 @@
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.eclipse.jetty.util.component.Dumpable;
import org.eclipse.jetty.util.component.DumpableCollection;
Expand Down Expand Up @@ -166,16 +167,42 @@ public final void setMaxMultiplex(int maxMultiplex)
this.maxMultiplex = maxMultiplex;
}

/**
* Get the maximum number of times the entries of the pool
* can be acquired.
* @return the max usage count.
*/
public int getMaxUsageCount()
{
return maxUsageCount;
}

/**
* Change the max usage count of the pool's entries. All existing
* idle entries over this new max usage are removed and closed.
* @param maxUsageCount the max usage count.
*/
public final void setMaxUsageCount(int maxUsageCount)
{
if (maxUsageCount == 0)
throw new IllegalArgumentException("Max usage count must be != 0");
this.maxUsageCount = maxUsageCount;

// Iterate the entries, remove overused ones and collect a list of the closeable removed ones.
List<Closeable> copy;
try (Locker.Lock l = locker.lock())
{
if (closed)
return;

copy = entries.stream()
.filter(entry -> entry.isIdleAndOverUsed() && remove(entry) && entry.pooled instanceof Closeable)
.map(entry -> (Closeable)entry.pooled)
.collect(Collectors.toList());
}

// Iterate the copy and close the collected entries.
copy.forEach(IO::close);
}

/**
Expand Down Expand Up @@ -571,13 +598,6 @@ boolean tryRelease()
return !(overUsed && newMultiplexingCount == 0);
}

public boolean isOverUsed()
{
int currentMaxUsageCount = maxUsageCount;
int usageCount = state.getHi();
return currentMaxUsageCount > 0 && usageCount >= currentMaxUsageCount;
}

/**
* Try to mark the entry as removed.
* @return true if the entry has to be removed from the containing pool, false otherwise.
Expand Down Expand Up @@ -618,6 +638,22 @@ public boolean isInUse()
return AtomicBiInteger.getHi(encoded) >= 0 && AtomicBiInteger.getLo(encoded) > 0;
}

public boolean isOverUsed()
{
int currentMaxUsageCount = maxUsageCount;
int usageCount = state.getHi();
return currentMaxUsageCount > 0 && usageCount >= currentMaxUsageCount;
}

boolean isIdleAndOverUsed()
{
int currentMaxUsageCount = maxUsageCount;
long encoded = state.get();
int usageCount = AtomicBiInteger.getHi(encoded);
int multiplexCount = AtomicBiInteger.getLo(encoded);
return currentMaxUsageCount > 0 && usageCount >= currentMaxUsageCount && multiplexCount == 0;
}

public int getUsageCount()
{
return Math.max(state.getHi(), 0);
Expand Down
18 changes: 18 additions & 0 deletions jetty-util/src/test/java/org/eclipse/jetty/util/PoolTest.java
Expand Up @@ -572,6 +572,24 @@ public void testDynamicMaxUsageCountChangeOverflowMaxInt(Factory factory)
assertThat(acquired2, nullValue());
}

@ParameterizedTest
@MethodSource(value = "strategy")
public void testDynamicMaxUsageCountChangeSweep(Factory factory)
{
Pool<String> pool = factory.getPool(2);
Pool<String>.Entry entry1 = pool.reserve(-1);
entry1.enable("aaa", false);
Pool<String>.Entry entry2 = pool.reserve(-1);
entry2.enable("bbb", false);

Pool<String>.Entry acquired1 = pool.acquire();
assertThat(acquired1, notNullValue());
assertThat(pool.release(acquired1), is(true));

pool.setMaxUsageCount(1);
assertThat(pool.size(), is(1));
}

@Test
public void testConfigLimits()
{
Expand Down

0 comments on commit 44e6f4a

Please sign in to comment.