From ff4ef533a558a7a176e4649ce5c6d54fb2383217 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Sat, 11 Jul 2020 16:39:14 -0400 Subject: [PATCH 1/8] Redo this class after Rob Tompkins found a bug. Much simpler now as well. --- .../concurrent/locks/LockingVisitors.java | 457 +++++------------- .../concurrent/locks/LockingVisitorsTest.java | 4 +- 2 files changed, 113 insertions(+), 348 deletions(-) diff --git a/src/main/java/org/apache/commons/lang3/concurrent/locks/LockingVisitors.java b/src/main/java/org/apache/commons/lang3/concurrent/locks/LockingVisitors.java index feccb423f..41164c006 100644 --- a/src/main/java/org/apache/commons/lang3/concurrent/locks/LockingVisitors.java +++ b/src/main/java/org/apache/commons/lang3/concurrent/locks/LockingVisitors.java @@ -21,7 +21,6 @@ import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.StampedLock; -import java.util.function.LongSupplier; import java.util.function.Supplier; import org.apache.commons.lang3.function.Failable; @@ -76,18 +75,26 @@ import org.apache.commons.lang3.function.FailableFunction; public class LockingVisitors { /** - * Wraps a domain object for access by lambdas. + * Wraps a domain object and a lock for access by lambdas. * * @param the wrapped object type. + * @param the wrapped lock type. */ - public abstract static class AbstractLockVisitor { - protected AbstractLockVisitor(final O object) { + public static class LockVisitor { + + private final L lock; + + private final O object; + private final Supplier readLockSupplier; + private final Supplier writeLockSupplier; + protected LockVisitor(final O object, L lock, Supplier readLockSupplier, Supplier writeLockSupplier) { super(); this.object = Objects.requireNonNull(object, "object"); + this.lock = Objects.requireNonNull(lock, "lock"); + this.readLockSupplier = Objects.requireNonNull(readLockSupplier, "readLockSupplier"); + this.writeLockSupplier = Objects.requireNonNull(writeLockSupplier, "writeLockSupplier"); } - protected final O object; - /** * Provides read (shared, non-exclusive) access to the locked (hidden) object. More precisely, what the method * will do (in the given order): @@ -104,7 +111,9 @@ public class LockingVisitors { * @see #acceptWriteLocked(FailableConsumer) * @see #applyReadLocked(FailableFunction) */ - public abstract void acceptReadLocked(FailableConsumer consumer); + public void acceptReadLocked(FailableConsumer consumer) { + lockAcceptUnlock(readLockSupplier, consumer); + } /** * Provides write (exclusive) access to the locked (hidden) object. More precisely, what the method will do (in @@ -122,7 +131,9 @@ public class LockingVisitors { * @see #acceptReadLocked(FailableConsumer) * @see #applyWriteLocked(FailableFunction) */ - public abstract void acceptWriteLocked(FailableConsumer consumer); + public void acceptWriteLocked(final FailableConsumer consumer) { + lockAcceptUnlock(writeLockSupplier, consumer); + } /** * Provides read (shared, non-exclusive) access to the locked (hidden) object for the purpose of computing a @@ -158,7 +169,9 @@ public class LockingVisitors { * @see #acceptReadLocked(FailableConsumer) * @see #applyWriteLocked(FailableFunction) */ - public abstract T applyReadLocked(FailableFunction function); + public T applyReadLocked(FailableFunction function) { + return lockApplyUnlock(readLockSupplier, function); + } /** * Provides write (exclusive) access to the locked (hidden) object for the purpose of computing a result object. @@ -182,7 +195,77 @@ public class LockingVisitors { * @see #acceptReadLocked(FailableConsumer) * @see #applyWriteLocked(FailableFunction) */ - public abstract T applyWriteLocked(FailableFunction function); + public T applyWriteLocked(final FailableFunction function) { + return lockApplyUnlock(writeLockSupplier, function); + } + + /** + * Gets the lock. + * + * @return the lock. + */ + public L getLock() { + return lock; + } + + /** + * Gets the object. + * + * @return the object. + */ + public O getObject() { + return object; + } + + /** + * This method provides the default implementation for {@link #acceptReadLocked(FailableConsumer)}, and + * {@link #acceptWriteLocked(FailableConsumer)}. + * + * @param lockSupplier A supplier for the lock. (This provides, in fact, a long, because a {@link StampedLock} is used + * internally.) + * @param consumer The consumer, which is to be given access to the locked (hidden) object, which will be passed + * as a parameter. + * @see #acceptReadLocked(FailableConsumer) + * @see #acceptWriteLocked(FailableConsumer) + */ + protected void lockAcceptUnlock(final Supplier lockSupplier, final FailableConsumer consumer) { + final Lock lock = lockSupplier.get(); + lock.lock(); + try { + consumer.accept(object); + } catch (Throwable t) { + throw Failable.rethrow(t); + } finally { + lock.unlock(); + } + } + + /** + * This method provides the actual implementation for {@link #applyReadLocked(FailableFunction)}, and + * {@link #applyWriteLocked(FailableFunction)}. + * + * @param The result type (both the functions, and this method's.) + * @param lockSupplier A supplier for the lock. (This provides, in fact, a long, because a {@link StampedLock} is used + * internally.) + * @param function The function, which is being invoked to compute the result object. This function will receive + * the locked (hidden) object as a parameter. + * @return The result object, which has been returned by the functions invocation. + * @throws IllegalStateException The result object would be, in fact, the hidden object. This would extend + * access to the hidden object beyond this methods lifetime and will therefore be prevented. + * @see #applyReadLocked(FailableFunction) + * @see #applyWriteLocked(FailableFunction) + */ + protected T lockApplyUnlock(final Supplier lockSupplier, final FailableFunction function) { + final Lock lock = lockSupplier.get(); + lock.lock(); + try { + return function.apply(object); + } catch (Throwable t) { + throw Failable.rethrow(t); + } finally { + lock.unlock(); + } + } } @@ -195,8 +278,7 @@ public class LockingVisitors { * * @param The locked (hidden) objects type. */ - public static class ReadWriteLockVisitor extends AbstractLockVisitor { - private final ReadWriteLock readWriteLock; + public static class ReadWriteLockVisitor extends LockVisitor { /** * Creates a new instance with the given locked object. This constructor is supposed to be used for subclassing @@ -205,167 +287,8 @@ public class LockingVisitors { * @param object The locked (hidden) object. The caller is supposed to drop all references to the locked object. * @param readWriteLock the lock to use. */ - public ReadWriteLockVisitor(final O object, final ReadWriteLock readWriteLock) { - super(object); - this.readWriteLock = readWriteLock; - } - - /** - * Provides read (shared, non-exclusive) access to the locked (hidden) object. More precisely, what the method - * will do (in the given order): - *
    - *
  1. Obtain a read (shared) lock on the locked (hidden) object. The current thread may block, until such a - * lock is granted.
  2. - *
  3. Invokes the given {@link FailableConsumer consumer}, passing the locked object as the parameter.
  4. - *
  5. Release the lock, as soon as the consumers invocation is done. If the invocation results in an error, the - * lock will be released anyways.
  6. - *
- * - * @param consumer The consumer, which is being invoked to use the hidden object, which will be passed as the - * consumers parameter. - * @see #acceptWriteLocked(FailableConsumer) - * @see #applyReadLocked(FailableFunction) - */ - @Override - public void acceptReadLocked(final FailableConsumer consumer) { - lockAcceptUnlock(() -> readWriteLock.readLock(), consumer); - } - - /** - * Provides write (exclusive) access to the locked (hidden) object. More precisely, what the method will do (in - * the given order): - *
    - *
  1. Obtain a write (shared) lock on the locked (hidden) object. The current thread may block, until such a - * lock is granted.
  2. - *
  3. Invokes the given {@link FailableConsumer consumer}, passing the locked object as the parameter.
  4. - *
  5. Release the lock, as soon as the consumers invocation is done. If the invocation results in an error, the - * lock will be released anyways.
  6. - *
- * - * @param consumer The consumer, which is being invoked to use the hidden object, which will be passed as the - * consumers parameter. - * @see #acceptReadLocked(FailableConsumer) - * @see #applyWriteLocked(FailableFunction) - */ - @Override - public void acceptWriteLocked(final FailableConsumer consumer) { - lockAcceptUnlock(() -> readWriteLock.writeLock(), consumer); - } - - /** - * Provides read (shared, non-exclusive) access to the locked (hidden) object for the purpose of computing a - * result object. More precisely, what the method will do (in the given order): - *
    - *
  1. Obtain a read (shared) lock on the locked (hidden) object. The current thread may block, until such a - * lock is granted.
  2. - *
  3. Invokes the given {@link FailableFunction function}, passing the locked object as the parameter, - * receiving the functions result.
  4. - *
  5. Release the lock, as soon as the consumers invocation is done. If the invocation results in an error, the - * lock will be released anyways.
  6. - *
  7. Return the result object, that has been received from the functions invocation.
  8. - *
- * - * Example: Suggest, that the hidden object is a list, and we wish to know the current size of the - * list. This might be achieved with the following: - * - *
-         * private Lock<List<Object>> listLock;
-         *
-         * public int getCurrentListSize() {
-         *     final Integer sizeInteger = listLock.applyReadLocked((list) -> Integer.valueOf(list.size));
-         *     return sizeInteger.intValue();
-         * }
-         * 
- * - * @param The result type (both the functions, and this method's.) - * @param function The function, which is being invoked to compute the result. The function will receive the - * hidden object. - * @return The result object, which has been returned by the functions invocation. - * @throws IllegalStateException The result object would be, in fact, the hidden object. This would extend - * access to the hidden object beyond this methods lifetime and will therefore be prevented. - * @see #acceptReadLocked(FailableConsumer) - * @see #applyWriteLocked(FailableFunction) - */ - @Override - public T applyReadLocked(final FailableFunction function) { - return lockApplyUnlock(() -> readWriteLock.readLock(), function); - } - - /** - * Provides write (exclusive) access to the locked (hidden) object for the purpose of computing a result object. - * More precisely, what the method will do (in the given order): - *
    - *
  1. Obtain a read (shared) lock on the locked (hidden) object. The current thread may block, until such a - * lock is granted.
  2. - *
  3. Invokes the given {@link FailableFunction function}, passing the locked object as the parameter, - * receiving the functions result.
  4. - *
  5. Release the lock, as soon as the consumers invocation is done. If the invocation results in an error, the - * lock will be released anyways.
  6. - *
  7. Return the result object, that has been received from the functions invocation.
  8. - *
- * - * @param The result type (both the functions, and this method's.) - * @param function The function, which is being invoked to compute the result. The function will receive the - * hidden object. - * @return The result object, which has been returned by the functions invocation. - * @throws IllegalStateException The result object would be, in fact, the hidden object. This would extend - * access to the hidden object beyond this methods lifetime and will therefore be prevented. - * @see #acceptReadLocked(FailableConsumer) - * @see #applyWriteLocked(FailableFunction) - */ - @Override - public T applyWriteLocked(final FailableFunction function) { - return lockApplyUnlock(() -> readWriteLock.writeLock(), function); - } - - /** - * This method provides the actual implementation for {@link #acceptReadLocked(FailableConsumer)}, and - * {@link #acceptWriteLocked(FailableConsumer)}. - * - * @param lock A supplier for the lock. (This provides, in fact, a long, because a {@link StampedLock} is used - * internally.) - * @param consumer The consumer, which is to be given access to the locked (hidden) object, which will be passed - * as a parameter. - * @see #acceptReadLocked(FailableConsumer) - * @see #acceptWriteLocked(FailableConsumer) - * @see #lockApplyUnlock(LongSupplier, FailableFunction) - */ - private void lockAcceptUnlock(final Supplier lockSupplier, final FailableConsumer consumer) { - final Lock lock = lockSupplier.get(); - try { - consumer.accept(object); - } catch (Throwable t) { - throw Failable.rethrow(t); - } finally { - lock.unlock(); - } - } - - /** - * This method provides the actual implementation for {@link #applyReadLocked(FailableFunction)}, and - * {@link #applyWriteLocked(FailableFunction)}. - * - * @param The result type (both the functions, and this method's.) - * @param lock A supplier for the lock. (This provides, in fact, a long, because a {@link StampedLock} is used - * internally.) - * @param function The function, which is being invoked to compute the result object. This function will receive - * the locked (hidden) object as a parameter. - * @return The result object, which has been returned by the functions invocation. - * @throws IllegalStateException The result object would be, in fact, the hidden object. This would extend - * access to the hidden object beyond this methods lifetime and will therefore be prevented. - * @see #applyReadLocked(FailableFunction) - * @see #applyWriteLocked(FailableFunction) - * @see #lockAcceptUnlock(LongSupplier, FailableConsumer) - */ - private T lockApplyUnlock(final Supplier lockSupplier, final FailableFunction function) { - final Lock lock = lockSupplier.get(); - try { - return function.apply(object); - } catch (Throwable t) { - throw Failable.rethrow(t); - } finally { - lock.unlock(); - } + protected ReadWriteLockVisitor(final O object, final ReadWriteLock readWriteLock) { + super(object, readWriteLock, readWriteLock::readLock, readWriteLock::writeLock); } } @@ -378,187 +301,18 @@ public class LockingVisitors { * * @param The locked (hidden) objects type. */ - public static class StampedLockVisitor extends AbstractLockVisitor { - private final StampedLock stampedLock = new StampedLock(); + public static class StampedLockVisitor extends LockVisitor { /** * Creates a new instance with the given locked object. This constructor is supposed to be used for subclassing * only. In general, it is suggested to use {@link LockingVisitors#stampedLockVisitor(Object)} instead. * * @param object The locked (hidden) object. The caller is supposed to drop all references to the locked object. + * @param stampedLock the lock to use. */ - public StampedLockVisitor(final O object) { - super(object); + protected StampedLockVisitor(final O object, StampedLock stampedLock) { + super(object, stampedLock, stampedLock::asReadLock, stampedLock::asWriteLock); } - - /** - * Provides read (shared, non-exclusive) access to the locked (hidden) object. More precisely, what the method - * will do (in the given order): - *
    - *
  1. Obtain a read (shared) lock on the locked (hidden) object. The current thread may block, until such a - * lock is granted.
  2. - *
  3. Invokes the given {@link FailableConsumer consumer}, passing the locked object as the parameter.
  4. - *
  5. Release the lock, as soon as the consumers invocation is done. If the invocation results in an error, the - * lock will be released anyways.
  6. - *
- * - * @param consumer The consumer, which is being invoked to use the hidden object, which will be passed as the - * consumers parameter. - * @see #acceptWriteLocked(FailableConsumer) - * @see #applyReadLocked(FailableFunction) - */ - @Override - public void acceptReadLocked(final FailableConsumer consumer) { - lockAcceptUnlock(() -> stampedLock.readLock(), consumer); - } - - /** - * Provides write (exclusive) access to the locked (hidden) object. More precisely, what the method will do (in - * the given order): - *
    - *
  1. Obtain a write (shared) lock on the locked (hidden) object. The current thread may block, until such a - * lock is granted.
  2. - *
  3. Invokes the given {@link FailableConsumer consumer}, passing the locked object as the parameter.
  4. - *
  5. Release the lock, as soon as the consumers invocation is done. If the invocation results in an error, the - * lock will be released anyways.
  6. - *
- * - * @param consumer The consumer, which is being invoked to use the hidden object, which will be passed as the - * consumers parameter. - * @see #acceptReadLocked(FailableConsumer) - * @see #applyWriteLocked(FailableFunction) - */ - @Override - public void acceptWriteLocked(final FailableConsumer consumer) { - lockAcceptUnlock(() -> stampedLock.writeLock(), consumer); - } - - /** - * Provides read (shared, non-exclusive) access to the locked (hidden) object for the purpose of computing a - * result object. More precisely, what the method will do (in the given order): - *
    - *
  1. Obtain a read (shared) lock on the locked (hidden) object. The current thread may block, until such a - * lock is granted.
  2. - *
  3. Invokes the given {@link FailableFunction function}, passing the locked object as the parameter, - * receiving the functions result.
  4. - *
  5. Release the lock, as soon as the consumers invocation is done. If the invocation results in an error, the - * lock will be released anyways.
  6. - *
  7. Return the result object, that has been received from the functions invocation.
  8. - *
- * - * Example: Suggest, that the hidden object is a list, and we wish to know the current size of the - * list. This might be achieved with the following: - * - *
-         * private Lock<List<Object>> listLock;
-         *
-         * public int getCurrentListSize() {
-         *     final Integer sizeInteger = listLock.applyReadLocked((list) -> Integer.valueOf(list.size));
-         *     return sizeInteger.intValue();
-         * }
-         * 
- * - * @param The result type (both the functions, and this method's.) - * @param function The function, which is being invoked to compute the result. The function will receive the - * hidden object. - * @return The result object, which has been returned by the functions invocation. - * @throws IllegalStateException The result object would be, in fact, the hidden object. This would extend - * access to the hidden object beyond this methods lifetime and will therefore be prevented. - * @see #acceptReadLocked(FailableConsumer) - * @see #applyWriteLocked(FailableFunction) - */ - @Override - public T applyReadLocked(final FailableFunction function) { - return lockApplyUnlock(() -> stampedLock.readLock(), function); - } - - /** - * Provides write (exclusive) access to the locked (hidden) object for the purpose of computing a result object. - * More precisely, what the method will do (in the given order): - *
    - *
  1. Obtain a read (shared) lock on the locked (hidden) object. The current thread may block, until such a - * lock is granted.
  2. - *
  3. Invokes the given {@link FailableFunction function}, passing the locked object as the parameter, - * receiving the functions result.
  4. - *
  5. Release the lock, as soon as the consumers invocation is done. If the invocation results in an error, the - * lock will be released anyways.
  6. - *
  7. Return the result object, that has been received from the functions invocation.
  8. - *
- * - * @param The result type (both the functions, and this method's.) - * @param function The function, which is being invoked to compute the result. The function will receive the - * hidden object. - * @return The result object, which has been returned by the functions invocation. - * @throws IllegalStateException The result object would be, in fact, the hidden object. This would extend - * access to the hidden object beyond this methods lifetime and will therefore be prevented. - * @see #acceptReadLocked(FailableConsumer) - * @see #applyWriteLocked(FailableFunction) - */ - @Override - public T applyWriteLocked(final FailableFunction function) { - return lockApplyUnlock(() -> stampedLock.writeLock(), function); - } - - /** - * This method provides the actual implementation for {@link #acceptReadLocked(FailableConsumer)}, and - * {@link #acceptWriteLocked(FailableConsumer)}. - * - * @param stampSupplier A supplier for the lock. (This provides, in fact, a long, because a {@link StampedLock} - * is used internally.) - * @param consumer The consumer, which is to be given access to the locked (hidden) object, which will be passed - * as a parameter. - * @see #acceptReadLocked(FailableConsumer) - * @see #acceptWriteLocked(FailableConsumer) - * @see #lockApplyUnlock(LongSupplier, FailableFunction) - */ - private void lockAcceptUnlock(final LongSupplier stampSupplier, final FailableConsumer consumer) { - final long stamp = stampSupplier.getAsLong(); - try { - consumer.accept(object); - } catch (Throwable t) { - throw Failable.rethrow(t); - } finally { - stampedLock.unlock(stamp); - } - } - - /** - * This method provides the actual implementation for {@link #applyReadLocked(FailableFunction)}, and - * {@link #applyWriteLocked(FailableFunction)}. - * - * @param The result type (both the functions, and this method's.) - * @param stampSupplier A supplier for the lock. (This provides, in fact, a long, because a {@link StampedLock} - * is used internally.) - * @param function The function, which is being invoked to compute the result object. This function will receive - * the locked (hidden) object as a parameter. - * @return The result object, which has been returned by the functions invocation. - * @throws IllegalStateException The result object would be, in fact, the hidden object. This would extend - * access to the hidden object beyond this methods lifetime and will therefore be prevented. - * @see #applyReadLocked(FailableFunction) - * @see #applyWriteLocked(FailableFunction) - * @see #lockAcceptUnlock(LongSupplier, FailableConsumer) - */ - private T lockApplyUnlock(final LongSupplier stampSupplier, final FailableFunction function) { - final long stamp = stampSupplier.getAsLong(); - try { - return function.apply(object); - } catch (Throwable t) { - throw Failable.rethrow(t); - } finally { - stampedLock.unlock(stamp); - } - } - } - - /** - * Creates a new instance of {@link StampedLockVisitor} with the given (hidden) object. - * - * @param The locked objects type. - * @param object The locked (hidden) object. - * @return The created instance, a {@link StampedLockVisitor lock} for the given object. - */ - public static StampedLockVisitor stampedLockVisitor(final O object) { - return new LockingVisitors.StampedLockVisitor<>(object); } /** @@ -572,4 +326,15 @@ public class LockingVisitors { return new LockingVisitors.ReadWriteLockVisitor<>(object, new ReentrantReadWriteLock()); } + /** + * Creates a new instance of {@link StampedLockVisitor} with the given (hidden) object. + * + * @param The locked objects type. + * @param object The locked (hidden) object. + * @return The created instance, a {@link StampedLockVisitor lock} for the given object. + */ + public static StampedLockVisitor stampedLockVisitor(final O object) { + return new LockingVisitors.StampedLockVisitor<>(object, new StampedLock()); + } + } diff --git a/src/test/java/org/apache/commons/lang3/concurrent/locks/LockingVisitorsTest.java b/src/test/java/org/apache/commons/lang3/concurrent/locks/LockingVisitorsTest.java index bd3a7ffbf..df3982ac7 100644 --- a/src/test/java/org/apache/commons/lang3/concurrent/locks/LockingVisitorsTest.java +++ b/src/test/java/org/apache/commons/lang3/concurrent/locks/LockingVisitorsTest.java @@ -22,7 +22,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.function.LongConsumer; -import org.apache.commons.lang3.concurrent.locks.LockingVisitors.AbstractLockVisitor; +import org.apache.commons.lang3.concurrent.locks.LockingVisitors.LockVisitor; import org.apache.commons.lang3.concurrent.locks.LockingVisitors.StampedLockVisitor; import org.apache.commons.lang3.function.FailableConsumer; import org.junit.jupiter.api.Test; @@ -91,7 +91,7 @@ public class LockingVisitorsTest { } private void runTest(final long delayMillis, final boolean exclusiveLock, final LongConsumer runTimeCheck, - boolean[] booleanValues, AbstractLockVisitor visitor) throws InterruptedException { + boolean[] booleanValues, LockVisitor visitor) throws InterruptedException { final boolean[] runningValues = new boolean[10]; final long startTime = System.currentTimeMillis(); From 3e86896c13aa51480fbde2a15b2beada9b6f0008 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Sat, 11 Jul 2020 16:54:07 -0400 Subject: [PATCH 2/8] Format. --- .../apache/commons/lang3/concurrent/locks/LockingVisitors.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/apache/commons/lang3/concurrent/locks/LockingVisitors.java b/src/main/java/org/apache/commons/lang3/concurrent/locks/LockingVisitors.java index 41164c006..4663e1374 100644 --- a/src/main/java/org/apache/commons/lang3/concurrent/locks/LockingVisitors.java +++ b/src/main/java/org/apache/commons/lang3/concurrent/locks/LockingVisitors.java @@ -87,6 +87,7 @@ public class LockingVisitors { private final O object; private final Supplier readLockSupplier; private final Supplier writeLockSupplier; + protected LockVisitor(final O object, L lock, Supplier readLockSupplier, Supplier writeLockSupplier) { super(); this.object = Objects.requireNonNull(object, "object"); From 5283344927583f0894b79ae93515fd27ec5f9180 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Sat, 11 Jul 2020 17:16:31 -0400 Subject: [PATCH 3/8] Added missing Javadoc and tweaked another Javadoc. --- .../concurrent/locks/LockingVisitors.java | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/apache/commons/lang3/concurrent/locks/LockingVisitors.java b/src/main/java/org/apache/commons/lang3/concurrent/locks/LockingVisitors.java index 4663e1374..62868736c 100644 --- a/src/main/java/org/apache/commons/lang3/concurrent/locks/LockingVisitors.java +++ b/src/main/java/org/apache/commons/lang3/concurrent/locks/LockingVisitors.java @@ -82,12 +82,35 @@ public class LockingVisitors { */ public static class LockVisitor { + /** + * The lock object, untyped, since, for example {@link StampedLock} does not implement a locking interface in + * Java 8. + */ private final L lock; - + + /** + * The guarded object. + */ private final O object; + + /** + * Supplies the read lock, usually from the lock object. + */ private final Supplier readLockSupplier; + + /** + * Supplies the write lock, usually from the lock object. + */ private final Supplier writeLockSupplier; + /** + * Constructs an instance. + * + * @param object The object to guard. + * @param lock The locking object. + * @param readLockSupplier Supplies the read lock, usually from the lock object. + * @param writeLockSupplier Supplies the write lock, usually from the lock object. + */ protected LockVisitor(final O object, L lock, Supplier readLockSupplier, Supplier writeLockSupplier) { super(); this.object = Objects.requireNonNull(object, "object"); @@ -210,7 +233,7 @@ public class LockingVisitors { } /** - * Gets the object. + * Gets the guarded object. * * @return the object. */ From 92d6be66ff13ff452a06f8e2b7d4cc4b056e74dd Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Sat, 11 Jul 2020 17:21:25 -0400 Subject: [PATCH 4/8] Whitespace and lambda clean ups. --- .../commons/lang3/concurrent/locks/LockingVisitors.java | 4 ++-- .../commons/lang3/concurrent/locks/LockingVisitorsTest.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/apache/commons/lang3/concurrent/locks/LockingVisitors.java b/src/main/java/org/apache/commons/lang3/concurrent/locks/LockingVisitors.java index 62868736c..490447232 100644 --- a/src/main/java/org/apache/commons/lang3/concurrent/locks/LockingVisitors.java +++ b/src/main/java/org/apache/commons/lang3/concurrent/locks/LockingVisitors.java @@ -87,12 +87,12 @@ public class LockingVisitors { * Java 8. */ private final L lock; - + /** * The guarded object. */ private final O object; - + /** * Supplies the read lock, usually from the lock object. */ diff --git a/src/test/java/org/apache/commons/lang3/concurrent/locks/LockingVisitorsTest.java b/src/test/java/org/apache/commons/lang3/concurrent/locks/LockingVisitorsTest.java index df3982ac7..aba1d20bb 100644 --- a/src/test/java/org/apache/commons/lang3/concurrent/locks/LockingVisitorsTest.java +++ b/src/test/java/org/apache/commons/lang3/concurrent/locks/LockingVisitorsTest.java @@ -80,11 +80,11 @@ public class LockingVisitorsTest { public void testResultValidation() { final Object hidden = new Object(); final StampedLockVisitor lock = LockingVisitors.stampedLockVisitor(hidden); - final Object o1 = lock.applyReadLocked((h) -> { + final Object o1 = lock.applyReadLocked(h -> { return new Object(); }); assertNotNull(o1); assertNotSame(hidden, o1); - final Object o2 = lock.applyWriteLocked((h) -> { + final Object o2 = lock.applyWriteLocked(h -> { return new Object(); }); assertNotNull(o2); assertNotSame(hidden, o2); From 669f6833e3356f210dd463c56d2f6544bda7439b Mon Sep 17 00:00:00 2001 From: Rob Tompkins Date: Sat, 11 Jul 2020 17:24:29 -0400 Subject: [PATCH 5/8] (docs) formatting nit --- .../apache/commons/lang3/concurrent/locks/LockingVisitors.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/apache/commons/lang3/concurrent/locks/LockingVisitors.java b/src/main/java/org/apache/commons/lang3/concurrent/locks/LockingVisitors.java index 490447232..c3dce37ea 100644 --- a/src/main/java/org/apache/commons/lang3/concurrent/locks/LockingVisitors.java +++ b/src/main/java/org/apache/commons/lang3/concurrent/locks/LockingVisitors.java @@ -32,7 +32,7 @@ import org.apache.commons.lang3.function.FailableFunction; * objects are an alternative to synchronization. * * Locking is preferable, if there is a distinction between read access (multiple threads may have read access - * concurrently), and write access (only one thread may have write access at any given time. In comparison, + * concurrently), and write access (only one thread may have write access at any given time). In comparison, * synchronization doesn't support read access, because synchronized access is exclusive. * * Using this class is fairly straightforward: From 19f5632919d500f56234674846ca05918f91c778 Mon Sep 17 00:00:00 2001 From: Rob Tompkins Date: Sun, 12 Jul 2020 08:14:24 -0400 Subject: [PATCH 6/8] (docs) Add citations to LockingVisitors javadoc --- .../lang3/concurrent/locks/LockingVisitors.java | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/apache/commons/lang3/concurrent/locks/LockingVisitors.java b/src/main/java/org/apache/commons/lang3/concurrent/locks/LockingVisitors.java index c3dce37ea..1df6152c3 100644 --- a/src/main/java/org/apache/commons/lang3/concurrent/locks/LockingVisitors.java +++ b/src/main/java/org/apache/commons/lang3/concurrent/locks/LockingVisitors.java @@ -28,17 +28,23 @@ import org.apache.commons.lang3.function.FailableConsumer; import org.apache.commons.lang3.function.FailableFunction; /** + *

* Combines the monitor and visitor pattern to work with {@link java.util.concurrent.locks.Lock locked objects}. Locked - * objects are an alternative to synchronization. - * + * objects are an alternative to synchronization. This, on Wikipedia, is known as the Visitor pattern + * (https://en.wikipedia.org/wiki/Visitor_pattern), and from the "Gang of Four" "Design Patterns" book's Visitor pattern + * [Gamma, E., Helm, R., & Johnson, R. (1998). Visitor. In Design patterns elements of reusable object oriented software (pp. 331-344). Reading: Addison Wesley.]. + *

+ *

* Locking is preferable, if there is a distinction between read access (multiple threads may have read access * concurrently), and write access (only one thread may have write access at any given time). In comparison, * synchronization doesn't support read access, because synchronized access is exclusive. - * + *

+ *

* Using this class is fairly straightforward: + *

*
    *
  1. While still in single thread mode, create an instance of {@link LockingVisitors.StampedLockVisitor} by calling - * {@link #stampedLockVisitor(Object)}, passing the object, which needs to be locked. Discard all references to the + * {@link #stampedLockVisitor(Object)}, passing the object which needs to be locked. Discard all references to the * locked object. Instead, use references to the lock.
  2. *
  3. If you want to access the locked object, create a {@link FailableConsumer}. The consumer will receive the locked * object as a parameter. For convenience, the consumer may be implemented as a Lambda. Then invoke @@ -49,8 +55,9 @@ import org.apache.commons.lang3.function.FailableFunction; * {@link LockingVisitors.StampedLockVisitor#applyReadLocked(FailableFunction)}, or * {@link LockingVisitors.StampedLockVisitor#applyWriteLocked(FailableFunction)}.
  4. *
- * + *

* Example: A thread safe logger class. + *

* *
  *   public class SimpleLogger {

From 3d8408776ea4d281fc68680c4d87483a4ed66883 Mon Sep 17 00:00:00 2001
From: Rob Tompkins 
Date: Sun, 12 Jul 2020 08:43:32 -0400
Subject: [PATCH 7/8] (docs) minor nits and whitespace addions for readability

---
 .../lang3/concurrent/locks/LockingVisitors.java  | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)

diff --git a/src/main/java/org/apache/commons/lang3/concurrent/locks/LockingVisitors.java b/src/main/java/org/apache/commons/lang3/concurrent/locks/LockingVisitors.java
index 1df6152c3..98d3218ea 100644
--- a/src/main/java/org/apache/commons/lang3/concurrent/locks/LockingVisitors.java
+++ b/src/main/java/org/apache/commons/lang3/concurrent/locks/LockingVisitors.java
@@ -127,8 +127,10 @@ public class LockingVisitors {
         }
 
         /**
+         * 

* Provides read (shared, non-exclusive) access to the locked (hidden) object. More precisely, what the method * will do (in the given order): + *

*
    *
  1. Obtain a read (shared) lock on the locked (hidden) object. The current thread may block, until such a * lock is granted.
  2. @@ -147,8 +149,10 @@ public class LockingVisitors { } /** + *

    * Provides write (exclusive) access to the locked (hidden) object. More precisely, what the method will do (in * the given order): + *

    *
      *
    1. Obtain a write (shared) lock on the locked (hidden) object. The current thread may block, until such a * lock is granted.
    2. @@ -167,8 +171,10 @@ public class LockingVisitors { } /** + *

      * Provides read (shared, non-exclusive) access to the locked (hidden) object for the purpose of computing a * result object. More precisely, what the method will do (in the given order): + *

      *
        *
      1. Obtain a read (shared) lock on the locked (hidden) object. The current thread may block, until such a * lock is granted.
      2. @@ -178,10 +184,10 @@ public class LockingVisitors { * lock will be released anyways. *
      3. Return the result object, that has been received from the functions invocation.
      4. *
      - * - * Example: Suggest, that the hidden object is a list, and we wish to know the current size of the + *

      + * Example: Consider that the hidden object is a list, and we wish to know the current size of the * list. This might be achieved with the following: - * + *

      *
                * private Lock<List<Object>> listLock;
                *
      @@ -205,8 +211,10 @@ public class LockingVisitors {
               }
       
               /**
      +         * 

      * Provides write (exclusive) access to the locked (hidden) object for the purpose of computing a result object. * More precisely, what the method will do (in the given order): + *

      *
        *
      1. Obtain a read (shared) lock on the locked (hidden) object. The current thread may block, until such a * lock is granted.
      2. @@ -325,7 +333,7 @@ public class LockingVisitors { /** * This class implements a wrapper for a locked (hidden) object, and provides the means to access it. The basic - * idea, is that the user code forsakes all references to the locked object, using only the wrapper object, and the + * idea is that the user code forsakes all references to the locked object, using only the wrapper object, and the * accessor methods {@link #acceptReadLocked(FailableConsumer)}, {@link #acceptWriteLocked(FailableConsumer)}, * {@link #applyReadLocked(FailableFunction)}, and {@link #applyWriteLocked(FailableFunction)}. By doing so, the * necessary protections are guaranteed. From fe95edd45ffe8d4ffcec17cd8d7741687374c17f Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Sun, 12 Jul 2020 08:59:56 -0400 Subject: [PATCH 8/8] Prepare for 3.11-RC2. --- src/changes/changes.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 2d616613e..74b49eb38 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -45,7 +45,7 @@ The type attribute can be add,update,fix,remove. - + Refine test output for FastDateParserTest CharSequenceUtils.lastIndexOf : remake it remove encoding and docEncoding and use inherited values from commons-parent