Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for loom #230

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions src/main/java/org/apache/commons/pool3/PooledObject.java
Original file line number Diff line number Diff line change
Expand Up @@ -255,4 +255,19 @@ default void setRequireFullStackTrace(final boolean requireFullStackTrace) {
*/
void use();

/**
* Acquires a lock on this PooledObject.
*/
@SuppressWarnings("no-ops")
default void lock() {

}

/**
* Release a lock on this PooledObject.
*/
@SuppressWarnings("no-ops")
default void unlock() {

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

import javax.management.InstanceAlreadyExistsException;
Expand Down Expand Up @@ -379,7 +380,7 @@ public String toString() {
final Object closeLock = new Object();
volatile boolean closed;

final Object evictionLock = new Object();
final ReentrantLock evictionLock = new ReentrantLock ();
private Evictor evictor; // @GuardedBy("evictionLock")
EvictionIterator evictionIterator; // @GuardedBy("evictionLock")

Expand Down Expand Up @@ -496,12 +497,15 @@ ArrayList<PooledObject<T>> createRemoveList(final AbandonedConfig abandonedConfi
final Instant timeout = Instant.now().minus(abandonedConfig.getRemoveAbandonedTimeoutDuration());
final ArrayList<PooledObject<T>> remove = new ArrayList<>();
allObjects.values().forEach(pooledObject -> {
synchronized (pooledObject) {
pooledObject.lock();
try {
if (pooledObject.getState() == PooledObjectState.ALLOCATED &&
pooledObject.getLastUsedInstant().compareTo(timeout) <= 0) {
pooledObject.markAbandoned();
remove.add(pooledObject);
}
} finally {
pooledObject.unlock();
}
});
return remove;
Expand Down Expand Up @@ -1215,11 +1219,14 @@ final void jmxUnregister() {
* @param pooledObject instance to return to the keyed pool
*/
protected void markReturningState(final PooledObject<T> pooledObject) {
synchronized (pooledObject) {
pooledObject.lock();
try {
if (pooledObject.getState() != PooledObjectState.ALLOCATED) {
throw new IllegalStateException("Object has already been returned to this pool or is invalid");
}
pooledObject.markReturning(); // Keep from being marked abandoned
} finally {
pooledObject.unlock();
}
}

Expand Down Expand Up @@ -1617,7 +1624,8 @@ public final void setTestWhileIdle(final boolean testWhileIdle) {
* @param delay duration before start and between eviction runs.
*/
final void startEvictor(final Duration delay) {
synchronized (evictionLock) {
evictionLock.lock();
try {
final boolean isPositiverDelay = PoolImplUtils.isPositive(delay);
if (evictor == null) { // Starting evictor for the first time or after a cancel
if (isPositiverDelay) { // Starting new evictor
Expand All @@ -1635,6 +1643,8 @@ final void startEvictor(final Duration delay) {
} else { // Stopping evictor
EvictionTimer.cancel(evictor, evictorShutdownTimeoutDuration, false);
}
} finally {
evictionLock.unlock();
}
}

Expand Down
147 changes: 99 additions & 48 deletions src/main/java/org/apache/commons/pool3/impl/DefaultPooledObject.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import java.time.Instant;
import java.util.Deque;
import java.util.Objects;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.commons.pool3.PooledObject;
import org.apache.commons.pool3.PooledObjectState;
Expand All @@ -44,6 +45,7 @@ public class DefaultPooledObject<T> implements PooledObject<T> {
private PooledObjectState state = PooledObjectState.IDLE; // @GuardedBy("this") to ensure transitions are valid
private final Clock systemClock = Clock.systemUTC();
private final Instant createInstant = now();
private final ReentrantLock lock = new ReentrantLock();

private volatile Instant lastBorrowInstant = createInstant;
private volatile Instant lastUseInstant = createInstant;
Expand All @@ -69,24 +71,29 @@ public DefaultPooledObject(final T object) {
* @return {@code true} if the original state was {@link PooledObjectState#IDLE IDLE}
*/
@Override
public synchronized boolean allocate() {
if (state == PooledObjectState.IDLE) {
state = PooledObjectState.ALLOCATED;
lastBorrowInstant = now();
lastUseInstant = lastBorrowInstant;
borrowedCount++;
if (logAbandoned) {
borrowedBy.fillInStackTrace();
public boolean allocate() {
lock();
try {
if (state == PooledObjectState.IDLE) {
state = PooledObjectState.ALLOCATED;
lastBorrowInstant = now();
lastUseInstant = lastBorrowInstant;
borrowedCount++;
if (logAbandoned) {
borrowedBy.fillInStackTrace();
}
return true;
}
return true;
}
if (state == PooledObjectState.EVICTION) {
// TODO Allocate anyway and ignore eviction test
state = PooledObjectState.EVICTION_RETURN_TO_HEAD;
if (state == PooledObjectState.EVICTION) {
// TODO Allocate anyway and ignore eviction test
state = PooledObjectState.EVICTION_RETURN_TO_HEAD;
}
// TODO if validating and testOnBorrow == true then pre-allocate for
// performance
return false;
} finally {
unlock();
}
// TODO if validating and testOnBorrow == true then pre-allocate for
// performance
return false;
}

@Override
Expand All @@ -111,30 +118,38 @@ public int compareTo(final PooledObject<T> other) {
* or {@link PooledObjectState#RETURNING RETURNING}.
*/
@Override
public synchronized boolean deallocate() {
if (state == PooledObjectState.ALLOCATED || state == PooledObjectState.RETURNING) {
state = PooledObjectState.IDLE;
lastReturnInstant = now();
borrowedBy.clear();
return true;
public boolean deallocate() {
lock();
try {
if (state == PooledObjectState.ALLOCATED || state == PooledObjectState.RETURNING) {
state = PooledObjectState.IDLE;
lastReturnInstant = now();
borrowedBy.clear();
return true;
}
return false;
} finally {
unlock();
}

return false;
}

@Override
public synchronized boolean endEvictionTest(
public boolean endEvictionTest(
final Deque<PooledObject<T>> idleQueue) {
if (state == PooledObjectState.EVICTION) {
state = PooledObjectState.IDLE;
return true;
}
if (state == PooledObjectState.EVICTION_RETURN_TO_HEAD) {
state = PooledObjectState.IDLE;
idleQueue.offerFirst(this);
lock();
try {
if (state == PooledObjectState.EVICTION) {
state = PooledObjectState.IDLE;
return true;
}
if (state == PooledObjectState.EVICTION_RETURN_TO_HEAD) {
state = PooledObjectState.IDLE;
idleQueue.offerFirst(this);
}
return false;
} finally {
unlock();
}

return false;
}

/**
Expand Down Expand Up @@ -198,32 +213,52 @@ public T getObject() {
* @return state
*/
@Override
public synchronized PooledObjectState getState() {
return state;
public PooledObjectState getState() {
lock();
try {
return state;
} finally {
unlock();
}
}

/**
* Sets the state to {@link PooledObjectState#INVALID INVALID}.
*/
@Override
public synchronized void invalidate() {
state = PooledObjectState.INVALID;
public void invalidate() {
lock();
try {
state = PooledObjectState.INVALID;
} finally {
unlock();
}
}

/**
* Marks the pooled object as {@link PooledObjectState#ABANDONED ABANDONED}.
*/
@Override
public synchronized void markAbandoned() {
state = PooledObjectState.ABANDONED;
public void markAbandoned() {
lock();
try {
state = PooledObjectState.ABANDONED;
} finally {
unlock();
}
}

/**
* Marks the pooled object as {@link PooledObjectState#RETURNING RETURNING}.
*/
@Override
public synchronized void markReturning() {
state = PooledObjectState.RETURNING;
public void markReturning() {
lock();
try {
state = PooledObjectState.RETURNING;
} finally {
unlock();
}
}

/**
Expand Down Expand Up @@ -269,12 +304,17 @@ public void setRequireFullStackTrace(final boolean requireFullStackTrace) {
}

@Override
public synchronized boolean startEvictionTest() {
if (state == PooledObjectState.IDLE) {
state = PooledObjectState.EVICTION;
return true;
public boolean startEvictionTest() {
lock();
try {
if (state == PooledObjectState.IDLE) {
state = PooledObjectState.EVICTION;
return true;
}
return false;
} finally {
unlock();
}
return false;
}

@Override
Expand All @@ -283,8 +323,11 @@ public String toString() {
result.append("Object: ");
result.append(Objects.toString(object));
result.append(", State: ");
synchronized (this) {
lock();
try {
result.append(state.toString());
} finally {
unlock();
}
return result.toString();
// TODO add other attributes
Expand All @@ -296,4 +339,12 @@ public void use() {
usedBy.fillInStackTrace();
}

public void lock() {
lock.lock();
}

public void unlock() {
lock.unlock();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -881,14 +881,17 @@ private boolean destroy(final K key, final PooledObject<T> toDestroy, final bool

try {
boolean isIdle;
synchronized (toDestroy) {
toDestroy.lock();
try {
// Check idle state directly
isIdle = toDestroy.getState().equals(PooledObjectState.IDLE);
// If idle, not under eviction test, or always is true, remove instance,
// updating isIdle if instance is found in idle objects
if (isIdle || always) {
isIdle = objectDeque.getIdleObjects().remove(toDestroy);
}
} finally {
toDestroy.unlock();
}
if (isIdle || always) {
objectDeque.getAllObjects().remove(IdentityWrapper.on(toDestroy));
Expand Down Expand Up @@ -979,7 +982,8 @@ public void evict() throws E {
PooledObject<T> underTest = null;
final EvictionPolicy<T> evictionPolicy = getEvictionPolicy();

synchronized (evictionLock) {
evictionLock.lock();
try {
final EvictionConfig evictionConfig = new EvictionConfig(
getMinEvictableIdleDuration(),
getSoftMinEvictableIdleDuration(),
Expand Down Expand Up @@ -1101,6 +1105,8 @@ public void evict() throws E {
// states are used
}
}
} finally {
evictionLock.unlock();
}
}
final AbandonedConfig ac = this.abandonedConfig;
Expand Down Expand Up @@ -1333,11 +1339,14 @@ public void invalidateObject(final K key, final T obj, final DestroyMode destroy
if (p == null) {
throw new IllegalStateException(appendStats("Object not currently part of this pool"));
}
synchronized (p) {
p.lock();
try {
if (p.getState() != PooledObjectState.INVALID) {
destroy(key, p, true, destroyMode);
reuseCapacity();
}
} finally {
p.unlock();
}
}

Expand Down
Loading