From b149ebc0f1c9ec57930d7facfab00083fe436aff Mon Sep 17 00:00:00 2001 From: Adam Retter Date: Thu, 20 Jul 2017 14:53:55 -0400 Subject: [PATCH 01/21] [feature] Maven build of Fast Multi-Level Locks for Java --- .gitignore | 3 + pom.xml | 191 ++++++++++++++++++ .../ac/ic/doc/slurp/multilock}/MultiLock.java | 2 +- .../ac/ic/doc/slurp/multilock}/Lockable.java | 4 +- .../doc/slurp/multilock}/bank/BankTest.java | 5 +- .../multilock}/bank/BankTestMultiLock.java | 2 +- .../slurp/multilock}/bank/BankTestRWLock.java | 2 +- .../slurp/multilock}/bank/BankTestSTM.java | 2 +- .../ic/doc/slurp/multilock}/bank/Locker.java | 2 +- .../slurp/multilock}/counter/CounterTest.java | 8 +- .../counter/CounterTestMultiLock.java | 2 +- .../multilock}/counter/CounterTestRWLock.java | 2 +- .../multilock}/counter/CounterTestSTM.java | 2 +- 13 files changed, 209 insertions(+), 18 deletions(-) create mode 100644 .gitignore create mode 100644 pom.xml rename {multilock => src/main/java/uk/ac/ic/doc/slurp/multilock}/MultiLock.java (99%) rename {test => src/test/java/uk/ac/ic/doc/slurp/multilock}/Lockable.java (97%) rename {test => src/test/java/uk/ac/ic/doc/slurp/multilock}/bank/BankTest.java (98%) rename {test => src/test/java/uk/ac/ic/doc/slurp/multilock}/bank/BankTestMultiLock.java (98%) rename {test => src/test/java/uk/ac/ic/doc/slurp/multilock}/bank/BankTestRWLock.java (99%) rename {test => src/test/java/uk/ac/ic/doc/slurp/multilock}/bank/BankTestSTM.java (98%) rename {test => src/test/java/uk/ac/ic/doc/slurp/multilock}/bank/Locker.java (96%) rename {test => src/test/java/uk/ac/ic/doc/slurp/multilock}/counter/CounterTest.java (91%) rename {test => src/test/java/uk/ac/ic/doc/slurp/multilock}/counter/CounterTestMultiLock.java (97%) rename {test => src/test/java/uk/ac/ic/doc/slurp/multilock}/counter/CounterTestRWLock.java (97%) rename {test => src/test/java/uk/ac/ic/doc/slurp/multilock}/counter/CounterTestSTM.java (97%) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4bfa550 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.idea/ +*.iml +target/ diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..2e52cf1 --- /dev/null +++ b/pom.xml @@ -0,0 +1,191 @@ + + + 4.0.0 + + + org.sonatype.oss + oss-parent + 7 + + + + uk.ac.ic.doc.slurp.multilock + multilock + 1.0-SNAPSHOT + + Multilock + Fast Multi-Level Locks for Java + http://pubs.doc.ic.ac.uk/fast-multi-level-locks/ + 2010 + + + SLURP, Department of Computing, Imperial College London + http://slurp.doc.ic.ac.uk/ + + + + + The BSD 2-Clause License + http://www.opensource.org/licenses/BSD-2-Clause + repo + + + + + 1.6 + UTF-8 + + + + + org.deucestm + deuce-annotations + 1.0-SNAPSHOT + + + + + + + com.mycila + license-maven-plugin + 3.0 + true + +
com/mycila/maven/plugin/license/templates/BSD-2.txt
+ true + true + true + + Khilan Gudka + + + pom.xml + README.md + LICENSE + + ${project.build.sourceEncoding} +
+ + + check-headers + verify + + check + + + +
+ + org.apache.maven.plugins + maven-compiler-plugin + 3.6.1 + + ${java.version} + ${java.version} + ${project.build.sourceEncoding} + + + + compile + + compile + + + + + + com.code54.mojo + buildversion-plugin + 1.0.3 + + + validate + + set-properties + + + + + + org.apache.maven.plugins + maven-jar-plugin + 3.0.2 + + + + ${build-tag} + ${build-commit} + ${build-commit-abbrev} + ${build-version} + ${build-tstamp} + ${project.scm.connection} + ${project.description} + ${project.url} + + + + + + + org.apache.maven.plugins + maven-source-plugin + 3.0.1 + + + + true + true + + + ${build-tag} + ${build-commit} + ${build-commit-abbrev} + ${build-version} + ${build-tstamp} + ${project.scm.connection} + ${project.description} + ${project.url} + + + + + + attach-sources + verify + + jar + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.6 + + true + + + + org.apache.maven.plugins + maven-release-plugin + 2.5.3 + + forked-path + true + @{project.version} + + +
+
+ + + + clojars.org + http://clojars.org/repo + + +
diff --git a/multilock/MultiLock.java b/src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java similarity index 99% rename from multilock/MultiLock.java rename to src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java index edc33a9..422b69d 100644 --- a/multilock/MultiLock.java +++ b/src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java @@ -24,7 +24,7 @@ * SUCH DAMAGE. */ -package multilock; +package uk.ac.ic.doc.slurp.multilock; import java.util.concurrent.TimeUnit; diff --git a/test/Lockable.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/Lockable.java similarity index 97% rename from test/Lockable.java rename to src/test/java/uk/ac/ic/doc/slurp/multilock/Lockable.java index cf84305..26a5963 100644 --- a/test/Lockable.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/Lockable.java @@ -24,12 +24,10 @@ * SUCH DAMAGE. */ -package test; +package uk.ac.ic.doc.slurp.multilock; import java.util.concurrent.locks.*; -import multilock.MultiLock; - public abstract class Lockable { public MultiLock mlock = new MultiLock(null); public ReentrantReadWriteLock rwlock = new ReentrantReadWriteLock(); diff --git a/test/bank/BankTest.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/bank/BankTest.java similarity index 98% rename from test/bank/BankTest.java rename to src/test/java/uk/ac/ic/doc/slurp/multilock/bank/BankTest.java index ee9b698..64731bf 100644 --- a/test/bank/BankTest.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/bank/BankTest.java @@ -24,13 +24,12 @@ * SUCH DAMAGE. */ -package test.bank; +package uk.ac.ic.doc.slurp.multilock.bank; import java.io.FileNotFoundException; import java.util.Random; -import java.util.concurrent.atomic.AtomicLong; -import test.Lockable; +import uk.ac.ic.doc.slurp.multilock.Lockable; class Account extends Lockable { diff --git a/test/bank/BankTestMultiLock.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/bank/BankTestMultiLock.java similarity index 98% rename from test/bank/BankTestMultiLock.java rename to src/test/java/uk/ac/ic/doc/slurp/multilock/bank/BankTestMultiLock.java index 6733a7a..4bcf9fb 100644 --- a/test/bank/BankTestMultiLock.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/bank/BankTestMultiLock.java @@ -24,7 +24,7 @@ * SUCH DAMAGE. */ -package test.bank; +package uk.ac.ic.doc.slurp.multilock.bank; import java.io.FileNotFoundException; import java.util.Random; diff --git a/test/bank/BankTestRWLock.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/bank/BankTestRWLock.java similarity index 99% rename from test/bank/BankTestRWLock.java rename to src/test/java/uk/ac/ic/doc/slurp/multilock/bank/BankTestRWLock.java index 4f2a989..9b77559 100644 --- a/test/bank/BankTestRWLock.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/bank/BankTestRWLock.java @@ -24,7 +24,7 @@ * SUCH DAMAGE. */ -package test.bank; +package uk.ac.ic.doc.slurp.multilock.bank; import java.io.FileNotFoundException; import java.util.Random; diff --git a/test/bank/BankTestSTM.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/bank/BankTestSTM.java similarity index 98% rename from test/bank/BankTestSTM.java rename to src/test/java/uk/ac/ic/doc/slurp/multilock/bank/BankTestSTM.java index 00444d0..0b13b52 100644 --- a/test/bank/BankTestSTM.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/bank/BankTestSTM.java @@ -24,7 +24,7 @@ * SUCH DAMAGE. */ -package test.bank; +package uk.ac.ic.doc.slurp.multilock.bank; import java.io.FileNotFoundException; import java.util.Random; diff --git a/test/bank/Locker.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/bank/Locker.java similarity index 96% rename from test/bank/Locker.java rename to src/test/java/uk/ac/ic/doc/slurp/multilock/bank/Locker.java index 1798049..221e447 100644 --- a/test/bank/Locker.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/bank/Locker.java @@ -24,7 +24,7 @@ * SUCH DAMAGE. */ -package test.bank; +package uk.ac.ic.doc.slurp.multilock.bank; public interface Locker { diff --git a/test/counter/CounterTest.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/counter/CounterTest.java similarity index 91% rename from test/counter/CounterTest.java rename to src/test/java/uk/ac/ic/doc/slurp/multilock/counter/CounterTest.java index 805608e..3ad0501 100644 --- a/test/counter/CounterTest.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/counter/CounterTest.java @@ -24,9 +24,9 @@ * SUCH DAMAGE. */ -package test.counter; +package uk.ac.ic.doc.slurp.multilock.counter; -import test.Lockable; +import uk.ac.ic.doc.slurp.multilock.Lockable; class Counter extends Lockable { long value = 0; @@ -76,8 +76,8 @@ public void run() { double took = (System.currentTimeMillis() - start)/1000.0; // time in seconds long expected = nThreads*nIncs; double throughput = expected/took; - System.out.println("Expected counter value: " + expected); - System.out.println("Actual counter value: " + c.value); + System.out.println("Expected uk.ac.ic.doc.slurp.multilock.counter value: " + expected); + System.out.println("Actual uk.ac.ic.doc.slurp.multilock.counter value: " + c.value); System.out.println("Running time (secs): " + String.format("%.2f", took)); String throughputStr = String.format("%.2f", throughput); System.out.println("incs/sec: " + throughputStr); diff --git a/test/counter/CounterTestMultiLock.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/counter/CounterTestMultiLock.java similarity index 97% rename from test/counter/CounterTestMultiLock.java rename to src/test/java/uk/ac/ic/doc/slurp/multilock/counter/CounterTestMultiLock.java index 7162da5..3fe438f 100644 --- a/test/counter/CounterTestMultiLock.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/counter/CounterTestMultiLock.java @@ -24,7 +24,7 @@ * SUCH DAMAGE. */ -package test.counter; +package uk.ac.ic.doc.slurp.multilock.counter; public class CounterTestMultiLock extends CounterTest { diff --git a/test/counter/CounterTestRWLock.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/counter/CounterTestRWLock.java similarity index 97% rename from test/counter/CounterTestRWLock.java rename to src/test/java/uk/ac/ic/doc/slurp/multilock/counter/CounterTestRWLock.java index b3de308..ca8f005 100644 --- a/test/counter/CounterTestRWLock.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/counter/CounterTestRWLock.java @@ -24,7 +24,7 @@ * SUCH DAMAGE. */ -package test.counter; +package uk.ac.ic.doc.slurp.multilock.counter; public class CounterTestRWLock extends CounterTest { diff --git a/test/counter/CounterTestSTM.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/counter/CounterTestSTM.java similarity index 97% rename from test/counter/CounterTestSTM.java rename to src/test/java/uk/ac/ic/doc/slurp/multilock/counter/CounterTestSTM.java index c94545a..9a1b676 100644 --- a/test/counter/CounterTestSTM.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/counter/CounterTestSTM.java @@ -24,7 +24,7 @@ * SUCH DAMAGE. */ -package test.counter; +package uk.ac.ic.doc.slurp.multilock.counter; import org.deuce.Atomic; From 34d8074c7921b263fc91b12a3783c126e5d36cee Mon Sep 17 00:00:00 2001 From: Adam Retter Date: Thu, 20 Jul 2017 14:35:21 -0400 Subject: [PATCH 02/21] [feature] Add facilities for reporting on lock counts --- .../ac/ic/doc/slurp/multilock/MultiLock.java | 193 +++++++++++++++++- 1 file changed, 191 insertions(+), 2 deletions(-) diff --git a/src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java b/src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java index 422b69d..af5cdce 100644 --- a/src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java +++ b/src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java @@ -27,6 +27,9 @@ package uk.ac.ic.doc.slurp.multilock; +import sun.misc.Unsafe; + +import java.lang.reflect.Field; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.*; @@ -309,7 +312,79 @@ protected boolean tryReleaseShared(long arg) { } } } - + + final int getIntentionReadLockCount() { + return (int)isCount(getState()); + } + + final int getReadLockCount() { + return (int)sCount(getState()); + } + + final int getIntentionWriteLockCount() { + return (int)ixCount(getState()); + } + + final int getWriteLockCount() { + return (int)xCount(getState()); + } + + final int getIntentionReadHoldCount() { + if (getIntentionReadLockCount() == 0) + return 0; + + final Thread current = Thread.currentThread(); + final HoldCounter rh = cachedHoldCounter; + if (rh != null && rh.tid == getThreadId(current)) + return (int)isCount(rh.state); + + int count = (int)isCount(holdCounts.get().state); + //if (count == 0) readHolds.remove(); + return count; + } + + final int getReadHoldCount() { + if (getReadLockCount() == 0) + return 0; + + final Thread current = Thread.currentThread(); + final HoldCounter rh = cachedHoldCounter; + if (rh != null && rh.tid == getThreadId(current)) + return (int)sCount(rh.state); + + int count = (int)sCount(holdCounts.get().state); + //if (count == 0) readHolds.remove(); + return count; + } + + final int getIntentionWriteHoldCount() { + if (getIntentionWriteLockCount() == 0) + return 0; + + final Thread current = Thread.currentThread(); + final HoldCounter rh = cachedHoldCounter; + if (rh != null && rh.tid == getThreadId(current)) + return (int)ixCount(rh.state); + + int count = (int)ixCount(holdCounts.get().state); + //if (count == 0) readHolds.remove(); + return count; + } + + + final int getWriteHoldCount() { + if (getWriteLockCount() == 0) + return 0; + + final Thread current = Thread.currentThread(); + final HoldCounter rh = cachedHoldCounter; + if (rh != null && rh.tid == getThreadId(current)) + return (int)xCount(rh.state); + + int count = (int)xCount(holdCounts.get().state); + //if (count == 0) readHolds.remove(); + return count; + } } public boolean lockRead() { @@ -371,5 +446,119 @@ public void unlockIntentionWrite() { owner.unlockIntentionWrite(); } } - + + /** + * Queries the number of intention read locks held for this lock. This + * method is designed for use in monitoring system state, not for + * synchronization control. + * @return the number of intention read locks held + */ + public int getIntentionReadLockCount() { + return sync.getIntentionReadLockCount(); + } + + /** + * Queries the number of read locks held for this lock. This + * method is designed for use in monitoring system state, not for + * synchronization control. + * @return the number of read locks held + */ + public int getReadLockCount() { + return sync.getReadLockCount(); + } + + /** + * Queries the number of intention write locks held for this lock. This + * method is designed for use in monitoring system state, not for + * synchronization control. + * @return the number of intention write locks held + */ + public int getIntentionWriteLockCount() { + return sync.getIntentionWriteLockCount(); + } + + /** + * Queries the number of write locks held for this lock. This + * method is designed for use in monitoring system state, not for + * synchronization control. + * @return the number of write locks held + */ + public int getWriteLockCount() { + return sync.getWriteLockCount(); + } + + /** + * Queries the number of reentrant intention read holds on this lock by the + * current thread. A reader thread has a hold on a lock for + * each lock action that is not matched by an unlock action. + * + * @return the number of holds on the intention read lock by the current thread, + * or zero if the intention read lock is not held by the current thread + */ + public int getIntentionReadHoldCount() { + return sync.getIntentionReadHoldCount(); + } + + /** + * Queries the number of reentrant read holds on this lock by the + * current thread. A reader thread has a hold on a lock for + * each lock action that is not matched by an unlock action. + * + * @return the number of holds on the read lock by the current thread, + * or zero if the read lock is not held by the current thread + */ + public int getReadHoldCount() { + return sync.getReadHoldCount(); + } + + /** + * Queries the number of reentrant intention write holds on this lock by the + * current thread. A reader thread has a hold on a lock for + * each lock action that is not matched by an unlock action. + * + * @return the number of holds on the intention write lock by the current thread, + * or zero if the intention write lock is not held by the current thread + */ + public int getIntentionWriteHoldCount() { + return sync.getIntentionWriteHoldCount(); + } + + /** + * Queries the number of reentrant write holds on this lock by the + * current thread. A reader thread has a hold on a lock for + * each lock action that is not matched by an unlock action. + * + * @return the number of holds on the write lock by the current thread, + * or zero if the write lock is not held by the current thread + */ + public int getWriteHoldCount() { + return sync.getWriteHoldCount(); + } + + /** + * Returns the thread id for the given thread. We must access + * this directly rather than via method Thread.getId() because + * getId() is not final, and has been known to be overridden in + * ways that do not preserve unique mappings. + */ + static final long getThreadId(Thread thread) { + return UNSAFE.getLongVolatile(thread, TID_OFFSET); + } + + // Unsafe mechanics + private static final sun.misc.Unsafe UNSAFE; + private static final long TID_OFFSET; + static { + try { + final Field singleoneInstanceField = Unsafe.class.getDeclaredField("theUnsafe"); + singleoneInstanceField.setAccessible(true); + UNSAFE = (Unsafe) singleoneInstanceField.get(null); + + final Class tk = Thread.class; + TID_OFFSET = UNSAFE.objectFieldOffset + (tk.getDeclaredField("tid")); + } catch (final Exception e) { + throw new Error(e); + } + } } From 78c38193316fbdb16106e0b2bf365347f1b3072b Mon Sep 17 00:00:00 2001 From: Adam Retter Date: Thu, 20 Jul 2017 14:41:48 -0400 Subject: [PATCH 03/21] [doc] Updated README.md with a pointer to the original project --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 32e2f0b..3587d3b 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ Java implementation of Fast Multi-Level locks. +**NOTE:** This is a Mavenized port of of the original https://github.com/kgudka/java-multilocks with a few additions. + This is the source-code accompanying our EC^2 2010 Workshop on Exploiting Concurrency Efficiently and Correctly position paper. From 70cb85272574be7b04e46828f57f9892fad266ff Mon Sep 17 00:00:00 2001 From: Adam Retter Date: Sun, 19 Aug 2018 20:18:16 +0530 Subject: [PATCH 04/21] [test] Tests to try and assert behvaiour of MultiLock --- pom.xml | 6 + .../ac/ic/doc/slurp/multilock/LockMode.java | 127 +++++ .../slurp/multilock/CompatibilityTest.java | 219 +++++++++ .../ic/doc/slurp/multilock/DowngradeTest.java | 117 +++++ .../ic/doc/slurp/multilock/HierarchyTest.java | 457 ++++++++++++++++++ .../doc/slurp/multilock/ReentrancyTest.java | 89 ++++ .../ic/doc/slurp/multilock/UpgradeTest.java | 117 +++++ 7 files changed, 1132 insertions(+) create mode 100644 src/main/java/uk/ac/ic/doc/slurp/multilock/LockMode.java create mode 100644 src/test/java/uk/ac/ic/doc/slurp/multilock/CompatibilityTest.java create mode 100644 src/test/java/uk/ac/ic/doc/slurp/multilock/DowngradeTest.java create mode 100644 src/test/java/uk/ac/ic/doc/slurp/multilock/HierarchyTest.java create mode 100644 src/test/java/uk/ac/ic/doc/slurp/multilock/ReentrancyTest.java create mode 100644 src/test/java/uk/ac/ic/doc/slurp/multilock/UpgradeTest.java diff --git a/pom.xml b/pom.xml index 2e52cf1..c41f8b3 100644 --- a/pom.xml +++ b/pom.xml @@ -44,6 +44,12 @@ deuce-annotations 1.0-SNAPSHOT + + junit + junit + 4.12 + test + diff --git a/src/main/java/uk/ac/ic/doc/slurp/multilock/LockMode.java b/src/main/java/uk/ac/ic/doc/slurp/multilock/LockMode.java new file mode 100644 index 0000000..6da5e3a --- /dev/null +++ b/src/main/java/uk/ac/ic/doc/slurp/multilock/LockMode.java @@ -0,0 +1,127 @@ +package uk.ac.ic.doc.slurp.multilock; + +/** + * An Enumeration of the MultiLock modes. + */ +public enum LockMode { + /** + * Intention Shared + */ + IS, + + /** + * Intention Exclusive + */ + IX, + + /** + * Shared + */ + S, + + /** + * Shared Intention Exclusive + */ + SIX, + + /** + * Exclusive + */ + X; + + /** + * Locks the MultiLock with this mode. + * + * @param multiLock the MultiLock object. + * + * @return true if the lock succeeded, false otherwise. + */ + public boolean lock(final MultiLock multiLock) { + return lock(multiLock, this); + } + + /** + * Unlocks the MultiLock with this mode. + * + * @param multiLock the MultiLock object. + */ + public void unlock(final MultiLock multiLock) { + unlock(multiLock, this); + } + + /** + * Locks the MultiLock with the provided mode. + * + * @param multiLock the MultiLock object. + * @param lockMode the mode to lock the MultiLock. + * + * @return true if the lock succeeded, false otherwise. + * + * @throws IllegalArgumentException if an unknown mode is provided. + */ + public static boolean lock(final MultiLock multiLock, final LockMode lockMode) { + final boolean lockResult; + switch (lockMode) { + case IS: + lockResult = multiLock.lockIntentionRead(); + break; + + case IX: + lockResult = multiLock.lockIntentionWrite(); + break; + + case S: + lockResult = multiLock.lockRead(); + break; + + case SIX: + lockResult = multiLock.lockRead() && multiLock.lockIntentionWrite(); + break; + + case X: + lockResult = multiLock.lockWrite(); + break; + + default: + throw new IllegalArgumentException("Unknown lock mode: " + lockMode); + } + + return lockResult; + } + + /** + * Unlocks the MultiLock with the provided mode. + * + * @param multiLock the MultiLock object. + * @param lockMode the mode to unlock the MultiLock. + * + * @throws IllegalArgumentException if an unknown mode is provided. + */ + public static void unlock(final MultiLock multiLock, final LockMode lockMode) { + switch (lockMode) { + case IS: + multiLock.unlockIntentionRead(); + break; + + case IX: + multiLock.unlockIntentionWrite(); + break; + + case S: + multiLock.unlockRead(); + break; + + case SIX: + multiLock.unlockIntentionWrite(); + multiLock.unlockRead(); + break; + + case X: + multiLock.unlockWrite(); + break; + + default: + throw new IllegalArgumentException("Unknown lock mode: " + lockMode); + } + } +} diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/CompatibilityTest.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/CompatibilityTest.java new file mode 100644 index 0000000..327059b --- /dev/null +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/CompatibilityTest.java @@ -0,0 +1,219 @@ +package uk.ac.ic.doc.slurp.multilock; + +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.*; + +import static junit.framework.TestCase.assertFalse; +import static org.junit.Assert.assertTrue; +import static uk.ac.ic.doc.slurp.multilock.LockMode.*; + +/** + * Tests which check the compatibility of lock modes. + * + * For MultiLock the compatibility matrix is as described in the + * 1975 paper by Gray et al. "Granularity of Locks in a Shared Data Base". + * + * |----------------------------------| + * | COMPATIBILITY | + * |----------------------------------| + * | | IS | IX | S | SIX | X | + * |----------------------------------| + * | IS | YES | YES | YES | YES | NO | + * | IX | YES | YES | NO | NO | NO | + * | S | YES | NO | YES | NO | NO | + * | SIX | YES | NO | NO | NO | NO | + * | X | NO | NO | NO | NO | NO | + * |----------------------------------| + * + * Compatibility is asserted by having a thread t1 acquire + * a lock mode, and thread t2 acquire another lock mode. Both + * threads must acquire their lock modes within a defined + * timeout for the lock modes to be considered compatible. + * + * @author Adam Retter + */ +public class CompatibilityTest { + + private static final long LOCK_ACQUISITION_TIMEOUT = 20; // TODO(AR) this might need to be longer on slower machines... + + @Test + public void compatible_IS_IS() throws InterruptedException, ExecutionException { + assertCompatible(IS, IS); + } + + @Test + public void compatible_IS_IX() throws InterruptedException, ExecutionException { + assertCompatible(IS, IX); + } + + @Test + public void compatible_IS_S() throws InterruptedException, ExecutionException { + assertCompatible(IS, S); + } + + @Test + public void compatible_IS_SIX() throws InterruptedException, ExecutionException { + assertCompatible(IS, SIX); + } + + @Test + public void compatible_IS_X() throws InterruptedException, ExecutionException { + assertNotCompatible(IS, X); + } + + @Test + public void compatible_IX_IS() throws InterruptedException, ExecutionException { + assertCompatible(IX, IS); + } + + @Test + public void compatible_IX_IX() throws InterruptedException, ExecutionException { + assertCompatible(IX, IX); + } + + @Test + public void compatible_IX_S() throws InterruptedException, ExecutionException { + assertNotCompatible(IX, S); + } + + @Test + public void compatible_IX_SIX() throws InterruptedException, ExecutionException { + assertNotCompatible(IX, SIX); + } + + @Test + public void compatible_IX_X() throws InterruptedException, ExecutionException { + assertNotCompatible(IX, X); + } + + @Test + public void compatible_S_IS() throws InterruptedException, ExecutionException { + assertCompatible(S, IS); + } + + @Test + public void compatible_S_IX() throws InterruptedException, ExecutionException { + assertNotCompatible(S, IX); + } + + @Test + public void compatible_S_S() throws InterruptedException, ExecutionException { + assertCompatible(S, S); + } + + @Test + public void compatible_S_SIX() throws InterruptedException, ExecutionException { + assertNotCompatible(S, SIX); + } + + @Test + public void compatible_S_X() throws InterruptedException, ExecutionException { + assertNotCompatible(S, X); + } + + @Test + public void compatible_SIX_IS() throws InterruptedException, ExecutionException { + assertCompatible(SIX, IS); + } + + @Test + public void compatible_SIX_IX() throws InterruptedException, ExecutionException { + assertNotCompatible(SIX, IX); + } + + @Test + public void compatible_SIX_S() throws InterruptedException, ExecutionException { + assertNotCompatible(SIX, S); + } + + @Test + public void compatible_SIX_SIX() throws InterruptedException, ExecutionException { + assertNotCompatible(SIX, SIX); + } + + @Test + public void compatible_SIX_X() throws InterruptedException, ExecutionException { + assertNotCompatible(SIX, X); + } + + @Test + public void compatible_X_IS() throws InterruptedException, ExecutionException { + assertNotCompatible(X, IS); + } + + @Test + public void compatible_X_IX() throws InterruptedException, ExecutionException { + assertNotCompatible(X, IX); + } + + @Test + public void compatible_X_S() throws InterruptedException, ExecutionException { + assertNotCompatible(X, S); + } + + @Test + public void compatible_X_SIX() throws InterruptedException, ExecutionException { + assertNotCompatible(X, SIX); + } + + @Test + public void compatible_X_X() throws InterruptedException, ExecutionException { + assertNotCompatible(X, X); + } + + private static void assertCompatible(final LockMode mode1, final LockMode mode2) throws InterruptedException, ExecutionException { + final List> futures = checkCompatibility(mode1, mode2); + for (final Future future : futures) { + assertTrue(future.isDone()); + assertFalse(future.isCancelled()); + + assertTrue(future.get()); + } + } + + private static void assertNotCompatible(final LockMode mode1, final LockMode mode2) throws InterruptedException, ExecutionException { + final List> futures = checkCompatibility(mode1, mode2); + for (final Future future : futures) { + assertTrue(future.isDone()); + + assertTrue(future.isCancelled()); + } + } + + private static List> checkCompatibility(final LockMode mode1, final LockMode mode2) throws InterruptedException { + final MultiLock multiLock = new MultiLock(null); + + final CountDownLatch latch = new CountDownLatch(2); + + final Callable thread1 = new LockAcquirer(multiLock, mode1, latch); + final Callable thread2 = new LockAcquirer(multiLock, mode2, latch); + + final ExecutorService executorService = Executors.newFixedThreadPool(2); + return executorService.invokeAll(Arrays.asList(thread1, thread2), LOCK_ACQUISITION_TIMEOUT, TimeUnit.MILLISECONDS); + } + + private static class LockAcquirer implements Callable { + private final MultiLock multiLock; + private final LockMode lockMode; + private final CountDownLatch latch; + + public LockAcquirer(final MultiLock multiLock, final LockMode lockMode, final CountDownLatch latch) { + this.multiLock = multiLock; + this.lockMode = lockMode; + this.latch = latch; + } + + @Override + public Boolean call() throws Exception { + final boolean lockResult = lockMode.lock(multiLock); + + latch.countDown(); + latch.await(); + + return lockResult; + } + } +} diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/DowngradeTest.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/DowngradeTest.java new file mode 100644 index 0000000..dba10c2 --- /dev/null +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/DowngradeTest.java @@ -0,0 +1,117 @@ +package uk.ac.ic.doc.slurp.multilock; + +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.*; + +import static junit.framework.TestCase.assertFalse; +import static org.junit.Assert.assertTrue; +import static uk.ac.ic.doc.slurp.multilock.LockMode.*; + +/** + * Tests which check the facility for downgrading the + * mode of a lock from a stronger to a weaker mode. + * + * We use a separate thread, to avoid locking up test + * suite execution if downgrading is blocked. + * + * For each test, a new thread proceeds as follows: + * 1. Acquire the lock for the strong mode + * 2. Acquire the lock for the weak mode + * 3. Release the lock for the strong mode + * + * If the above cannot be executed within a defined + * timeout, then downgrading is considered to be + * impossible. + * + * @author Adam Retter + */ +public class DowngradeTest { + + private static final long LOCK_ACQUISITION_TIMEOUT = 40; // TODO(AR) this might need to be longer on slower machines... + + @Test + public void downgrade_IX_IS() throws InterruptedException, ExecutionException { + assertDowngradable(IX, IS); + } + + @Test + public void downgrade_S_IS() throws InterruptedException, ExecutionException { + assertDowngradable(S, IS); + } + + @Test + public void downgrade_SIX_IX() throws InterruptedException, ExecutionException { + assertDowngradable(SIX, IX); + } + + @Test + public void downgrade_SIX_S() throws InterruptedException, ExecutionException { + assertDowngradable(SIX, S); + } + + @Test + public void downgrade_SIX_IS() throws InterruptedException, ExecutionException { + assertDowngradable(SIX, IS); + } + + @Test + public void downgrade_X_SIX() throws InterruptedException, ExecutionException { + assertDowngradable(X, SIX); + } + + @Test + public void downgrade_X_IX() throws InterruptedException, ExecutionException { + assertDowngradable(X, IX); + } + + @Test + public void downgrade_X_S() throws InterruptedException, ExecutionException { + assertDowngradable(X, S); + } + + @Test + public void downgrade_X_IS() throws InterruptedException, ExecutionException { + assertDowngradable(X, IS); + } + + private static void assertDowngradable(final LockMode from, final LockMode to) throws InterruptedException, ExecutionException { + final MultiLock multiLock = new MultiLock(null); + + final ExecutorService executorService = Executors.newSingleThreadExecutor(); + final List> futures = executorService.invokeAll(Arrays.asList(new Downgrade(multiLock, from, to)), LOCK_ACQUISITION_TIMEOUT, TimeUnit.MILLISECONDS); + + for (final Future future : futures) { + assertTrue(future.isDone()); + assertFalse(future.isCancelled()); + + assertTrue(future.get()); + } + } + + private static class Downgrade implements Callable { + private final MultiLock multiLock; + private final LockMode from; + private final LockMode to; + + private Downgrade(final MultiLock multiLock, final LockMode from, final LockMode to) { + this.multiLock = multiLock; + this.from = from; + this.to = to; + } + + @Override + public Boolean call() { + if (from.lock(multiLock)) { + if (to.lock(multiLock)) { + from.unlock(multiLock); + + return true; + } + } + return false; + } + } +} diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/HierarchyTest.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/HierarchyTest.java new file mode 100644 index 0000000..1ab986a --- /dev/null +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/HierarchyTest.java @@ -0,0 +1,457 @@ +package uk.ac.ic.doc.slurp.multilock; + +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.*; + +import static junit.framework.TestCase.assertFalse; +import static org.junit.Assert.assertTrue; +import static uk.ac.ic.doc.slurp.multilock.LockMode.*; + +/** + * Uses a lock hierarchy of two levels, root and child + * to test what lock acquisitions may be made on the child + * in the presence of locks on the root. + * + * Functions named like {@code t1_root_IS_t2_child_IX} + * should be read as, Test where: + * Thread 1 has IS lock on root, + * and Thread 2 will attempt IX lock on child. + * + * There are two types of tests here: + * + * 1. Single threaded tests which check what modes the accessor + * thread is allowed to take on the child after locking the root. + * + * 2. Bi-threaded tests where thread t1 locks the root, and then + * thread t2 tries to lock the child. + * + * @author Adam Retter + */ +public class HierarchyTest { + + private static final long LOCK_ACQUISITION_TIMEOUT = 20; // TODO(AR) this might need to be longer on slower machines... + + /* single threaded tests below */ + + @Test + public void t1_root_IS_t1_child_IS() throws ExecutionException, InterruptedException { + assertLockRootThenChild(IS, IS); + } + + // TODO - page 7 of Gray's paper - Granularity of Locks in a Shared Data Base + @Test + public void t1_root_IS_t1_child_IX() throws ExecutionException, InterruptedException { + assertLockRootThenNotChild(IS, IX); + } + + @Test + public void t1_root_IS_t1_child_S() throws ExecutionException, InterruptedException { + assertLockRootThenChild(IS, S); + } + + // TODO - page 7 of Gray's paper - Granularity of Locks in a Shared Data Base + @Test + public void t1_root_IS_t1_child_SIX() throws ExecutionException, InterruptedException { + assertLockRootThenNotChild(IS, SIX); + } + + // TODO - page 7 of Gray's paper - Granularity of Locks in a Shared Data Base + @Test + public void t1_root_IS_t1_child_X() throws ExecutionException, InterruptedException { + assertLockRootThenNotChild(IS, X); + } + + @Test + public void t1_root_IX_t1_child_IS() throws ExecutionException, InterruptedException { + assertLockRootThenChild(IX, IS); + } + + @Test + public void t1_root_IX_t1_child_IX() throws ExecutionException, InterruptedException { + assertLockRootThenChild(IX, IX); + } + + @Test + public void t1_root_IX_t1_child_S() throws ExecutionException, InterruptedException { + assertLockRootThenChild(IX, S); + } + + @Test + public void t1_root_IX_t1_child_SIX() throws ExecutionException, InterruptedException { + assertLockRootThenChild(IX, SIX); + } + + @Test + public void t1_root_IX_t1_child_X() throws ExecutionException, InterruptedException { + assertLockRootThenChild(IX, X); + } + + //TODO(AR) need tests for t1_root_S_child... -- I think it's likely that S on root should only be able to get S on child + + // TODO - page 7 of Gray's paper - Granularity of Locks in a Shared Data Base + @Test + public void t1_root_SIX_t1_child_IS() throws ExecutionException, InterruptedException { + assertLockRootThenNotChild(SIX, IS); + } + + @Test + public void t1_root_SIX_t1_child_IX() throws ExecutionException, InterruptedException { + assertLockRootThenChild(SIX, IX); + } + + // TODO - page 7 of Gray's paper - Granularity of Locks in a Shared Data Base + @Test + public void t1_root_SIX_t1_child_S() throws ExecutionException, InterruptedException { + assertLockRootThenNotChild(SIX, S); + } + + @Test + public void t1_root_SIX_t1_child_SIX() throws ExecutionException, InterruptedException { + assertLockRootThenChild(SIX, SIX); + } + + @Test + public void t1_root_SIX_t1_child_X() throws ExecutionException, InterruptedException { + assertLockRootThenChild(SIX, X); + } + + //TODO(AR) need tests for t1_root_X_child... -- I think it's likely that X on root should only be able to get X on child + + + /* bi-threaded tests below */ + + @Test + public void t1_root_IS_t2_child_IS() throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(null); + assertTrue(IS.lock(root)); + + final MultiLock child = new MultiLock(root); + assertOtherThreadLockableChild(child, IS); + + } + + @Test + public void t1_root_IS_t2_child_IX() throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(null); + assertTrue(IS.lock(root)); + + final MultiLock child = new MultiLock(root); + assertOtherThreadLockableChild(child, IX); + } + + @Test + public void t1_root_IS_t2_child_S() throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(null); + assertTrue(IS.lock(root)); + + final MultiLock child = new MultiLock(root); + assertOtherThreadLockableChild(child, S); + } + + @Test + public void t1_root_IS_t2_child_SIX() throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(null); + assertTrue(IS.lock(root)); + + final MultiLock child = new MultiLock(root); + assertOtherThreadLockableChild(child, SIX); + } + + @Test + public void t1_root_IS_t2_child_X() throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(null); + assertTrue(IS.lock(root)); + + final MultiLock child = new MultiLock(root); + assertOtherThreadLockableChild(child, X); + } + + @Test + public void t1_root_IX_t2_child_IS() throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(null); + assertTrue(IX.lock(root)); + + final MultiLock child = new MultiLock(root); + assertOtherThreadLockableChild(child, IS); + + } + + @Test + public void t1_root_IX_t2_child_IX() throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(null); + assertTrue(IX.lock(root)); + + final MultiLock child = new MultiLock(root); + assertOtherThreadLockableChild(child, IX); + } + + @Test + public void t1_root_IX_t2_child_S() throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(null); + assertTrue(IX.lock(root)); + + final MultiLock child = new MultiLock(root); + assertOtherThreadLockableChild(child, S); + } + + @Test + public void t1_root_IX_t2_child_SIX() throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(null); + assertTrue(IX.lock(root)); + + final MultiLock child = new MultiLock(root); + assertOtherThreadLockableChild(child, SIX); + } + + @Test + public void t1_root_IX_t2_child_X() throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(null); + assertTrue(IX.lock(root)); + + final MultiLock child = new MultiLock(root); + assertOtherThreadLockableChild(child, SIX); + } + + @Test + public void t1_root_S_t2_child_IS() throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(null); + assertTrue(S.lock(root)); + + final MultiLock child = new MultiLock(root); + assertOtherThreadLockableChild(child, IS); + + } + + @Test + public void t1_root_S_t2_child_IX() throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(null); + assertTrue(S.lock(root)); + + final MultiLock child = new MultiLock(root); + // notLockable = test thread holds S on root, other thread attempts IX on child (which attempts IX on root). S is not compatible with IX + assertOtherThreadNotLockableChild(child, IX); + } + + @Test + public void t1_root_S_t2_child_S() throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(null); + assertTrue(S.lock(root)); + + final MultiLock child = new MultiLock(root); + assertOtherThreadLockableChild(child, S); + } + + @Test + public void t1_root_S_t2_child_SIX() throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(null); + assertTrue(S.lock(root)); + + final MultiLock child = new MultiLock(root); + // notLockable = test thread holds S on root, other thread attempts SIX on child (which attempts S+IX on root). S is not compatible with IX + assertOtherThreadNotLockableChild(child, SIX); + } + + @Test + public void t1_root_S_t2_child_X() throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(null); + assertTrue(S.lock(root)); + + final MultiLock child = new MultiLock(root); + // notLockable = test thread holds S on root, other thread attempts X on child (which attempts IX on root). S is not compatible with IX + assertOtherThreadNotLockableChild(child, X); + } + + @Test + public void t1_root_SIX_t2_child_IS() throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(null); + assertTrue(SIX.lock(root)); + + final MultiLock child = new MultiLock(root); + assertOtherThreadLockableChild(child, IS); + + } + + @Test + public void t1_root_SIX_t2_child_IX() throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(null); + assertTrue(SIX.lock(root)); + + final MultiLock child = new MultiLock(root); + // notLockable = test thread holds S+IX on root, other thread attempts IX on child (which attempts IX on root). S is not compatible with IX + assertOtherThreadNotLockableChild(child, IX); + } + + @Test + public void t1_root_SIX_t2_child_S() throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(null); + assertTrue(SIX.lock(root)); + + final MultiLock child = new MultiLock(root); + assertOtherThreadLockableChild(child, S); + } + + @Test + public void t1_root_SIX_t2_child_SIX() throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(null); + assertTrue(SIX.lock(root)); + + final MultiLock child = new MultiLock(root); + // notLockable = test thread holds S+IX on root, other thread attempts SIX on child (which attempts IS+IX on root). S is not compatible with IX + assertOtherThreadNotLockableChild(child, SIX); + } + + @Test + public void t1_root_SIX_t2_child_X() throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(null); + assertTrue(SIX.lock(root)); + + final MultiLock child = new MultiLock(root); + // notLockable = test thread holds S+IX on root, other thread attempts X on child (which attempts IX on root). S is not compatible with IX + assertOtherThreadNotLockableChild(child, X); + } + + @Test + public void t1_root_X_t2_child_IS() throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(null); + assertTrue(X.lock(root)); + + final MultiLock child = new MultiLock(root); + // notLockable = test thread holds X on root, other thread attempts IS on child (which attempts IS on root). X is not compatible with IS + assertOtherThreadNotLockableChild(child, IS); + + } + + @Test + public void t1_root_X_t2_child_IX() throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(null); + assertTrue(X.lock(root)); + + final MultiLock child = new MultiLock(root); + // notLockable = test thread holds X on root, other thread attempts IX on child (which attempts IX on root). X is not compatible with IX + assertOtherThreadNotLockableChild(child, IX); + } + + @Test + public void t1_root_X_t2_child_S() throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(null); + assertTrue(X.lock(root)); + + final MultiLock child = new MultiLock(root); + // notLockable = test thread holds X on root, other thread attempts S on child (which attempts IS on root). X is not compatible with IS + assertOtherThreadNotLockableChild(child, S); + } + + @Test + public void t1_root_X_t2_child_SIX() throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(null); + assertTrue(X.lock(root)); + + final MultiLock child = new MultiLock(root); + // notLockable = test thread holds X on root, other thread attempts SIX on child (which attempts IS+IX on root). X is not compatible with IS or IX + assertOtherThreadNotLockableChild(child, SIX); + } + + @Test + public void t1_root_X_t2_child_X() throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(null); + assertTrue(X.lock(root)); + + final MultiLock child = new MultiLock(root); + // notLockable = test thread holds X on root, other thread attempts X on child (which attempts IX on root). X is not compatible with IX + assertOtherThreadNotLockableChild(child, X); + } + + private static void assertLockRootThenChild(final LockMode rootMode, final LockMode childMode) throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(null); + final MultiLock child = new MultiLock(root); + + final ExecutorService executorService = Executors.newSingleThreadExecutor(); + final List> futures = executorService.invokeAll(Arrays.asList(new RootAndChildLockAcquirer(root, rootMode, child, childMode)), LOCK_ACQUISITION_TIMEOUT, TimeUnit.MILLISECONDS); + + for (final Future future : futures) { + assertTrue(future.isDone()); + + assertFalse(future.isCancelled()); + + assertTrue(future.get()); + } + } + + private static void assertLockRootThenNotChild(final LockMode rootMode, final LockMode childMode) throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(null); + final MultiLock child = new MultiLock(root); + + final ExecutorService executorService = Executors.newSingleThreadExecutor(); + final List> futures = executorService.invokeAll(Arrays.asList(new RootAndChildLockAcquirer(root, rootMode, child, childMode)), LOCK_ACQUISITION_TIMEOUT, TimeUnit.MILLISECONDS); + + for (final Future future : futures) { + assertTrue(future.isDone()); + + assertTrue(future.isCancelled()); + } + } + + private static void assertOtherThreadLockableChild(final MultiLock child, final LockMode childMode) throws InterruptedException, ExecutionException { + final ExecutorService executorService = Executors.newSingleThreadExecutor(); + final List> futures = executorService.invokeAll(Arrays.asList(new ChildLockAcquirer(child, childMode)), LOCK_ACQUISITION_TIMEOUT, TimeUnit.MILLISECONDS); + + for (final Future future : futures) { + assertTrue(future.isDone()); + assertFalse(future.isCancelled()); + + assertTrue(future.get()); + } + } + + private static void assertOtherThreadNotLockableChild(final MultiLock child, final LockMode childMode) throws InterruptedException, ExecutionException { + final ExecutorService executorService = Executors.newSingleThreadExecutor(); + final List> futures = executorService.invokeAll(Arrays.asList(new ChildLockAcquirer(child, childMode)), LOCK_ACQUISITION_TIMEOUT, TimeUnit.MILLISECONDS); + + for (final Future future : futures) { + assertTrue(future.isDone()); + + assertTrue(future.isCancelled()); + } + } + + private static class RootAndChildLockAcquirer implements Callable { + private final MultiLock root; + private final LockMode rootLockMode; + private final MultiLock child; + private final LockMode childLockMode; + + public RootAndChildLockAcquirer(final MultiLock root, final LockMode rootLockMode, final MultiLock child, final LockMode childLockMode) { + this.root = root; + this.rootLockMode = rootLockMode; + this.child = child; + this.childLockMode = childLockMode; + } + + @Override + public Boolean call() { + if (rootLockMode.lock(root)) { + // NOTE: we intentionally don't combine the two boolean expressions to make debugging the cause of deadlocks simpler + return childLockMode.lock(child); + } + return false; + } + } + + private static class ChildLockAcquirer implements Callable { + private final MultiLock child; + private final LockMode lockMode; + + public ChildLockAcquirer(final MultiLock child, final LockMode lockMode) { + this.child = child; + this.lockMode = lockMode; + } + + @Override + public Boolean call() { + return lockMode.lock(child); + } + } +} diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/ReentrancyTest.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/ReentrancyTest.java new file mode 100644 index 0000000..e60a4e8 --- /dev/null +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/ReentrancyTest.java @@ -0,0 +1,89 @@ +package uk.ac.ic.doc.slurp.multilock; + +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.*; + +import static junit.framework.TestCase.assertFalse; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static uk.ac.ic.doc.slurp.multilock.LockMode.*; + +/** + * Tests which check that acquiring the lock + * for a mode is re-entrant / recusive. + * + * We use a separate thread, to avoid locking up test + * suite execution if lock acquisition is not reentrant. + * + * @author Adam Retter + */ +public class ReentrancyTest { + + private static final int REENTER_COUNT = 10; + private static final long LOCK_ACQUISITIONS_TIMEOUT = 20 * REENTER_COUNT; // TODO(AR) this might need to be longer on slower machines... + + @Test + public void reentrant_IS() throws InterruptedException, ExecutionException { + assertReentrant(IS); + } + + @Test + public void reentrant_IX() throws InterruptedException, ExecutionException { + assertReentrant(IX); + } + + @Test + public void reentrant_S() throws InterruptedException, ExecutionException { + assertReentrant(S); + } + + @Test + public void reentrant_SIX() throws InterruptedException, ExecutionException { + assertReentrant(SIX); + } + + @Test + public void reentrant_X() throws InterruptedException, ExecutionException { + assertReentrant(X); + } + + private static void assertReentrant(final LockMode lockMode) throws InterruptedException, ExecutionException { + final MultiLock multiLock = new MultiLock(null); + + final ExecutorService executorService = Executors.newSingleThreadExecutor(); + final List> futures = executorService.invokeAll(Arrays.asList(new Reenter(multiLock, lockMode, REENTER_COUNT)), LOCK_ACQUISITIONS_TIMEOUT, TimeUnit.MILLISECONDS); + + for (final Future future : futures) { + assertTrue(future.isDone()); + assertFalse(future.isCancelled()); + + assertEquals(REENTER_COUNT, (int)future.get()); + } + } + + private static class Reenter implements Callable { + private final MultiLock multiLock; + private final LockMode lockMode; + private final int count; + + private Reenter(final MultiLock multiLock, final LockMode lockMode, final int count) { + this.multiLock = multiLock; + this.lockMode = lockMode; + this.count = count; + } + + @Override + public Integer call() { + int success = 0; + for (int i = 0; i < count; i++) { + if (lockMode.lock(multiLock)) { + success++; + } + } + return success; + } + } +} diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/UpgradeTest.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/UpgradeTest.java new file mode 100644 index 0000000..3a3c569 --- /dev/null +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/UpgradeTest.java @@ -0,0 +1,117 @@ +package uk.ac.ic.doc.slurp.multilock; + +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.*; + +import static junit.framework.TestCase.assertFalse; +import static org.junit.Assert.assertTrue; +import static uk.ac.ic.doc.slurp.multilock.LockMode.*; + +/** + * Tests which check the facility for upgrading the + * mode of a lock from a weaker to a stronger mode. + * + * We use a separate thread, to avoid locking up test + * suite execution if upgrading is blocked. + * + * For each test, a new thread proceeds as follows: + * 1. Acquire the lock for the weak mode + * 2. Acquire the lock for the strong mode + * 3. Release the lock for the weak mode + * + * If the above cannot be executed within a defined + * timeout, then upgrading is considered to be + * impossible. + * + * @author Adam Retter + */ +public class UpgradeTest { + + private static final long LOCK_ACQUISITION_TIMEOUT = 40; // TODO(AR) this might need to be longer on slower machines... + + @Test + public void upgrade_IS_IX() throws InterruptedException, ExecutionException { + assertUpgradeable(IS, IX); + } + + @Test + public void upgrade_IS_S() throws InterruptedException, ExecutionException { + assertUpgradeable(IS, S); + } + + @Test + public void upgrade_IS_SIX() throws InterruptedException, ExecutionException { + assertUpgradeable(IS, SIX); + } + + @Test + public void upgrade_IS_X() throws InterruptedException, ExecutionException { + assertUpgradeable(IS, X); + } + + @Test + public void upgrade_IX_SIX() throws InterruptedException, ExecutionException { + assertUpgradeable(IX, SIX); + } + + @Test + public void upgrade_IX_X() throws InterruptedException, ExecutionException { + assertUpgradeable(IX, X); + } + + @Test + public void upgrade_S_SIX() throws InterruptedException, ExecutionException { + assertUpgradeable(S, SIX); + } + + @Test + public void upgrade_S_X() throws InterruptedException, ExecutionException { + assertUpgradeable(S, X); + } + + @Test + public void upgrade_SIX_X() throws InterruptedException, ExecutionException { + assertUpgradeable(SIX, X); + } + + private static void assertUpgradeable(final LockMode from, final LockMode to) throws InterruptedException, ExecutionException { + final MultiLock multiLock = new MultiLock(null); + + final ExecutorService executorService = Executors.newSingleThreadExecutor(); + final List> futures = executorService.invokeAll(Arrays.asList(new Upgrade(multiLock, from, to)), LOCK_ACQUISITION_TIMEOUT, TimeUnit.MILLISECONDS); + + for (final Future future : futures) { + assertTrue(future.isDone()); + assertFalse(future.isCancelled()); + + assertTrue(future.get()); + } + } + + private static class Upgrade implements Callable { + private final MultiLock multiLock; + private final LockMode from; + private final LockMode to; + + private Upgrade(final MultiLock multiLock, final LockMode from, final LockMode to) { + this.multiLock = multiLock; + this.from = from; + this.to = to; + } + + @Override + public Boolean call() { + if (from.lock(multiLock)) { + if (to.lock(multiLock)) { + from.unlock(multiLock); + + return true; + } + } + return false; + } + } +} From 275ea7bad70636bf75344af3a66bbe0364603e9b Mon Sep 17 00:00:00 2001 From: Adam Retter Date: Sat, 25 Aug 2018 15:40:41 +0530 Subject: [PATCH 05/21] [feature] Add convenience construtor to MultiLock --- pom.xml | 5 +++++ .../uk/ac/ic/doc/slurp/multilock/MultiLock.java | 16 +++++++++++----- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index c41f8b3..7428e8c 100644 --- a/pom.xml +++ b/pom.xml @@ -39,6 +39,11 @@ + + com.google.code.findbugs + jsr305 + 3.0.2 + org.deucestm deuce-annotations diff --git a/src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java b/src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java index af5cdce..47b566b 100644 --- a/src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java +++ b/src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java @@ -29,6 +29,7 @@ import sun.misc.Unsafe; +import javax.annotation.Nullable; import java.lang.reflect.Field; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.*; @@ -54,11 +55,16 @@ public class MultiLock { final ReadLock readLock; final WriteLock writeLock; - public MultiLock(MultiLock o) { - owner = o; - sync = new Sync(); - readLock = new ReadLock(); - writeLock = new WriteLock(); + + public MultiLock() { + this(null); + } + + public MultiLock(@Nullable final MultiLock o) { + this.owner = o; + this.sync = new Sync(); + this.readLock = new ReadLock(); + this.writeLock = new WriteLock(); } class ReadLock implements Lock { From 1634271fd632691248e64c4b6879fa8865189029 Mon Sep 17 00:00:00 2001 From: Adam Retter Date: Sat, 25 Aug 2018 15:41:26 +0530 Subject: [PATCH 06/21] [bugfix] DeuceSTM is only used for comparison tests --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index 7428e8c..3c0a807 100644 --- a/pom.xml +++ b/pom.xml @@ -48,6 +48,7 @@ org.deucestm deuce-annotations 1.0-SNAPSHOT + test junit From 8d1890904e042bf381fbb171cd0856b0718b4a2b Mon Sep 17 00:00:00 2001 From: Adam Retter Date: Sat, 25 Aug 2018 17:12:01 +0530 Subject: [PATCH 07/21] [refactor] Move Hierarchical locking aspects to a seprarate lock class implementation --- .../multilock/HierarchicalMultiLock.java | 84 ++++++++++++++ .../ac/ic/doc/slurp/multilock/MultiLock.java | 35 +----- .../slurp/multilock/CompatibilityTest.java | 2 +- .../ic/doc/slurp/multilock/DowngradeTest.java | 2 +- .../ic/doc/slurp/multilock/HierarchyTest.java | 108 +++++++++--------- .../ac/ic/doc/slurp/multilock/Lockable.java | 2 +- .../doc/slurp/multilock/ReentrancyTest.java | 2 +- .../ic/doc/slurp/multilock/UpgradeTest.java | 2 +- 8 files changed, 146 insertions(+), 91 deletions(-) create mode 100644 src/main/java/uk/ac/ic/doc/slurp/multilock/HierarchicalMultiLock.java diff --git a/src/main/java/uk/ac/ic/doc/slurp/multilock/HierarchicalMultiLock.java b/src/main/java/uk/ac/ic/doc/slurp/multilock/HierarchicalMultiLock.java new file mode 100644 index 0000000..79c72a8 --- /dev/null +++ b/src/main/java/uk/ac/ic/doc/slurp/multilock/HierarchicalMultiLock.java @@ -0,0 +1,84 @@ +package uk.ac.ic.doc.slurp.multilock; + +import javax.annotation.Nullable; + +public class HierarchicalMultiLock extends MultiLock { + + private final MultiLock parent; + + /** + * Constructs a MultiLock where the lock + * is within a lock hierarchy. + * + * @param parent the parent lock, or null if this lock + * should be the root of the hierarchy. + */ + public HierarchicalMultiLock(@Nullable final MultiLock parent) { + super(); + this.parent = parent; + } + + @Override + public boolean lockRead() { + if (parent != null) { + parent.lockIntentionRead(); + } + return super.lockRead(); + } + + @Override + public boolean lockWrite() { + if (parent != null) { + parent.lockIntentionWrite(); + } + return super.lockWrite(); + } + + @Override + public boolean lockIntentionRead() { + if (parent != null) { + parent.lockIntentionRead(); + } + return super.lockIntentionRead(); + } + + @Override + public boolean lockIntentionWrite() { + if (parent != null) { + parent.lockIntentionWrite(); + } + return super.lockIntentionWrite(); + } + + @Override + public void unlockRead() { + super.unlockRead(); + if (parent != null) { + parent.unlockIntentionRead(); + } + } + + @Override + public void unlockWrite() { + super.unlockWrite(); + if (parent != null) { + parent.unlockIntentionWrite(); + } + } + + @Override + public void unlockIntentionRead() { + super.unlockIntentionRead(); + if (parent != null) { + parent.unlockIntentionRead(); + } + } + + @Override + public void unlockIntentionWrite() { + super.unlockIntentionWrite(); + if (parent != null) { + parent.unlockIntentionWrite(); + } + } +} diff --git a/src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java b/src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java index 47b566b..0d872ad 100644 --- a/src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java +++ b/src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java @@ -29,7 +29,6 @@ import sun.misc.Unsafe; -import javax.annotation.Nullable; import java.lang.reflect.Field; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.*; @@ -49,19 +48,15 @@ public class MultiLock { static final long IX_UNIT = 0x0000000000010000L; static final long IS_UNIT = 0x0000000000000001L; - final MultiLock owner; final Sync sync; final ReadLock readLock; final WriteLock writeLock; - + /** + * Constructs a MultiLock. + */ public MultiLock() { - this(null); - } - - public MultiLock(@Nullable final MultiLock o) { - this.owner = o; this.sync = new Sync(); this.readLock = new ReadLock(); this.writeLock = new WriteLock(); @@ -394,63 +389,39 @@ final int getWriteHoldCount() { } public boolean lockRead() { - if (owner != null) { - owner.lockIntentionRead(); - } sync.acquireShared(S_UNIT); return true; } public boolean lockWrite() { - if (owner != null) { - owner.lockIntentionWrite(); - } sync.acquire(X_UNIT); return true; } public boolean lockIntentionRead() { - if (owner != null) { - owner.lockIntentionRead(); - } sync.acquireShared(IS_UNIT); return true; } public boolean lockIntentionWrite() { - if (owner != null) { - owner.lockIntentionWrite(); - } sync.acquireShared(IX_UNIT); return true; } public void unlockRead() { sync.releaseShared(S_UNIT); - if (owner != null) { - owner.unlockIntentionRead(); - } } public void unlockWrite() { sync.release(X_UNIT); - if (owner != null) { - owner.unlockIntentionWrite(); - } } public void unlockIntentionRead() { sync.releaseShared(IS_UNIT); - if (owner != null) { - owner.unlockIntentionRead(); - } } public void unlockIntentionWrite() { sync.releaseShared(IX_UNIT); - if (owner != null) { - owner.unlockIntentionWrite(); - } } /** diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/CompatibilityTest.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/CompatibilityTest.java index 327059b..6dad659 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/CompatibilityTest.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/CompatibilityTest.java @@ -184,7 +184,7 @@ private static void assertNotCompatible(final LockMode mode1, final LockMode mod } private static List> checkCompatibility(final LockMode mode1, final LockMode mode2) throws InterruptedException { - final MultiLock multiLock = new MultiLock(null); + final MultiLock multiLock = new MultiLock(); final CountDownLatch latch = new CountDownLatch(2); diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/DowngradeTest.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/DowngradeTest.java index dba10c2..b80b5ba 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/DowngradeTest.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/DowngradeTest.java @@ -78,7 +78,7 @@ public void downgrade_X_IS() throws InterruptedException, ExecutionException { } private static void assertDowngradable(final LockMode from, final LockMode to) throws InterruptedException, ExecutionException { - final MultiLock multiLock = new MultiLock(null); + final MultiLock multiLock = new MultiLock(); final ExecutorService executorService = Executors.newSingleThreadExecutor(); final List> futures = executorService.invokeAll(Arrays.asList(new Downgrade(multiLock, from, to)), LOCK_ACQUISITION_TIMEOUT, TimeUnit.MILLISECONDS); diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/HierarchyTest.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/HierarchyTest.java index 1ab986a..d6be9b3 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/HierarchyTest.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/HierarchyTest.java @@ -125,200 +125,200 @@ public void t1_root_SIX_t1_child_X() throws ExecutionException, InterruptedExcep @Test public void t1_root_IS_t2_child_IS() throws InterruptedException, ExecutionException { - final MultiLock root = new MultiLock(null); + final MultiLock root = new MultiLock(); assertTrue(IS.lock(root)); - final MultiLock child = new MultiLock(root); + final MultiLock child = new HierarchicalMultiLock(root); assertOtherThreadLockableChild(child, IS); } @Test public void t1_root_IS_t2_child_IX() throws InterruptedException, ExecutionException { - final MultiLock root = new MultiLock(null); + final MultiLock root = new MultiLock(); assertTrue(IS.lock(root)); - final MultiLock child = new MultiLock(root); + final MultiLock child = new HierarchicalMultiLock(root); assertOtherThreadLockableChild(child, IX); } @Test public void t1_root_IS_t2_child_S() throws InterruptedException, ExecutionException { - final MultiLock root = new MultiLock(null); + final MultiLock root = new MultiLock(); assertTrue(IS.lock(root)); - final MultiLock child = new MultiLock(root); + final MultiLock child = new HierarchicalMultiLock(root); assertOtherThreadLockableChild(child, S); } @Test public void t1_root_IS_t2_child_SIX() throws InterruptedException, ExecutionException { - final MultiLock root = new MultiLock(null); + final MultiLock root = new MultiLock(); assertTrue(IS.lock(root)); - final MultiLock child = new MultiLock(root); + final MultiLock child = new HierarchicalMultiLock(root); assertOtherThreadLockableChild(child, SIX); } @Test public void t1_root_IS_t2_child_X() throws InterruptedException, ExecutionException { - final MultiLock root = new MultiLock(null); + final MultiLock root = new MultiLock(); assertTrue(IS.lock(root)); - final MultiLock child = new MultiLock(root); + final MultiLock child = new HierarchicalMultiLock(root); assertOtherThreadLockableChild(child, X); } @Test public void t1_root_IX_t2_child_IS() throws InterruptedException, ExecutionException { - final MultiLock root = new MultiLock(null); + final MultiLock root = new MultiLock(); assertTrue(IX.lock(root)); - final MultiLock child = new MultiLock(root); + final MultiLock child = new HierarchicalMultiLock(root); assertOtherThreadLockableChild(child, IS); } @Test public void t1_root_IX_t2_child_IX() throws InterruptedException, ExecutionException { - final MultiLock root = new MultiLock(null); + final MultiLock root = new MultiLock(); assertTrue(IX.lock(root)); - final MultiLock child = new MultiLock(root); + final MultiLock child = new HierarchicalMultiLock(root); assertOtherThreadLockableChild(child, IX); } @Test public void t1_root_IX_t2_child_S() throws InterruptedException, ExecutionException { - final MultiLock root = new MultiLock(null); + final MultiLock root = new MultiLock(); assertTrue(IX.lock(root)); - final MultiLock child = new MultiLock(root); + final MultiLock child = new HierarchicalMultiLock(root); assertOtherThreadLockableChild(child, S); } @Test public void t1_root_IX_t2_child_SIX() throws InterruptedException, ExecutionException { - final MultiLock root = new MultiLock(null); + final MultiLock root = new MultiLock(); assertTrue(IX.lock(root)); - final MultiLock child = new MultiLock(root); + final MultiLock child = new HierarchicalMultiLock(root); assertOtherThreadLockableChild(child, SIX); } @Test public void t1_root_IX_t2_child_X() throws InterruptedException, ExecutionException { - final MultiLock root = new MultiLock(null); + final MultiLock root = new MultiLock(); assertTrue(IX.lock(root)); - final MultiLock child = new MultiLock(root); + final MultiLock child = new HierarchicalMultiLock(root); assertOtherThreadLockableChild(child, SIX); } @Test public void t1_root_S_t2_child_IS() throws InterruptedException, ExecutionException { - final MultiLock root = new MultiLock(null); + final MultiLock root = new MultiLock(); assertTrue(S.lock(root)); - final MultiLock child = new MultiLock(root); + final MultiLock child = new HierarchicalMultiLock(root); assertOtherThreadLockableChild(child, IS); } @Test public void t1_root_S_t2_child_IX() throws InterruptedException, ExecutionException { - final MultiLock root = new MultiLock(null); + final MultiLock root = new MultiLock(); assertTrue(S.lock(root)); - final MultiLock child = new MultiLock(root); + final MultiLock child = new HierarchicalMultiLock(root); // notLockable = test thread holds S on root, other thread attempts IX on child (which attempts IX on root). S is not compatible with IX assertOtherThreadNotLockableChild(child, IX); } @Test public void t1_root_S_t2_child_S() throws InterruptedException, ExecutionException { - final MultiLock root = new MultiLock(null); + final MultiLock root = new MultiLock(); assertTrue(S.lock(root)); - final MultiLock child = new MultiLock(root); + final MultiLock child = new HierarchicalMultiLock(root); assertOtherThreadLockableChild(child, S); } @Test public void t1_root_S_t2_child_SIX() throws InterruptedException, ExecutionException { - final MultiLock root = new MultiLock(null); + final MultiLock root = new MultiLock(); assertTrue(S.lock(root)); - final MultiLock child = new MultiLock(root); + final MultiLock child = new HierarchicalMultiLock(root); // notLockable = test thread holds S on root, other thread attempts SIX on child (which attempts S+IX on root). S is not compatible with IX assertOtherThreadNotLockableChild(child, SIX); } @Test public void t1_root_S_t2_child_X() throws InterruptedException, ExecutionException { - final MultiLock root = new MultiLock(null); + final MultiLock root = new MultiLock(); assertTrue(S.lock(root)); - final MultiLock child = new MultiLock(root); + final MultiLock child = new HierarchicalMultiLock(root); // notLockable = test thread holds S on root, other thread attempts X on child (which attempts IX on root). S is not compatible with IX assertOtherThreadNotLockableChild(child, X); } @Test public void t1_root_SIX_t2_child_IS() throws InterruptedException, ExecutionException { - final MultiLock root = new MultiLock(null); + final MultiLock root = new MultiLock(); assertTrue(SIX.lock(root)); - final MultiLock child = new MultiLock(root); + final MultiLock child = new HierarchicalMultiLock(root); assertOtherThreadLockableChild(child, IS); } @Test public void t1_root_SIX_t2_child_IX() throws InterruptedException, ExecutionException { - final MultiLock root = new MultiLock(null); + final MultiLock root = new MultiLock(); assertTrue(SIX.lock(root)); - final MultiLock child = new MultiLock(root); + final MultiLock child = new HierarchicalMultiLock(root); // notLockable = test thread holds S+IX on root, other thread attempts IX on child (which attempts IX on root). S is not compatible with IX assertOtherThreadNotLockableChild(child, IX); } @Test public void t1_root_SIX_t2_child_S() throws InterruptedException, ExecutionException { - final MultiLock root = new MultiLock(null); + final MultiLock root = new MultiLock(); assertTrue(SIX.lock(root)); - final MultiLock child = new MultiLock(root); + final MultiLock child = new HierarchicalMultiLock(root); assertOtherThreadLockableChild(child, S); } @Test public void t1_root_SIX_t2_child_SIX() throws InterruptedException, ExecutionException { - final MultiLock root = new MultiLock(null); + final MultiLock root = new MultiLock(); assertTrue(SIX.lock(root)); - final MultiLock child = new MultiLock(root); + final MultiLock child = new HierarchicalMultiLock(root); // notLockable = test thread holds S+IX on root, other thread attempts SIX on child (which attempts IS+IX on root). S is not compatible with IX assertOtherThreadNotLockableChild(child, SIX); } @Test public void t1_root_SIX_t2_child_X() throws InterruptedException, ExecutionException { - final MultiLock root = new MultiLock(null); + final MultiLock root = new MultiLock(); assertTrue(SIX.lock(root)); - final MultiLock child = new MultiLock(root); + final MultiLock child = new HierarchicalMultiLock(root); // notLockable = test thread holds S+IX on root, other thread attempts X on child (which attempts IX on root). S is not compatible with IX assertOtherThreadNotLockableChild(child, X); } @Test public void t1_root_X_t2_child_IS() throws InterruptedException, ExecutionException { - final MultiLock root = new MultiLock(null); + final MultiLock root = new MultiLock(); assertTrue(X.lock(root)); - final MultiLock child = new MultiLock(root); + final MultiLock child = new HierarchicalMultiLock(root); // notLockable = test thread holds X on root, other thread attempts IS on child (which attempts IS on root). X is not compatible with IS assertOtherThreadNotLockableChild(child, IS); @@ -326,47 +326,47 @@ public void t1_root_X_t2_child_IS() throws InterruptedException, ExecutionExcept @Test public void t1_root_X_t2_child_IX() throws InterruptedException, ExecutionException { - final MultiLock root = new MultiLock(null); + final MultiLock root = new MultiLock(); assertTrue(X.lock(root)); - final MultiLock child = new MultiLock(root); + final MultiLock child = new HierarchicalMultiLock(root); // notLockable = test thread holds X on root, other thread attempts IX on child (which attempts IX on root). X is not compatible with IX assertOtherThreadNotLockableChild(child, IX); } @Test public void t1_root_X_t2_child_S() throws InterruptedException, ExecutionException { - final MultiLock root = new MultiLock(null); + final MultiLock root = new MultiLock(); assertTrue(X.lock(root)); - final MultiLock child = new MultiLock(root); + final MultiLock child = new HierarchicalMultiLock(root); // notLockable = test thread holds X on root, other thread attempts S on child (which attempts IS on root). X is not compatible with IS assertOtherThreadNotLockableChild(child, S); } @Test public void t1_root_X_t2_child_SIX() throws InterruptedException, ExecutionException { - final MultiLock root = new MultiLock(null); + final MultiLock root = new MultiLock(); assertTrue(X.lock(root)); - final MultiLock child = new MultiLock(root); + final MultiLock child = new HierarchicalMultiLock(root); // notLockable = test thread holds X on root, other thread attempts SIX on child (which attempts IS+IX on root). X is not compatible with IS or IX assertOtherThreadNotLockableChild(child, SIX); } @Test public void t1_root_X_t2_child_X() throws InterruptedException, ExecutionException { - final MultiLock root = new MultiLock(null); + final MultiLock root = new MultiLock(); assertTrue(X.lock(root)); - final MultiLock child = new MultiLock(root); + final MultiLock child = new HierarchicalMultiLock(root); // notLockable = test thread holds X on root, other thread attempts X on child (which attempts IX on root). X is not compatible with IX assertOtherThreadNotLockableChild(child, X); } private static void assertLockRootThenChild(final LockMode rootMode, final LockMode childMode) throws InterruptedException, ExecutionException { - final MultiLock root = new MultiLock(null); - final MultiLock child = new MultiLock(root); + final MultiLock root = new MultiLock(); + final MultiLock child = new HierarchicalMultiLock(root); final ExecutorService executorService = Executors.newSingleThreadExecutor(); final List> futures = executorService.invokeAll(Arrays.asList(new RootAndChildLockAcquirer(root, rootMode, child, childMode)), LOCK_ACQUISITION_TIMEOUT, TimeUnit.MILLISECONDS); @@ -381,8 +381,8 @@ private static void assertLockRootThenChild(final LockMode rootMode, final LockM } private static void assertLockRootThenNotChild(final LockMode rootMode, final LockMode childMode) throws InterruptedException, ExecutionException { - final MultiLock root = new MultiLock(null); - final MultiLock child = new MultiLock(root); + final MultiLock root = new MultiLock(); + final MultiLock child = new HierarchicalMultiLock(root); final ExecutorService executorService = Executors.newSingleThreadExecutor(); final List> futures = executorService.invokeAll(Arrays.asList(new RootAndChildLockAcquirer(root, rootMode, child, childMode)), LOCK_ACQUISITION_TIMEOUT, TimeUnit.MILLISECONDS); diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/Lockable.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/Lockable.java index 26a5963..e94de00 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/Lockable.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/Lockable.java @@ -29,7 +29,7 @@ import java.util.concurrent.locks.*; public abstract class Lockable { - public MultiLock mlock = new MultiLock(null); + public MultiLock mlock = new HierarchicalMultiLock(null); public ReentrantReadWriteLock rwlock = new ReentrantReadWriteLock(); public Lock rlock = rwlock.readLock(); public Lock wlock = rwlock.writeLock(); diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/ReentrancyTest.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/ReentrancyTest.java index e60a4e8..d60125b 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/ReentrancyTest.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/ReentrancyTest.java @@ -51,7 +51,7 @@ public void reentrant_X() throws InterruptedException, ExecutionException { } private static void assertReentrant(final LockMode lockMode) throws InterruptedException, ExecutionException { - final MultiLock multiLock = new MultiLock(null); + final MultiLock multiLock = new MultiLock(); final ExecutorService executorService = Executors.newSingleThreadExecutor(); final List> futures = executorService.invokeAll(Arrays.asList(new Reenter(multiLock, lockMode, REENTER_COUNT)), LOCK_ACQUISITIONS_TIMEOUT, TimeUnit.MILLISECONDS); diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/UpgradeTest.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/UpgradeTest.java index 3a3c569..ef666cc 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/UpgradeTest.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/UpgradeTest.java @@ -78,7 +78,7 @@ public void upgrade_SIX_X() throws InterruptedException, ExecutionException { } private static void assertUpgradeable(final LockMode from, final LockMode to) throws InterruptedException, ExecutionException { - final MultiLock multiLock = new MultiLock(null); + final MultiLock multiLock = new MultiLock(); final ExecutorService executorService = Executors.newSingleThreadExecutor(); final List> futures = executorService.invokeAll(Arrays.asList(new Upgrade(multiLock, from, to)), LOCK_ACQUISITION_TIMEOUT, TimeUnit.MILLISECONDS); From 1936f0a965e2177b90f88b9ff937f32df0af4ef2 Mon Sep 17 00:00:00 2001 From: Adam Retter Date: Sat, 25 Aug 2018 17:25:48 +0530 Subject: [PATCH 08/21] [refactor] Rename methods and sub-classes so they are more similar to this in Java SDK's StampedLock --- .../multilock/HierarchicalMultiLock.java | 24 ++-- .../ac/ic/doc/slurp/multilock/LockMode.java | 10 +- .../ac/ic/doc/slurp/multilock/MultiLock.java | 110 ++++++++++++++---- .../multilock/bank/BankTestMultiLock.java | 28 +++-- .../counter/CounterTestMultiLock.java | 2 +- 5 files changed, 121 insertions(+), 53 deletions(-) diff --git a/src/main/java/uk/ac/ic/doc/slurp/multilock/HierarchicalMultiLock.java b/src/main/java/uk/ac/ic/doc/slurp/multilock/HierarchicalMultiLock.java index 79c72a8..bc3b886 100644 --- a/src/main/java/uk/ac/ic/doc/slurp/multilock/HierarchicalMultiLock.java +++ b/src/main/java/uk/ac/ic/doc/slurp/multilock/HierarchicalMultiLock.java @@ -19,35 +19,35 @@ public HierarchicalMultiLock(@Nullable final MultiLock parent) { } @Override - public boolean lockRead() { + public boolean readLock() { if (parent != null) { - parent.lockIntentionRead(); + parent.intentionReadLock(); } - return super.lockRead(); + return super.readLock(); } @Override - public boolean lockWrite() { + public boolean writeLock() { if (parent != null) { - parent.lockIntentionWrite(); + parent.intentionWriteLock(); } - return super.lockWrite(); + return super.writeLock(); } @Override - public boolean lockIntentionRead() { + public boolean intentionReadLock() { if (parent != null) { - parent.lockIntentionRead(); + parent.intentionReadLock(); } - return super.lockIntentionRead(); + return super.intentionReadLock(); } @Override - public boolean lockIntentionWrite() { + public boolean intentionWriteLock() { if (parent != null) { - parent.lockIntentionWrite(); + parent.intentionWriteLock(); } - return super.lockIntentionWrite(); + return super.intentionWriteLock(); } @Override diff --git a/src/main/java/uk/ac/ic/doc/slurp/multilock/LockMode.java b/src/main/java/uk/ac/ic/doc/slurp/multilock/LockMode.java index 6da5e3a..b7920b8 100644 --- a/src/main/java/uk/ac/ic/doc/slurp/multilock/LockMode.java +++ b/src/main/java/uk/ac/ic/doc/slurp/multilock/LockMode.java @@ -63,23 +63,23 @@ public static boolean lock(final MultiLock multiLock, final LockMode lockMode) { final boolean lockResult; switch (lockMode) { case IS: - lockResult = multiLock.lockIntentionRead(); + lockResult = multiLock.intentionReadLock(); break; case IX: - lockResult = multiLock.lockIntentionWrite(); + lockResult = multiLock.intentionWriteLock(); break; case S: - lockResult = multiLock.lockRead(); + lockResult = multiLock.readLock(); break; case SIX: - lockResult = multiLock.lockRead() && multiLock.lockIntentionWrite(); + lockResult = multiLock.readLock() && multiLock.intentionWriteLock(); break; case X: - lockResult = multiLock.lockWrite(); + lockResult = multiLock.writeLock(); break; default: diff --git a/src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java b/src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java index 0d872ad..3c67237 100644 --- a/src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java +++ b/src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java @@ -49,80 +49,144 @@ public class MultiLock { static final long IS_UNIT = 0x0000000000000001L; final Sync sync; - - final ReadLock readLock; - final WriteLock writeLock; - + + // views + transient ReadLockView readLockView; + transient WriteLockView writeLockView; + transient ReadWriteLockView readWriteLockView; + /** * Constructs a MultiLock. */ public MultiLock() { this.sync = new Sync(); - this.readLock = new ReadLock(); - this.writeLock = new WriteLock(); } - class ReadLock implements Lock { - + final class ReadLockView implements Lock { + + @Override public void lock() { - lockRead(); + readLock(); } + @Override public void unlock() { unlockRead(); } - + + @Override public void lockInterruptibly() throws InterruptedException { throw new UnsupportedOperationException(); } + @Override public Condition newCondition() { throw new UnsupportedOperationException(); } + @Override public boolean tryLock() { throw new UnsupportedOperationException(); } - public boolean tryLock(long time, TimeUnit unit) + @Override + public boolean tryLock(final long time, final TimeUnit unit) throws InterruptedException { throw new UnsupportedOperationException(); } - } - class WriteLock implements Lock { + final class WriteLockView implements Lock { + @Override public void lock() { - lockWrite(); + writeLock(); } + @Override public void unlock() { unlockWrite(); } + @Override public void lockInterruptibly() throws InterruptedException { throw new UnsupportedOperationException(); } + @Override public Condition newCondition() { throw new UnsupportedOperationException(); } + @Override public boolean tryLock() { throw new UnsupportedOperationException(); } - public boolean tryLock(long time, TimeUnit unit) + @Override + public boolean tryLock(final long time, final TimeUnit unit) throws InterruptedException { throw new UnsupportedOperationException(); } + } + + final class ReadWriteLockView implements ReadWriteLock { + @Override + public Lock readLock() { + return asReadLock(); + } + @Override + public Lock writeLock() { + return asWriteLock(); + } + } + + /** + * Returns a plain {@link Lock} view of this MultiLock in which + * the {@link Lock#lock} method is mapped to {@link #readLock()}, + * and similarly for other methods. The returned Lock does not + * support a {@link Condition}; method {@link + * Lock#newCondition()} throws {@code + * UnsupportedOperationException}. + * + * @return the lock + */ + public Lock asReadLock() { + ReadLockView v; + return ((v = readLockView) != null ? v : + (readLockView = new ReadLockView())); + } + + /** + * Returns a plain {@link Lock} view of this MultiLock in which + * the {@link Lock#lock} method is mapped to {@link #writeLock()}, + * and similarly for other methods. The returned Lock does not + * support a {@link Condition}; method {@link + * Lock#newCondition()} throws {@code + * UnsupportedOperationException}. + * + * @return the lock + */ + public Lock asWriteLock() { + WriteLockView v; + return ((v = writeLockView) != null ? v : + (writeLockView = new WriteLockView())); + } + + /** + * Returns a {@link ReadWriteLock} view of this MultiLock in + * which the {@link ReadWriteLock#readLock()} method is mapped to + * {@link #asReadLock()}, and {@link ReadWriteLock#writeLock()} to + * {@link #asWriteLock()}. + * + * @return the lock + */ + public ReadWriteLock asReadWriteLock() { + ReadWriteLockView v; + return ((v = readWriteLockView) != null ? v : + (readWriteLockView = new ReadWriteLockView())); } - - public Lock readLock() { return readLock; } - - public Lock writeLock() { return writeLock; } static class Sync extends AbstractQueuedLongSynchronizer { @@ -388,22 +452,22 @@ final int getWriteHoldCount() { } } - public boolean lockRead() { + public boolean readLock() { sync.acquireShared(S_UNIT); return true; } - public boolean lockWrite() { + public boolean writeLock() { sync.acquire(X_UNIT); return true; } - public boolean lockIntentionRead() { + public boolean intentionReadLock() { sync.acquireShared(IS_UNIT); return true; } - public boolean lockIntentionWrite() { + public boolean intentionWriteLock() { sync.acquireShared(IX_UNIT); return true; } diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/bank/BankTestMultiLock.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/bank/BankTestMultiLock.java index 4bcf9fb..bbad377 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/bank/BankTestMultiLock.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/bank/BankTestMultiLock.java @@ -35,16 +35,17 @@ public BankTestMultiLock(int nWithdraw, int nDeposit, int nBranch, int nBank) { super(nWithdraw, nDeposit, nBranch, nBank); } + @Override public void withdraw(Bank b, Random r) { int branchId = r.nextInt(NUM_BRANCHES); int acctId = r.nextInt(NUM_ACCTS_PER_BRANCH); int amt = r.nextInt(60); - b.mlock.lockIntentionWrite(); + b.mlock.intentionWriteLock(); Branch branch = b.branches[branchId]; - branch.mlock.lockIntentionWrite(); + branch.mlock.intentionWriteLock(); Account acct = branch.accounts[acctId]; - acct.mlock.lockWrite(); + acct.mlock.writeLock(); acct.withdraw(amt); @@ -52,17 +53,18 @@ public void withdraw(Bank b, Random r) { branch.mlock.unlockIntentionWrite(); b.mlock.unlockIntentionWrite(); } - + + @Override public void deposit(Bank b, Random r) { int branchId = r.nextInt(NUM_BRANCHES); int acctId = r.nextInt(NUM_ACCTS_PER_BRANCH); int amt = r.nextInt(60); - b.mlock.lockIntentionWrite(); + b.mlock.intentionWriteLock(); Branch branch = b.branches[branchId]; - branch.mlock.lockIntentionWrite(); + branch.mlock.intentionWriteLock(); Account acct = branch.accounts[acctId]; - acct.mlock.lockWrite(); + acct.mlock.writeLock(); acct.deposit(amt); @@ -70,22 +72,24 @@ public void deposit(Bank b, Random r) { branch.mlock.unlockIntentionWrite(); b.mlock.unlockIntentionWrite(); } - + + @Override public void sumOneBranch(Bank b, Random r) { int branchId = r.nextInt(NUM_BRANCHES); - b.mlock.lockIntentionRead(); + b.mlock.intentionReadLock(); Branch branch = b.branches[branchId]; - branch.mlock.lockRead(); + branch.mlock.readLock(); branch.sumBalances(); branch.mlock.unlockRead(); b.mlock.unlockIntentionRead(); } - + + @Override public void sumAll(Bank b) { - b.mlock.lockRead(); + b.mlock.readLock(); b.sumAll(); diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/counter/CounterTestMultiLock.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/counter/CounterTestMultiLock.java index 3fe438f..3d098cd 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/counter/CounterTestMultiLock.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/counter/CounterTestMultiLock.java @@ -30,7 +30,7 @@ public class CounterTestMultiLock extends CounterTest { @Override public void inc(Counter c) { - c.mlock.lockWrite(); + c.mlock.writeLock(); c.inc(); c.mlock.unlockWrite(); } From 465e03381678a4b2068a55c41b6bba25680f32e5 Mon Sep 17 00:00:00 2001 From: Adam Retter Date: Sun, 26 Aug 2018 11:33:11 +0530 Subject: [PATCH 09/21] [feature] Implement lock interruptibly methods for MultiLock --- .../multilock/HierarchicalMultiLock.java | 64 +++ .../ac/ic/doc/slurp/multilock/LockMode.java | 53 +++ .../ac/ic/doc/slurp/multilock/MultiLock.java | 20 +- .../slurp/multilock/CompatibilityTest.java | 178 ++++++++ .../ic/doc/slurp/multilock/DowngradeTest.java | 80 ++++ .../ic/doc/slurp/multilock/HierarchyTest.java | 405 ++++++++++++++++++ .../doc/slurp/multilock/ReentrancyTest.java | 61 +++ .../ic/doc/slurp/multilock/UpgradeTest.java | 79 ++++ 8 files changed, 938 insertions(+), 2 deletions(-) diff --git a/src/main/java/uk/ac/ic/doc/slurp/multilock/HierarchicalMultiLock.java b/src/main/java/uk/ac/ic/doc/slurp/multilock/HierarchicalMultiLock.java index bc3b886..1643362 100644 --- a/src/main/java/uk/ac/ic/doc/slurp/multilock/HierarchicalMultiLock.java +++ b/src/main/java/uk/ac/ic/doc/slurp/multilock/HierarchicalMultiLock.java @@ -26,6 +26,22 @@ public boolean readLock() { return super.readLock(); } + @Override + public void readLockInterruptibly() throws InterruptedException { + if (parent != null) { + parent.intentionReadLockInterruptibly(); + } + + try { + super.readLockInterruptibly(); + } catch (final InterruptedException e) { + if (parent != null) { + parent.unlockIntentionRead(); + } + throw e; + } + } + @Override public boolean writeLock() { if (parent != null) { @@ -34,6 +50,22 @@ public boolean writeLock() { return super.writeLock(); } + @Override + public void writeLockInterruptibly() throws InterruptedException { + if (parent != null) { + parent.intentionWriteLockInterruptibly(); + } + + try { + super.writeLockInterruptibly(); + } catch (final InterruptedException e) { + if (parent != null) { + parent.unlockIntentionWrite(); + } + throw e; + } + } + @Override public boolean intentionReadLock() { if (parent != null) { @@ -42,6 +74,22 @@ public boolean intentionReadLock() { return super.intentionReadLock(); } + @Override + public void intentionReadLockInterruptibly() throws InterruptedException { + if (parent != null) { + parent.intentionReadLockInterruptibly(); + } + + try { + super.intentionReadLockInterruptibly(); + } catch (final InterruptedException e) { + if (parent != null) { + parent.unlockIntentionRead(); + } + throw e; + } + } + @Override public boolean intentionWriteLock() { if (parent != null) { @@ -50,6 +98,22 @@ public boolean intentionWriteLock() { return super.intentionWriteLock(); } + @Override + public void intentionWriteLockInterruptibly() throws InterruptedException { + if (parent != null) { + parent.intentionWriteLockInterruptibly(); + } + + try { + super.intentionWriteLockInterruptibly(); + } catch (final InterruptedException e) { + if (parent != null) { + parent.unlockIntentionWrite(); + } + throw e; + } + } + @Override public void unlockRead() { super.unlockRead(); diff --git a/src/main/java/uk/ac/ic/doc/slurp/multilock/LockMode.java b/src/main/java/uk/ac/ic/doc/slurp/multilock/LockMode.java index b7920b8..71783f7 100644 --- a/src/main/java/uk/ac/ic/doc/slurp/multilock/LockMode.java +++ b/src/main/java/uk/ac/ic/doc/slurp/multilock/LockMode.java @@ -40,6 +40,17 @@ public boolean lock(final MultiLock multiLock) { return lock(multiLock, this); } + /** + * Locks the MultiLock with this mode, aborting if interrupted. + * + * @param multiLock the MultiLock object. + * + * @throws InterruptedException if the thread was interrupted + */ + public void lockInterruptibly(final MultiLock multiLock) throws InterruptedException { + lockInterruptibly(multiLock, this); + } + /** * Unlocks the MultiLock with this mode. * @@ -89,6 +100,48 @@ public static boolean lock(final MultiLock multiLock, final LockMode lockMode) { return lockResult; } + /** + * Locks the MultiLock with the provided mode, aborting if interrupted. + * + * @param multiLock the MultiLock object. + * @param lockMode the mode to lock the MultiLock. + * + * @throws IllegalArgumentException if an unknown mode is provided. + * @throws InterruptedException if the thread was interrupted + */ + public static void lockInterruptibly(final MultiLock multiLock, final LockMode lockMode) throws InterruptedException { + switch (lockMode) { + case IS: + multiLock.intentionReadLockInterruptibly(); + break; + + case IX: + multiLock.intentionWriteLockInterruptibly(); + break; + + case S: + multiLock.readLockInterruptibly(); + break; + + case SIX: + multiLock.readLockInterruptibly(); + try { + multiLock.intentionWriteLockInterruptibly(); + } catch (final InterruptedException e) { + multiLock.unlockRead(); + throw e; + } + break; + + case X: + multiLock.writeLockInterruptibly(); + break; + + default: + throw new IllegalArgumentException("Unknown lock mode: " + lockMode); + } + } + /** * Unlocks the MultiLock with the provided mode. * diff --git a/src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java b/src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java index 3c67237..a17e500 100644 --- a/src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java +++ b/src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java @@ -76,7 +76,7 @@ public void unlock() { @Override public void lockInterruptibly() throws InterruptedException { - throw new UnsupportedOperationException(); + readLockInterruptibly(); } @Override @@ -110,7 +110,7 @@ public void unlock() { @Override public void lockInterruptibly() throws InterruptedException { - throw new UnsupportedOperationException(); + writeLockInterruptibly(); } @Override @@ -456,21 +456,37 @@ public boolean readLock() { sync.acquireShared(S_UNIT); return true; } + + public void readLockInterruptibly() throws InterruptedException { + sync.acquireSharedInterruptibly(S_UNIT); + } public boolean writeLock() { sync.acquire(X_UNIT); return true; } + + public void writeLockInterruptibly() throws InterruptedException { + sync.acquireInterruptibly(X_UNIT); + } public boolean intentionReadLock() { sync.acquireShared(IS_UNIT); return true; } + + public void intentionReadLockInterruptibly() throws InterruptedException { + sync.acquireSharedInterruptibly(IS_UNIT); + } public boolean intentionWriteLock() { sync.acquireShared(IX_UNIT); return true; } + + public void intentionWriteLockInterruptibly() throws InterruptedException { + sync.acquireSharedInterruptibly(IX_UNIT); + } public void unlockRead() { sync.releaseShared(S_UNIT); diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/CompatibilityTest.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/CompatibilityTest.java index 6dad659..7533529 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/CompatibilityTest.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/CompatibilityTest.java @@ -44,126 +44,251 @@ public void compatible_IS_IS() throws InterruptedException, ExecutionException { assertCompatible(IS, IS); } + @Test + public void compatible_IS_IS_interruptibly() throws InterruptedException, ExecutionException { + assertCompatibleInterruptibly(IS, IS); + } + @Test public void compatible_IS_IX() throws InterruptedException, ExecutionException { assertCompatible(IS, IX); } + @Test + public void compatible_IS_IX_interruptibly() throws InterruptedException, ExecutionException { + assertCompatibleInterruptibly(IS, IX); + } + @Test public void compatible_IS_S() throws InterruptedException, ExecutionException { assertCompatible(IS, S); } + @Test + public void compatible_IS_S_interruptibly() throws InterruptedException, ExecutionException { + assertCompatibleInterruptibly(IS, S); + } + @Test public void compatible_IS_SIX() throws InterruptedException, ExecutionException { assertCompatible(IS, SIX); } + @Test + public void compatible_IS_SIX_interruptibly() throws InterruptedException, ExecutionException { + assertCompatibleInterruptibly(IS, SIX); + } + @Test public void compatible_IS_X() throws InterruptedException, ExecutionException { assertNotCompatible(IS, X); } + @Test + public void compatible_IS_X_interruptibly() throws InterruptedException, ExecutionException { + assertNotCompatibleInterruptibly(IS, X); + } + @Test public void compatible_IX_IS() throws InterruptedException, ExecutionException { assertCompatible(IX, IS); } + @Test + public void compatible_IX_IS_interruptibly() throws InterruptedException, ExecutionException { + assertCompatibleInterruptibly(IX, IS); + } + @Test public void compatible_IX_IX() throws InterruptedException, ExecutionException { assertCompatible(IX, IX); } + @Test + public void compatible_IX_IX_interruptibly() throws InterruptedException, ExecutionException { + assertCompatibleInterruptibly(IX, IX); + } + @Test public void compatible_IX_S() throws InterruptedException, ExecutionException { assertNotCompatible(IX, S); } + @Test + public void compatible_IX_S_interruptibly() throws InterruptedException, ExecutionException { + assertNotCompatibleInterruptibly(IX, S); + } + @Test public void compatible_IX_SIX() throws InterruptedException, ExecutionException { assertNotCompatible(IX, SIX); } + @Test + public void compatible_IX_SIX_interruptibly() throws InterruptedException, ExecutionException { + assertNotCompatibleInterruptibly(IX, SIX); + } + @Test public void compatible_IX_X() throws InterruptedException, ExecutionException { assertNotCompatible(IX, X); } + @Test + public void compatible_IX_X_interruptibly() throws InterruptedException, ExecutionException { + assertNotCompatibleInterruptibly(IX, X); + } + @Test public void compatible_S_IS() throws InterruptedException, ExecutionException { assertCompatible(S, IS); } + @Test + public void compatible_S_IS_interruptibly() throws InterruptedException, ExecutionException { + assertCompatibleInterruptibly(S, IS); + } + @Test public void compatible_S_IX() throws InterruptedException, ExecutionException { assertNotCompatible(S, IX); } + @Test + public void compatible_S_IX_interruptibly() throws InterruptedException, ExecutionException { + assertNotCompatibleInterruptibly(S, IX); + } + @Test public void compatible_S_S() throws InterruptedException, ExecutionException { assertCompatible(S, S); } + @Test + public void compatible_S_S_interruptibly() throws InterruptedException, ExecutionException { + assertCompatibleInterruptibly(S, S); + } + @Test public void compatible_S_SIX() throws InterruptedException, ExecutionException { assertNotCompatible(S, SIX); } + @Test + public void compatible_S_SIX_interruptibly() throws InterruptedException, ExecutionException { + assertNotCompatibleInterruptibly(S, SIX); + } + @Test public void compatible_S_X() throws InterruptedException, ExecutionException { assertNotCompatible(S, X); } + @Test + public void compatible_S_X_interruptibly() throws InterruptedException, ExecutionException { + assertNotCompatibleInterruptibly(S, X); + } + @Test public void compatible_SIX_IS() throws InterruptedException, ExecutionException { assertCompatible(SIX, IS); } + @Test + public void compatible_SIX_IS_interruptibly() throws InterruptedException, ExecutionException { + assertCompatibleInterruptibly(SIX, IS); + } + @Test public void compatible_SIX_IX() throws InterruptedException, ExecutionException { assertNotCompatible(SIX, IX); } + @Test + public void compatible_SIX_IX_interruptibly() throws InterruptedException, ExecutionException { + assertNotCompatibleInterruptibly(SIX, IX); + } + @Test public void compatible_SIX_S() throws InterruptedException, ExecutionException { assertNotCompatible(SIX, S); } + @Test + public void compatible_SIX_S_interruptibly() throws InterruptedException, ExecutionException { + assertNotCompatibleInterruptibly(SIX, S); + } + @Test public void compatible_SIX_SIX() throws InterruptedException, ExecutionException { assertNotCompatible(SIX, SIX); } + @Test + public void compatible_SIX_SIX_interruptibly() throws InterruptedException, ExecutionException { + assertNotCompatibleInterruptibly(SIX, SIX); + } + @Test public void compatible_SIX_X() throws InterruptedException, ExecutionException { assertNotCompatible(SIX, X); } + @Test + public void compatible_SIX_X_interruptibly() throws InterruptedException, ExecutionException { + assertNotCompatibleInterruptibly(SIX, X); + } + @Test public void compatible_X_IS() throws InterruptedException, ExecutionException { assertNotCompatible(X, IS); } + @Test + public void compatible_X_IS_interruptibly() throws InterruptedException, ExecutionException { + assertNotCompatibleInterruptibly(X, IS); + } + @Test public void compatible_X_IX() throws InterruptedException, ExecutionException { assertNotCompatible(X, IX); } + @Test + public void compatible_X_IX_interruptibly() throws InterruptedException, ExecutionException { + assertNotCompatibleInterruptibly(X, IX); + } + @Test public void compatible_X_S() throws InterruptedException, ExecutionException { assertNotCompatible(X, S); } + @Test + public void compatible_X_S_interruptibly() throws InterruptedException, ExecutionException { + assertNotCompatibleInterruptibly(X, S); + } + @Test public void compatible_X_SIX() throws InterruptedException, ExecutionException { assertNotCompatible(X, SIX); } + @Test + public void compatible_X_SIX_interruptibly() throws InterruptedException, ExecutionException { + assertNotCompatibleInterruptibly(X, SIX); + } + @Test public void compatible_X_X() throws InterruptedException, ExecutionException { assertNotCompatible(X, X); } + @Test + public void compatible_X_X_interruptibly() throws InterruptedException, ExecutionException { + assertNotCompatibleInterruptibly(X, X); + } + private static void assertCompatible(final LockMode mode1, final LockMode mode2) throws InterruptedException, ExecutionException { final List> futures = checkCompatibility(mode1, mode2); for (final Future future : futures) { @@ -174,6 +299,16 @@ private static void assertCompatible(final LockMode mode1, final LockMode mode2) } } + private static void assertCompatibleInterruptibly(final LockMode mode1, final LockMode mode2) throws InterruptedException, ExecutionException { + final List> futures = checkCompatibilityInterruptibly(mode1, mode2); + for (final Future future : futures) { + assertTrue(future.isDone()); + assertFalse(future.isCancelled()); + + assertTrue(future.get()); + } + } + private static void assertNotCompatible(final LockMode mode1, final LockMode mode2) throws InterruptedException, ExecutionException { final List> futures = checkCompatibility(mode1, mode2); for (final Future future : futures) { @@ -183,6 +318,15 @@ private static void assertNotCompatible(final LockMode mode1, final LockMode mod } } + private static void assertNotCompatibleInterruptibly(final LockMode mode1, final LockMode mode2) throws InterruptedException, ExecutionException { + final List> futures = checkCompatibilityInterruptibly(mode1, mode2); + for (final Future future : futures) { + assertTrue(future.isDone()); + + assertTrue(future.isCancelled()); + } + } + private static List> checkCompatibility(final LockMode mode1, final LockMode mode2) throws InterruptedException { final MultiLock multiLock = new MultiLock(); @@ -195,6 +339,18 @@ private static List> checkCompatibility(final LockMode mode1, fi return executorService.invokeAll(Arrays.asList(thread1, thread2), LOCK_ACQUISITION_TIMEOUT, TimeUnit.MILLISECONDS); } + private static List> checkCompatibilityInterruptibly(final LockMode mode1, final LockMode mode2) throws InterruptedException { + final MultiLock multiLock = new MultiLock(); + + final CountDownLatch latch = new CountDownLatch(2); + + final Callable thread1 = new LockInterruptiblyAcquirer(multiLock, mode1, latch); + final Callable thread2 = new LockInterruptiblyAcquirer(multiLock, mode2, latch); + + final ExecutorService executorService = Executors.newFixedThreadPool(2); + return executorService.invokeAll(Arrays.asList(thread1, thread2), LOCK_ACQUISITION_TIMEOUT, TimeUnit.MILLISECONDS); + } + private static class LockAcquirer implements Callable { private final MultiLock multiLock; private final LockMode lockMode; @@ -216,4 +372,26 @@ public Boolean call() throws Exception { return lockResult; } } + + private static class LockInterruptiblyAcquirer implements Callable { + private final MultiLock multiLock; + private final LockMode lockMode; + private final CountDownLatch latch; + + public LockInterruptiblyAcquirer(final MultiLock multiLock, final LockMode lockMode, final CountDownLatch latch) { + this.multiLock = multiLock; + this.lockMode = lockMode; + this.latch = latch; + } + + @Override + public Boolean call() throws InterruptedException { + lockMode.lockInterruptibly(multiLock); + + latch.countDown(); + latch.await(); + + return true; + } + } } diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/DowngradeTest.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/DowngradeTest.java index b80b5ba..696aeb7 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/DowngradeTest.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/DowngradeTest.java @@ -37,46 +37,91 @@ public void downgrade_IX_IS() throws InterruptedException, ExecutionException { assertDowngradable(IX, IS); } + @Test + public void downgrade_IX_IS_interruptibly() throws InterruptedException, ExecutionException { + assertDowngradableInterruptibly(IX, IS); + } + @Test public void downgrade_S_IS() throws InterruptedException, ExecutionException { assertDowngradable(S, IS); } + @Test + public void downgrade_S_IS_interruptibly() throws InterruptedException, ExecutionException { + assertDowngradableInterruptibly(S, IS); + } + @Test public void downgrade_SIX_IX() throws InterruptedException, ExecutionException { assertDowngradable(SIX, IX); } + @Test + public void downgrade_SIX_IX_interruptibly() throws InterruptedException, ExecutionException { + assertDowngradableInterruptibly(SIX, IX); + } + @Test public void downgrade_SIX_S() throws InterruptedException, ExecutionException { assertDowngradable(SIX, S); } + @Test + public void downgrade_SIX_S_interruptibly() throws InterruptedException, ExecutionException { + assertDowngradableInterruptibly(SIX, S); + } + @Test public void downgrade_SIX_IS() throws InterruptedException, ExecutionException { assertDowngradable(SIX, IS); } + @Test + public void downgrade_SIX_IS_interruptibly() throws InterruptedException, ExecutionException { + assertDowngradableInterruptibly(SIX, IS); + } + @Test public void downgrade_X_SIX() throws InterruptedException, ExecutionException { assertDowngradable(X, SIX); } + @Test + public void downgrade_X_SIX_interruptibly() throws InterruptedException, ExecutionException { + assertDowngradableInterruptibly(X, SIX); + } + @Test public void downgrade_X_IX() throws InterruptedException, ExecutionException { assertDowngradable(X, IX); } + @Test + public void downgrade_X_IX_interruptibly() throws InterruptedException, ExecutionException { + assertDowngradableInterruptibly(X, IX); + } + @Test public void downgrade_X_S() throws InterruptedException, ExecutionException { assertDowngradable(X, S); } + @Test + public void downgrade_X_S_interruptibly() throws InterruptedException, ExecutionException { + assertDowngradableInterruptibly(X, S); + } + @Test public void downgrade_X_IS() throws InterruptedException, ExecutionException { assertDowngradable(X, IS); } + @Test + public void downgrade_X_IS_interruptibly() throws InterruptedException, ExecutionException { + assertDowngradableInterruptibly(X, IS); + } + private static void assertDowngradable(final LockMode from, final LockMode to) throws InterruptedException, ExecutionException { final MultiLock multiLock = new MultiLock(); @@ -91,6 +136,20 @@ private static void assertDowngradable(final LockMode from, final LockMode to) t } } + private static void assertDowngradableInterruptibly(final LockMode from, final LockMode to) throws InterruptedException, ExecutionException { + final MultiLock multiLock = new MultiLock(); + + final ExecutorService executorService = Executors.newSingleThreadExecutor(); + final List> futures = executorService.invokeAll(Arrays.asList(new DowngradeInterruptibly(multiLock, from, to)), LOCK_ACQUISITION_TIMEOUT, TimeUnit.MILLISECONDS); + + for (final Future future : futures) { + assertTrue(future.isDone()); + assertFalse(future.isCancelled()); + + assertTrue(future.get()); + } + } + private static class Downgrade implements Callable { private final MultiLock multiLock; private final LockMode from; @@ -114,4 +173,25 @@ public Boolean call() { return false; } } + + private static class DowngradeInterruptibly implements Callable { + private final MultiLock multiLock; + private final LockMode from; + private final LockMode to; + + private DowngradeInterruptibly(final MultiLock multiLock, final LockMode from, final LockMode to) { + this.multiLock = multiLock; + this.from = from; + this.to = to; + } + + @Override + public Boolean call() throws InterruptedException { + from.lockInterruptibly(multiLock); + to.lockInterruptibly(multiLock); + from.unlock(multiLock); + + return true; + } + } } diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/HierarchyTest.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/HierarchyTest.java index d6be9b3..91a2c50 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/HierarchyTest.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/HierarchyTest.java @@ -41,54 +41,107 @@ public void t1_root_IS_t1_child_IS() throws ExecutionException, InterruptedExcep assertLockRootThenChild(IS, IS); } + @Test + public void t1_root_IS_t1_child_IS_interruptibly() throws ExecutionException, InterruptedException { + assertLockRootThenChildInterruptibly(IS, IS); + } + // TODO - page 7 of Gray's paper - Granularity of Locks in a Shared Data Base @Test public void t1_root_IS_t1_child_IX() throws ExecutionException, InterruptedException { assertLockRootThenNotChild(IS, IX); } + // TODO - page 7 of Gray's paper - Granularity of Locks in a Shared Data Base + @Test + public void t1_root_IS_t1_child_IX_interruptibly() throws ExecutionException, InterruptedException { + assertLockRootThenNotChildInterruptibly(IS, IX); + } + @Test public void t1_root_IS_t1_child_S() throws ExecutionException, InterruptedException { assertLockRootThenChild(IS, S); } + @Test + public void t1_root_IS_t1_child_S_interruptibly() throws ExecutionException, InterruptedException { + assertLockRootThenChildInterruptibly(IS, S); + } + // TODO - page 7 of Gray's paper - Granularity of Locks in a Shared Data Base @Test public void t1_root_IS_t1_child_SIX() throws ExecutionException, InterruptedException { assertLockRootThenNotChild(IS, SIX); } + // TODO - page 7 of Gray's paper - Granularity of Locks in a Shared Data Base + @Test + public void t1_root_IS_t1_child_SIX_interruptibly() throws ExecutionException, InterruptedException { + assertLockRootThenNotChildInterruptibly(IS, SIX); + } + // TODO - page 7 of Gray's paper - Granularity of Locks in a Shared Data Base @Test public void t1_root_IS_t1_child_X() throws ExecutionException, InterruptedException { assertLockRootThenNotChild(IS, X); } + // TODO - page 7 of Gray's paper - Granularity of Locks in a Shared Data Base + @Test + public void t1_root_IS_t1_child_X_interruptibly() throws ExecutionException, InterruptedException { + assertLockRootThenNotChildInterruptibly(IS, X); + } + @Test public void t1_root_IX_t1_child_IS() throws ExecutionException, InterruptedException { assertLockRootThenChild(IX, IS); } + @Test + public void t1_root_IX_t1_child_IS_interruptibly() throws ExecutionException, InterruptedException { + assertLockRootThenChildInterruptibly(IX, IS); + } + @Test public void t1_root_IX_t1_child_IX() throws ExecutionException, InterruptedException { assertLockRootThenChild(IX, IX); } + @Test + public void t1_root_IX_t1_child_IX_interruptibly() throws ExecutionException, InterruptedException { + assertLockRootThenChildInterruptibly(IX, IX); + } + @Test public void t1_root_IX_t1_child_S() throws ExecutionException, InterruptedException { assertLockRootThenChild(IX, S); } + @Test + public void t1_root_IX_t1_child_S_interruptibly() throws ExecutionException, InterruptedException { + assertLockRootThenChildInterruptibly(IX, S); + } + @Test public void t1_root_IX_t1_child_SIX() throws ExecutionException, InterruptedException { assertLockRootThenChild(IX, SIX); } + @Test + public void t1_root_IX_t1_child_SIX_interruptibly() throws ExecutionException, InterruptedException { + assertLockRootThenChildInterruptibly(IX, SIX); + } + @Test public void t1_root_IX_t1_child_X() throws ExecutionException, InterruptedException { assertLockRootThenChild(IX, X); } + @Test + public void t1_root_IX_t1_child_X_interruptibly() throws ExecutionException, InterruptedException { + assertLockRootThenChildInterruptibly(IX, X); + } + //TODO(AR) need tests for t1_root_S_child... -- I think it's likely that S on root should only be able to get S on child // TODO - page 7 of Gray's paper - Granularity of Locks in a Shared Data Base @@ -97,27 +150,54 @@ public void t1_root_SIX_t1_child_IS() throws ExecutionException, InterruptedExce assertLockRootThenNotChild(SIX, IS); } + // TODO - page 7 of Gray's paper - Granularity of Locks in a Shared Data Base + @Test + public void t1_root_SIX_t1_child_IS_interruptibly() throws ExecutionException, InterruptedException { + assertLockRootThenNotChildInterruptibly(SIX, IS); + } + @Test public void t1_root_SIX_t1_child_IX() throws ExecutionException, InterruptedException { assertLockRootThenChild(SIX, IX); } + @Test + public void t1_root_SIX_t1_child_IX_interruptibly() throws ExecutionException, InterruptedException { + assertLockRootThenChildInterruptibly(SIX, IX); + } + // TODO - page 7 of Gray's paper - Granularity of Locks in a Shared Data Base @Test public void t1_root_SIX_t1_child_S() throws ExecutionException, InterruptedException { assertLockRootThenNotChild(SIX, S); } + // TODO - page 7 of Gray's paper - Granularity of Locks in a Shared Data Base + @Test + public void t1_root_SIX_t1_child_S_interruptibly() throws ExecutionException, InterruptedException { + assertLockRootThenNotChildInterruptibly(SIX, S); + } + @Test public void t1_root_SIX_t1_child_SIX() throws ExecutionException, InterruptedException { assertLockRootThenChild(SIX, SIX); } + @Test + public void t1_root_SIX_t1_child_SIX_interruptibly() throws ExecutionException, InterruptedException { + assertLockRootThenChildInterruptibly(SIX, SIX); + } + @Test public void t1_root_SIX_t1_child_X() throws ExecutionException, InterruptedException { assertLockRootThenChild(SIX, X); } + @Test + public void t1_root_SIX_t1_child_X_interruptibly() throws ExecutionException, InterruptedException { + assertLockRootThenChildInterruptibly(SIX, X); + } + //TODO(AR) need tests for t1_root_X_child... -- I think it's likely that X on root should only be able to get X on child @@ -133,6 +213,16 @@ public void t1_root_IS_t2_child_IS() throws InterruptedException, ExecutionExcep } + @Test + public void t1_root_IS_t2_child_IS_interruptibly() throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(); + IS.lockInterruptibly(root); + + final MultiLock child = new HierarchicalMultiLock(root); + assertOtherThreadLockableChildInterruptibly(child, IS); + + } + @Test public void t1_root_IS_t2_child_IX() throws InterruptedException, ExecutionException { final MultiLock root = new MultiLock(); @@ -142,6 +232,15 @@ public void t1_root_IS_t2_child_IX() throws InterruptedException, ExecutionExcep assertOtherThreadLockableChild(child, IX); } + @Test + public void t1_root_IS_t2_child_IX_interruptibly() throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(); + IS.lockInterruptibly(root); + + final MultiLock child = new HierarchicalMultiLock(root); + assertOtherThreadLockableChildInterruptibly(child, IX); + } + @Test public void t1_root_IS_t2_child_S() throws InterruptedException, ExecutionException { final MultiLock root = new MultiLock(); @@ -151,6 +250,15 @@ public void t1_root_IS_t2_child_S() throws InterruptedException, ExecutionExcept assertOtherThreadLockableChild(child, S); } + @Test + public void t1_root_IS_t2_child_S_interruptibly() throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(); + IS.lockInterruptibly(root); + + final MultiLock child = new HierarchicalMultiLock(root); + assertOtherThreadLockableChildInterruptibly(child, S); + } + @Test public void t1_root_IS_t2_child_SIX() throws InterruptedException, ExecutionException { final MultiLock root = new MultiLock(); @@ -160,6 +268,15 @@ public void t1_root_IS_t2_child_SIX() throws InterruptedException, ExecutionExce assertOtherThreadLockableChild(child, SIX); } + @Test + public void t1_root_IS_t2_child_SIX_interruptibly() throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(); + IS.lockInterruptibly(root); + + final MultiLock child = new HierarchicalMultiLock(root); + assertOtherThreadLockableChildInterruptibly(child, SIX); + } + @Test public void t1_root_IS_t2_child_X() throws InterruptedException, ExecutionException { final MultiLock root = new MultiLock(); @@ -169,6 +286,15 @@ public void t1_root_IS_t2_child_X() throws InterruptedException, ExecutionExcept assertOtherThreadLockableChild(child, X); } + @Test + public void t1_root_IS_t2_child_X_interruptibly() throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(); + IS.lockInterruptibly(root); + + final MultiLock child = new HierarchicalMultiLock(root); + assertOtherThreadLockableChildInterruptibly(child, X); + } + @Test public void t1_root_IX_t2_child_IS() throws InterruptedException, ExecutionException { final MultiLock root = new MultiLock(); @@ -179,6 +305,16 @@ public void t1_root_IX_t2_child_IS() throws InterruptedException, ExecutionExcep } + @Test + public void t1_root_IX_t2_child_IS_interruptibly() throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(); + IX.lockInterruptibly(root); + + final MultiLock child = new HierarchicalMultiLock(root); + assertOtherThreadLockableChildInterruptibly(child, IS); + + } + @Test public void t1_root_IX_t2_child_IX() throws InterruptedException, ExecutionException { final MultiLock root = new MultiLock(); @@ -188,6 +324,15 @@ public void t1_root_IX_t2_child_IX() throws InterruptedException, ExecutionExcep assertOtherThreadLockableChild(child, IX); } + @Test + public void t1_root_IX_t2_child_IX_interruptibly() throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(); + IX.lockInterruptibly(root); + + final MultiLock child = new HierarchicalMultiLock(root); + assertOtherThreadLockableChildInterruptibly(child, IX); + } + @Test public void t1_root_IX_t2_child_S() throws InterruptedException, ExecutionException { final MultiLock root = new MultiLock(); @@ -197,6 +342,15 @@ public void t1_root_IX_t2_child_S() throws InterruptedException, ExecutionExcept assertOtherThreadLockableChild(child, S); } + @Test + public void t1_root_IX_t2_child_S_interruptibly() throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(); + IX.lockInterruptibly(root); + + final MultiLock child = new HierarchicalMultiLock(root); + assertOtherThreadLockableChildInterruptibly(child, S); + } + @Test public void t1_root_IX_t2_child_SIX() throws InterruptedException, ExecutionException { final MultiLock root = new MultiLock(); @@ -206,6 +360,15 @@ public void t1_root_IX_t2_child_SIX() throws InterruptedException, ExecutionExce assertOtherThreadLockableChild(child, SIX); } + @Test + public void t1_root_IX_t2_child_SIX_interruptibly() throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(); + IX.lockInterruptibly(root); + + final MultiLock child = new HierarchicalMultiLock(root); + assertOtherThreadLockableChildInterruptibly(child, SIX); + } + @Test public void t1_root_IX_t2_child_X() throws InterruptedException, ExecutionException { final MultiLock root = new MultiLock(); @@ -215,6 +378,15 @@ public void t1_root_IX_t2_child_X() throws InterruptedException, ExecutionExcept assertOtherThreadLockableChild(child, SIX); } + @Test + public void t1_root_IX_t2_child_X_interruptibly() throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(); + IX.lockInterruptibly(root); + + final MultiLock child = new HierarchicalMultiLock(root); + assertOtherThreadLockableChildInterruptibly(child, SIX); + } + @Test public void t1_root_S_t2_child_IS() throws InterruptedException, ExecutionException { final MultiLock root = new MultiLock(); @@ -222,7 +394,15 @@ public void t1_root_S_t2_child_IS() throws InterruptedException, ExecutionExcept final MultiLock child = new HierarchicalMultiLock(root); assertOtherThreadLockableChild(child, IS); + } + + @Test + public void t1_root_S_t2_child_IS_interruptibly() throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(); + S.lockInterruptibly(root); + final MultiLock child = new HierarchicalMultiLock(root); + assertOtherThreadLockableChildInterruptibly(child, IS); } @Test @@ -235,6 +415,16 @@ public void t1_root_S_t2_child_IX() throws InterruptedException, ExecutionExcept assertOtherThreadNotLockableChild(child, IX); } + @Test + public void t1_root_S_t2_child_IX_interruptibly() throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(); + S.lockInterruptibly(root); + + final MultiLock child = new HierarchicalMultiLock(root); + // notLockable = test thread holds S on root, other thread attempts IX on child (which attempts IX on root). S is not compatible with IX + assertOtherThreadNotLockableChildInterruptibly(child, IX); + } + @Test public void t1_root_S_t2_child_S() throws InterruptedException, ExecutionException { final MultiLock root = new MultiLock(); @@ -244,6 +434,15 @@ public void t1_root_S_t2_child_S() throws InterruptedException, ExecutionExcepti assertOtherThreadLockableChild(child, S); } + @Test + public void t1_root_S_t2_child_S_interruptibly() throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(); + S.lockInterruptibly(root); + + final MultiLock child = new HierarchicalMultiLock(root); + assertOtherThreadLockableChildInterruptibly(child, S); + } + @Test public void t1_root_S_t2_child_SIX() throws InterruptedException, ExecutionException { final MultiLock root = new MultiLock(); @@ -254,6 +453,16 @@ public void t1_root_S_t2_child_SIX() throws InterruptedException, ExecutionExcep assertOtherThreadNotLockableChild(child, SIX); } + @Test + public void t1_root_S_t2_child_SIX_interruptibly() throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(); + S.lockInterruptibly(root); + + final MultiLock child = new HierarchicalMultiLock(root); + // notLockable = test thread holds S on root, other thread attempts SIX on child (which attempts S+IX on root). S is not compatible with IX + assertOtherThreadNotLockableChildInterruptibly(child, SIX); + } + @Test public void t1_root_S_t2_child_X() throws InterruptedException, ExecutionException { final MultiLock root = new MultiLock(); @@ -264,6 +473,16 @@ public void t1_root_S_t2_child_X() throws InterruptedException, ExecutionExcepti assertOtherThreadNotLockableChild(child, X); } + @Test + public void t1_root_S_t2_child_X_interruptibly() throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(); + S.lockInterruptibly(root); + + final MultiLock child = new HierarchicalMultiLock(root); + // notLockable = test thread holds S on root, other thread attempts X on child (which attempts IX on root). S is not compatible with IX + assertOtherThreadNotLockableChildInterruptibly(child, X); + } + @Test public void t1_root_SIX_t2_child_IS() throws InterruptedException, ExecutionException { final MultiLock root = new MultiLock(); @@ -271,7 +490,15 @@ public void t1_root_SIX_t2_child_IS() throws InterruptedException, ExecutionExce final MultiLock child = new HierarchicalMultiLock(root); assertOtherThreadLockableChild(child, IS); + } + @Test + public void t1_root_SIX_t2_child_IS_interruptibly() throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(); + SIX.lockInterruptibly(root); + + final MultiLock child = new HierarchicalMultiLock(root); + assertOtherThreadLockableChildInterruptibly(child, IS); } @Test @@ -284,6 +511,16 @@ public void t1_root_SIX_t2_child_IX() throws InterruptedException, ExecutionExce assertOtherThreadNotLockableChild(child, IX); } + @Test + public void t1_root_SIX_t2_child_IX_interruptibly() throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(); + SIX.lockInterruptibly(root); + + final MultiLock child = new HierarchicalMultiLock(root); + // notLockable = test thread holds S+IX on root, other thread attempts IX on child (which attempts IX on root). S is not compatible with IX + assertOtherThreadNotLockableChildInterruptibly(child, IX); + } + @Test public void t1_root_SIX_t2_child_S() throws InterruptedException, ExecutionException { final MultiLock root = new MultiLock(); @@ -293,6 +530,15 @@ public void t1_root_SIX_t2_child_S() throws InterruptedException, ExecutionExcep assertOtherThreadLockableChild(child, S); } + @Test + public void t1_root_SIX_t2_child_S_interruptibly() throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(); + SIX.lockInterruptibly(root); + + final MultiLock child = new HierarchicalMultiLock(root); + assertOtherThreadLockableChildInterruptibly(child, S); + } + @Test public void t1_root_SIX_t2_child_SIX() throws InterruptedException, ExecutionException { final MultiLock root = new MultiLock(); @@ -303,6 +549,16 @@ public void t1_root_SIX_t2_child_SIX() throws InterruptedException, ExecutionExc assertOtherThreadNotLockableChild(child, SIX); } + @Test + public void t1_root_SIX_t2_child_SIX_interruptibly() throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(); + SIX.lockInterruptibly(root); + + final MultiLock child = new HierarchicalMultiLock(root); + // notLockable = test thread holds S+IX on root, other thread attempts SIX on child (which attempts IS+IX on root). S is not compatible with IX + assertOtherThreadNotLockableChildInterruptibly(child, SIX); + } + @Test public void t1_root_SIX_t2_child_X() throws InterruptedException, ExecutionException { final MultiLock root = new MultiLock(); @@ -313,6 +569,16 @@ public void t1_root_SIX_t2_child_X() throws InterruptedException, ExecutionExcep assertOtherThreadNotLockableChild(child, X); } + @Test + public void t1_root_SIX_t2_child_X_interruptibly() throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(); + SIX.lockInterruptibly(root); + + final MultiLock child = new HierarchicalMultiLock(root); + // notLockable = test thread holds S+IX on root, other thread attempts X on child (which attempts IX on root). S is not compatible with IX + assertOtherThreadNotLockableChildInterruptibly(child, X); + } + @Test public void t1_root_X_t2_child_IS() throws InterruptedException, ExecutionException { final MultiLock root = new MultiLock(); @@ -321,7 +587,16 @@ public void t1_root_X_t2_child_IS() throws InterruptedException, ExecutionExcept final MultiLock child = new HierarchicalMultiLock(root); // notLockable = test thread holds X on root, other thread attempts IS on child (which attempts IS on root). X is not compatible with IS assertOtherThreadNotLockableChild(child, IS); + } + @Test + public void t1_root_X_t2_child_IS_interruptibly() throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(); + X.lockInterruptibly(root); + + final MultiLock child = new HierarchicalMultiLock(root); + // notLockable = test thread holds X on root, other thread attempts IS on child (which attempts IS on root). X is not compatible with IS + assertOtherThreadNotLockableChildInterruptibly(child, IS); } @Test @@ -334,6 +609,16 @@ public void t1_root_X_t2_child_IX() throws InterruptedException, ExecutionExcept assertOtherThreadNotLockableChild(child, IX); } + @Test + public void t1_root_X_t2_child_IX_interruptibly() throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(); + X.lockInterruptibly(root); + + final MultiLock child = new HierarchicalMultiLock(root); + // notLockable = test thread holds X on root, other thread attempts IX on child (which attempts IX on root). X is not compatible with IX + assertOtherThreadNotLockableChildInterruptibly(child, IX); + } + @Test public void t1_root_X_t2_child_S() throws InterruptedException, ExecutionException { final MultiLock root = new MultiLock(); @@ -344,6 +629,16 @@ public void t1_root_X_t2_child_S() throws InterruptedException, ExecutionExcepti assertOtherThreadNotLockableChild(child, S); } + @Test + public void t1_root_X_t2_child_S_interruptibly() throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(); + X.lockInterruptibly(root); + + final MultiLock child = new HierarchicalMultiLock(root); + // notLockable = test thread holds X on root, other thread attempts S on child (which attempts IS on root). X is not compatible with IS + assertOtherThreadNotLockableChildInterruptibly(child, S); + } + @Test public void t1_root_X_t2_child_SIX() throws InterruptedException, ExecutionException { final MultiLock root = new MultiLock(); @@ -354,6 +649,16 @@ public void t1_root_X_t2_child_SIX() throws InterruptedException, ExecutionExcep assertOtherThreadNotLockableChild(child, SIX); } + @Test + public void t1_root_X_t2_child_SIX_interruptibly() throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(); + X.lockInterruptibly(root); + + final MultiLock child = new HierarchicalMultiLock(root); + // notLockable = test thread holds X on root, other thread attempts SIX on child (which attempts IS+IX on root). X is not compatible with IS or IX + assertOtherThreadNotLockableChildInterruptibly(child, SIX); + } + @Test public void t1_root_X_t2_child_X() throws InterruptedException, ExecutionException { final MultiLock root = new MultiLock(); @@ -364,6 +669,16 @@ public void t1_root_X_t2_child_X() throws InterruptedException, ExecutionExcepti assertOtherThreadNotLockableChild(child, X); } + @Test + public void t1_root_X_t2_child_X_interruptibly() throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(); + X.lockInterruptibly(root); + + final MultiLock child = new HierarchicalMultiLock(root); + // notLockable = test thread holds X on root, other thread attempts X on child (which attempts IX on root). X is not compatible with IX + assertOtherThreadNotLockableChildInterruptibly(child, X); + } + private static void assertLockRootThenChild(final LockMode rootMode, final LockMode childMode) throws InterruptedException, ExecutionException { final MultiLock root = new MultiLock(); final MultiLock child = new HierarchicalMultiLock(root); @@ -380,6 +695,22 @@ private static void assertLockRootThenChild(final LockMode rootMode, final LockM } } + private static void assertLockRootThenChildInterruptibly(final LockMode rootMode, final LockMode childMode) throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(); + final MultiLock child = new HierarchicalMultiLock(root); + + final ExecutorService executorService = Executors.newSingleThreadExecutor(); + final List> futures = executorService.invokeAll(Arrays.asList(new RootAndChildLockInterruptiblyAcquirer(root, rootMode, child, childMode)), LOCK_ACQUISITION_TIMEOUT, TimeUnit.MILLISECONDS); + + for (final Future future : futures) { + assertTrue(future.isDone()); + + assertFalse(future.isCancelled()); + + assertTrue(future.get()); + } + } + private static void assertLockRootThenNotChild(final LockMode rootMode, final LockMode childMode) throws InterruptedException, ExecutionException { final MultiLock root = new MultiLock(); final MultiLock child = new HierarchicalMultiLock(root); @@ -394,6 +725,20 @@ private static void assertLockRootThenNotChild(final LockMode rootMode, final Lo } } + private static void assertLockRootThenNotChildInterruptibly(final LockMode rootMode, final LockMode childMode) throws InterruptedException, ExecutionException { + final MultiLock root = new MultiLock(); + final MultiLock child = new HierarchicalMultiLock(root); + + final ExecutorService executorService = Executors.newSingleThreadExecutor(); + final List> futures = executorService.invokeAll(Arrays.asList(new RootAndChildLockInterruptiblyAcquirer(root, rootMode, child, childMode)), LOCK_ACQUISITION_TIMEOUT, TimeUnit.MILLISECONDS); + + for (final Future future : futures) { + assertTrue(future.isDone()); + + assertTrue(future.isCancelled()); + } + } + private static void assertOtherThreadLockableChild(final MultiLock child, final LockMode childMode) throws InterruptedException, ExecutionException { final ExecutorService executorService = Executors.newSingleThreadExecutor(); final List> futures = executorService.invokeAll(Arrays.asList(new ChildLockAcquirer(child, childMode)), LOCK_ACQUISITION_TIMEOUT, TimeUnit.MILLISECONDS); @@ -406,6 +751,18 @@ private static void assertOtherThreadLockableChild(final MultiLock child, final } } + private static void assertOtherThreadLockableChildInterruptibly(final MultiLock child, final LockMode childMode) throws InterruptedException, ExecutionException { + final ExecutorService executorService = Executors.newSingleThreadExecutor(); + final List> futures = executorService.invokeAll(Arrays.asList(new ChildLockInterruptiblyAcquirer(child, childMode)), LOCK_ACQUISITION_TIMEOUT, TimeUnit.MILLISECONDS); + + for (final Future future : futures) { + assertTrue(future.isDone()); + assertFalse(future.isCancelled()); + + assertTrue(future.get()); + } + } + private static void assertOtherThreadNotLockableChild(final MultiLock child, final LockMode childMode) throws InterruptedException, ExecutionException { final ExecutorService executorService = Executors.newSingleThreadExecutor(); final List> futures = executorService.invokeAll(Arrays.asList(new ChildLockAcquirer(child, childMode)), LOCK_ACQUISITION_TIMEOUT, TimeUnit.MILLISECONDS); @@ -417,6 +774,17 @@ private static void assertOtherThreadNotLockableChild(final MultiLock child, fin } } + private static void assertOtherThreadNotLockableChildInterruptibly(final MultiLock child, final LockMode childMode) throws InterruptedException, ExecutionException { + final ExecutorService executorService = Executors.newSingleThreadExecutor(); + final List> futures = executorService.invokeAll(Arrays.asList(new ChildLockInterruptiblyAcquirer(child, childMode)), LOCK_ACQUISITION_TIMEOUT, TimeUnit.MILLISECONDS); + + for (final Future future : futures) { + assertTrue(future.isDone()); + + assertTrue(future.isCancelled()); + } + } + private static class RootAndChildLockAcquirer implements Callable { private final MultiLock root; private final LockMode rootLockMode; @@ -440,6 +808,27 @@ public Boolean call() { } } + private static class RootAndChildLockInterruptiblyAcquirer implements Callable { + private final MultiLock root; + private final LockMode rootLockMode; + private final MultiLock child; + private final LockMode childLockMode; + + public RootAndChildLockInterruptiblyAcquirer(final MultiLock root, final LockMode rootLockMode, final MultiLock child, final LockMode childLockMode) { + this.root = root; + this.rootLockMode = rootLockMode; + this.child = child; + this.childLockMode = childLockMode; + } + + @Override + public Boolean call() throws InterruptedException { + rootLockMode.lockInterruptibly(root); + childLockMode.lockInterruptibly(child); + return true; + } + } + private static class ChildLockAcquirer implements Callable { private final MultiLock child; private final LockMode lockMode; @@ -454,4 +843,20 @@ public Boolean call() { return lockMode.lock(child); } } + + private static class ChildLockInterruptiblyAcquirer implements Callable { + private final MultiLock child; + private final LockMode lockMode; + + public ChildLockInterruptiblyAcquirer(final MultiLock child, final LockMode lockMode) { + this.child = child; + this.lockMode = lockMode; + } + + @Override + public Boolean call() throws InterruptedException { + lockMode.lockInterruptibly(child); + return true; + } + } } diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/ReentrancyTest.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/ReentrancyTest.java index d60125b..3d3850c 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/ReentrancyTest.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/ReentrancyTest.java @@ -30,26 +30,51 @@ public void reentrant_IS() throws InterruptedException, ExecutionException { assertReentrant(IS); } + @Test + public void reentrant_IS_interruptibly() throws InterruptedException, ExecutionException { + assertReentrantInterruptibly(IS); + } + @Test public void reentrant_IX() throws InterruptedException, ExecutionException { assertReentrant(IX); } + @Test + public void reentrant_IX_interruptibly() throws InterruptedException, ExecutionException { + assertReentrantInterruptibly(IX); + } + @Test public void reentrant_S() throws InterruptedException, ExecutionException { assertReentrant(S); } + @Test + public void reentrant_S_interruptibly() throws InterruptedException, ExecutionException { + assertReentrantInterruptibly(S); + } + @Test public void reentrant_SIX() throws InterruptedException, ExecutionException { assertReentrant(SIX); } + @Test + public void reentrant_SIX_interruptibly() throws InterruptedException, ExecutionException { + assertReentrantInterruptibly(SIX); + } + @Test public void reentrant_X() throws InterruptedException, ExecutionException { assertReentrant(X); } + @Test + public void reentrant_X_interruptibly() throws InterruptedException, ExecutionException { + assertReentrantInterruptibly(X); + } + private static void assertReentrant(final LockMode lockMode) throws InterruptedException, ExecutionException { final MultiLock multiLock = new MultiLock(); @@ -64,6 +89,20 @@ private static void assertReentrant(final LockMode lockMode) throws InterruptedE } } + private static void assertReentrantInterruptibly(final LockMode lockMode) throws InterruptedException, ExecutionException { + final MultiLock multiLock = new MultiLock(); + + final ExecutorService executorService = Executors.newSingleThreadExecutor(); + final List> futures = executorService.invokeAll(Arrays.asList(new ReenterInterruptibly(multiLock, lockMode, REENTER_COUNT)), LOCK_ACQUISITIONS_TIMEOUT, TimeUnit.MILLISECONDS); + + for (final Future future : futures) { + assertTrue(future.isDone()); + assertFalse(future.isCancelled()); + + assertEquals(REENTER_COUNT, (int)future.get()); + } + } + private static class Reenter implements Callable { private final MultiLock multiLock; private final LockMode lockMode; @@ -86,4 +125,26 @@ public Integer call() { return success; } } + + private static class ReenterInterruptibly implements Callable { + private final MultiLock multiLock; + private final LockMode lockMode; + private final int count; + + private ReenterInterruptibly(final MultiLock multiLock, final LockMode lockMode, final int count) { + this.multiLock = multiLock; + this.lockMode = lockMode; + this.count = count; + } + + @Override + public Integer call() throws InterruptedException { + int success = 0; + for (int i = 0; i < count; i++) { + lockMode.lockInterruptibly(multiLock); + success++; + } + return success; + } + } } diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/UpgradeTest.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/UpgradeTest.java index ef666cc..549b24e 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/UpgradeTest.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/UpgradeTest.java @@ -37,46 +37,91 @@ public void upgrade_IS_IX() throws InterruptedException, ExecutionException { assertUpgradeable(IS, IX); } + @Test + public void upgrade_IS_IX_interruptibly() throws InterruptedException, ExecutionException { + assertUpgradeableInterruptibly(IS, IX); + } + @Test public void upgrade_IS_S() throws InterruptedException, ExecutionException { assertUpgradeable(IS, S); } + @Test + public void upgrade_IS_S_interruptibly() throws InterruptedException, ExecutionException { + assertUpgradeableInterruptibly(IS, S); + } + @Test public void upgrade_IS_SIX() throws InterruptedException, ExecutionException { assertUpgradeable(IS, SIX); } + @Test + public void upgrade_IS_SIX_interruptibly() throws InterruptedException, ExecutionException { + assertUpgradeableInterruptibly(IS, SIX); + } + @Test public void upgrade_IS_X() throws InterruptedException, ExecutionException { assertUpgradeable(IS, X); } + @Test + public void upgrade_IS_X_interruptibly() throws InterruptedException, ExecutionException { + assertUpgradeableInterruptibly(IS, X); + } + @Test public void upgrade_IX_SIX() throws InterruptedException, ExecutionException { assertUpgradeable(IX, SIX); } + @Test + public void upgrade_IX_SIX_interruptibly() throws InterruptedException, ExecutionException { + assertUpgradeableInterruptibly(IX, SIX); + } + @Test public void upgrade_IX_X() throws InterruptedException, ExecutionException { assertUpgradeable(IX, X); } + @Test + public void upgrade_IX_X_interruptibly() throws InterruptedException, ExecutionException { + assertUpgradeableInterruptibly(IX, X); + } + @Test public void upgrade_S_SIX() throws InterruptedException, ExecutionException { assertUpgradeable(S, SIX); } + @Test + public void upgrade_S_SIX_interruptibly() throws InterruptedException, ExecutionException { + assertUpgradeableInterruptibly(S, SIX); + } + @Test public void upgrade_S_X() throws InterruptedException, ExecutionException { assertUpgradeable(S, X); } + @Test + public void upgrade_S_X_interruptibly() throws InterruptedException, ExecutionException { + assertUpgradeableInterruptibly(S, X); + } + @Test public void upgrade_SIX_X() throws InterruptedException, ExecutionException { assertUpgradeable(SIX, X); } + @Test + public void upgrade_SIX_X_interruptibly() throws InterruptedException, ExecutionException { + assertUpgradeableInterruptibly(SIX, X); + } + private static void assertUpgradeable(final LockMode from, final LockMode to) throws InterruptedException, ExecutionException { final MultiLock multiLock = new MultiLock(); @@ -91,6 +136,20 @@ private static void assertUpgradeable(final LockMode from, final LockMode to) th } } + private static void assertUpgradeableInterruptibly(final LockMode from, final LockMode to) throws InterruptedException, ExecutionException { + final MultiLock multiLock = new MultiLock(); + + final ExecutorService executorService = Executors.newSingleThreadExecutor(); + final List> futures = executorService.invokeAll(Arrays.asList(new UpgradeInterruptibly(multiLock, from, to)), LOCK_ACQUISITION_TIMEOUT, TimeUnit.MILLISECONDS); + + for (final Future future : futures) { + assertTrue(future.isDone()); + assertFalse(future.isCancelled()); + + assertTrue(future.get()); + } + } + private static class Upgrade implements Callable { private final MultiLock multiLock; private final LockMode from; @@ -114,4 +173,24 @@ public Boolean call() { return false; } } + + private static class UpgradeInterruptibly implements Callable { + private final MultiLock multiLock; + private final LockMode from; + private final LockMode to; + + private UpgradeInterruptibly(final MultiLock multiLock, final LockMode from, final LockMode to) { + this.multiLock = multiLock; + this.from = from; + this.to = to; + } + + @Override + public Boolean call() throws InterruptedException { + from.lockInterruptibly(multiLock); + to.lockInterruptibly(multiLock); + from.unlock(multiLock); + return true; + } + } } From 7dc4e543e0962c2da0cb6ba54faa36bb765fe156 Mon Sep 17 00:00:00 2001 From: Adam Retter Date: Wed, 29 Aug 2018 22:48:30 +0530 Subject: [PATCH 10/21] [refactor] Don't return boolean unneccesarily --- .../multilock/HierarchicalMultiLock.java | 16 ++--- .../ac/ic/doc/slurp/multilock/LockMode.java | 22 +++---- .../ac/ic/doc/slurp/multilock/MultiLock.java | 12 ++-- .../slurp/multilock/CompatibilityTest.java | 4 +- .../ic/doc/slurp/multilock/DowngradeTest.java | 13 ++-- .../ic/doc/slurp/multilock/HierarchyTest.java | 61 +++++++++---------- .../doc/slurp/multilock/ReentrancyTest.java | 5 +- .../ic/doc/slurp/multilock/UpgradeTest.java | 12 ++-- 8 files changed, 64 insertions(+), 81 deletions(-) diff --git a/src/main/java/uk/ac/ic/doc/slurp/multilock/HierarchicalMultiLock.java b/src/main/java/uk/ac/ic/doc/slurp/multilock/HierarchicalMultiLock.java index 1643362..055869a 100644 --- a/src/main/java/uk/ac/ic/doc/slurp/multilock/HierarchicalMultiLock.java +++ b/src/main/java/uk/ac/ic/doc/slurp/multilock/HierarchicalMultiLock.java @@ -19,11 +19,11 @@ public HierarchicalMultiLock(@Nullable final MultiLock parent) { } @Override - public boolean readLock() { + public void readLock() { if (parent != null) { parent.intentionReadLock(); } - return super.readLock(); + super.readLock(); } @Override @@ -43,11 +43,11 @@ public void readLockInterruptibly() throws InterruptedException { } @Override - public boolean writeLock() { + public void writeLock() { if (parent != null) { parent.intentionWriteLock(); } - return super.writeLock(); + super.writeLock(); } @Override @@ -67,11 +67,11 @@ public void writeLockInterruptibly() throws InterruptedException { } @Override - public boolean intentionReadLock() { + public void intentionReadLock() { if (parent != null) { parent.intentionReadLock(); } - return super.intentionReadLock(); + super.intentionReadLock(); } @Override @@ -91,11 +91,11 @@ public void intentionReadLockInterruptibly() throws InterruptedException { } @Override - public boolean intentionWriteLock() { + public void intentionWriteLock() { if (parent != null) { parent.intentionWriteLock(); } - return super.intentionWriteLock(); + super.intentionWriteLock(); } @Override diff --git a/src/main/java/uk/ac/ic/doc/slurp/multilock/LockMode.java b/src/main/java/uk/ac/ic/doc/slurp/multilock/LockMode.java index 71783f7..14c328d 100644 --- a/src/main/java/uk/ac/ic/doc/slurp/multilock/LockMode.java +++ b/src/main/java/uk/ac/ic/doc/slurp/multilock/LockMode.java @@ -33,11 +33,9 @@ public enum LockMode { * Locks the MultiLock with this mode. * * @param multiLock the MultiLock object. - * - * @return true if the lock succeeded, false otherwise. */ - public boolean lock(final MultiLock multiLock) { - return lock(multiLock, this); + public void lock(final MultiLock multiLock) { + lock(multiLock, this); } /** @@ -70,34 +68,32 @@ public void unlock(final MultiLock multiLock) { * * @throws IllegalArgumentException if an unknown mode is provided. */ - public static boolean lock(final MultiLock multiLock, final LockMode lockMode) { - final boolean lockResult; + public static void lock(final MultiLock multiLock, final LockMode lockMode) { switch (lockMode) { case IS: - lockResult = multiLock.intentionReadLock(); + multiLock.intentionReadLock(); break; case IX: - lockResult = multiLock.intentionWriteLock(); + multiLock.intentionWriteLock(); break; case S: - lockResult = multiLock.readLock(); + multiLock.readLock(); break; case SIX: - lockResult = multiLock.readLock() && multiLock.intentionWriteLock(); + multiLock.readLock(); + multiLock.intentionWriteLock(); break; case X: - lockResult = multiLock.writeLock(); + multiLock.writeLock(); break; default: throw new IllegalArgumentException("Unknown lock mode: " + lockMode); } - - return lockResult; } /** diff --git a/src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java b/src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java index a17e500..2b712fe 100644 --- a/src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java +++ b/src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java @@ -452,36 +452,32 @@ final int getWriteHoldCount() { } } - public boolean readLock() { + public void readLock() { sync.acquireShared(S_UNIT); - return true; } public void readLockInterruptibly() throws InterruptedException { sync.acquireSharedInterruptibly(S_UNIT); } - public boolean writeLock() { + public void writeLock() { sync.acquire(X_UNIT); - return true; } public void writeLockInterruptibly() throws InterruptedException { sync.acquireInterruptibly(X_UNIT); } - public boolean intentionReadLock() { + public void intentionReadLock() { sync.acquireShared(IS_UNIT); - return true; } public void intentionReadLockInterruptibly() throws InterruptedException { sync.acquireSharedInterruptibly(IS_UNIT); } - public boolean intentionWriteLock() { + public void intentionWriteLock() { sync.acquireShared(IX_UNIT); - return true; } public void intentionWriteLockInterruptibly() throws InterruptedException { diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/CompatibilityTest.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/CompatibilityTest.java index 7533529..502d6d5 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/CompatibilityTest.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/CompatibilityTest.java @@ -364,12 +364,12 @@ public LockAcquirer(final MultiLock multiLock, final LockMode lockMode, final Co @Override public Boolean call() throws Exception { - final boolean lockResult = lockMode.lock(multiLock); + lockMode.lock(multiLock); latch.countDown(); latch.await(); - return lockResult; + return true; } } diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/DowngradeTest.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/DowngradeTest.java index 696aeb7..b5989fc 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/DowngradeTest.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/DowngradeTest.java @@ -154,6 +154,7 @@ private static class Downgrade implements Callable { private final MultiLock multiLock; private final LockMode from; private final LockMode to; + //private final Function private Downgrade(final MultiLock multiLock, final LockMode from, final LockMode to) { this.multiLock = multiLock; @@ -163,14 +164,10 @@ private Downgrade(final MultiLock multiLock, final LockMode from, final LockMode @Override public Boolean call() { - if (from.lock(multiLock)) { - if (to.lock(multiLock)) { - from.unlock(multiLock); - - return true; - } - } - return false; + from.lock(multiLock); + to.lock(multiLock); + from.unlock(multiLock); + return true; } } diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/HierarchyTest.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/HierarchyTest.java index 91a2c50..a1550a1 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/HierarchyTest.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/HierarchyTest.java @@ -206,7 +206,7 @@ public void t1_root_SIX_t1_child_X_interruptibly() throws ExecutionException, In @Test public void t1_root_IS_t2_child_IS() throws InterruptedException, ExecutionException { final MultiLock root = new MultiLock(); - assertTrue(IS.lock(root)); + IS.lock(root); final MultiLock child = new HierarchicalMultiLock(root); assertOtherThreadLockableChild(child, IS); @@ -226,7 +226,7 @@ public void t1_root_IS_t2_child_IS_interruptibly() throws InterruptedException, @Test public void t1_root_IS_t2_child_IX() throws InterruptedException, ExecutionException { final MultiLock root = new MultiLock(); - assertTrue(IS.lock(root)); + IS.lock(root); final MultiLock child = new HierarchicalMultiLock(root); assertOtherThreadLockableChild(child, IX); @@ -244,7 +244,7 @@ public void t1_root_IS_t2_child_IX_interruptibly() throws InterruptedException, @Test public void t1_root_IS_t2_child_S() throws InterruptedException, ExecutionException { final MultiLock root = new MultiLock(); - assertTrue(IS.lock(root)); + IS.lock(root); final MultiLock child = new HierarchicalMultiLock(root); assertOtherThreadLockableChild(child, S); @@ -262,7 +262,7 @@ public void t1_root_IS_t2_child_S_interruptibly() throws InterruptedException, E @Test public void t1_root_IS_t2_child_SIX() throws InterruptedException, ExecutionException { final MultiLock root = new MultiLock(); - assertTrue(IS.lock(root)); + IS.lock(root); final MultiLock child = new HierarchicalMultiLock(root); assertOtherThreadLockableChild(child, SIX); @@ -280,7 +280,7 @@ public void t1_root_IS_t2_child_SIX_interruptibly() throws InterruptedException, @Test public void t1_root_IS_t2_child_X() throws InterruptedException, ExecutionException { final MultiLock root = new MultiLock(); - assertTrue(IS.lock(root)); + IS.lock(root); final MultiLock child = new HierarchicalMultiLock(root); assertOtherThreadLockableChild(child, X); @@ -298,7 +298,7 @@ public void t1_root_IS_t2_child_X_interruptibly() throws InterruptedException, E @Test public void t1_root_IX_t2_child_IS() throws InterruptedException, ExecutionException { final MultiLock root = new MultiLock(); - assertTrue(IX.lock(root)); + IX.lock(root); final MultiLock child = new HierarchicalMultiLock(root); assertOtherThreadLockableChild(child, IS); @@ -318,7 +318,7 @@ public void t1_root_IX_t2_child_IS_interruptibly() throws InterruptedException, @Test public void t1_root_IX_t2_child_IX() throws InterruptedException, ExecutionException { final MultiLock root = new MultiLock(); - assertTrue(IX.lock(root)); + IX.lock(root); final MultiLock child = new HierarchicalMultiLock(root); assertOtherThreadLockableChild(child, IX); @@ -336,7 +336,7 @@ public void t1_root_IX_t2_child_IX_interruptibly() throws InterruptedException, @Test public void t1_root_IX_t2_child_S() throws InterruptedException, ExecutionException { final MultiLock root = new MultiLock(); - assertTrue(IX.lock(root)); + IX.lock(root); final MultiLock child = new HierarchicalMultiLock(root); assertOtherThreadLockableChild(child, S); @@ -354,7 +354,7 @@ public void t1_root_IX_t2_child_S_interruptibly() throws InterruptedException, E @Test public void t1_root_IX_t2_child_SIX() throws InterruptedException, ExecutionException { final MultiLock root = new MultiLock(); - assertTrue(IX.lock(root)); + IX.lock(root); final MultiLock child = new HierarchicalMultiLock(root); assertOtherThreadLockableChild(child, SIX); @@ -372,7 +372,7 @@ public void t1_root_IX_t2_child_SIX_interruptibly() throws InterruptedException, @Test public void t1_root_IX_t2_child_X() throws InterruptedException, ExecutionException { final MultiLock root = new MultiLock(); - assertTrue(IX.lock(root)); + IX.lock(root); final MultiLock child = new HierarchicalMultiLock(root); assertOtherThreadLockableChild(child, SIX); @@ -390,7 +390,7 @@ public void t1_root_IX_t2_child_X_interruptibly() throws InterruptedException, E @Test public void t1_root_S_t2_child_IS() throws InterruptedException, ExecutionException { final MultiLock root = new MultiLock(); - assertTrue(S.lock(root)); + S.lock(root); final MultiLock child = new HierarchicalMultiLock(root); assertOtherThreadLockableChild(child, IS); @@ -408,7 +408,7 @@ public void t1_root_S_t2_child_IS_interruptibly() throws InterruptedException, E @Test public void t1_root_S_t2_child_IX() throws InterruptedException, ExecutionException { final MultiLock root = new MultiLock(); - assertTrue(S.lock(root)); + S.lock(root); final MultiLock child = new HierarchicalMultiLock(root); // notLockable = test thread holds S on root, other thread attempts IX on child (which attempts IX on root). S is not compatible with IX @@ -428,7 +428,7 @@ public void t1_root_S_t2_child_IX_interruptibly() throws InterruptedException, E @Test public void t1_root_S_t2_child_S() throws InterruptedException, ExecutionException { final MultiLock root = new MultiLock(); - assertTrue(S.lock(root)); + S.lock(root); final MultiLock child = new HierarchicalMultiLock(root); assertOtherThreadLockableChild(child, S); @@ -446,7 +446,7 @@ public void t1_root_S_t2_child_S_interruptibly() throws InterruptedException, Ex @Test public void t1_root_S_t2_child_SIX() throws InterruptedException, ExecutionException { final MultiLock root = new MultiLock(); - assertTrue(S.lock(root)); + S.lock(root); final MultiLock child = new HierarchicalMultiLock(root); // notLockable = test thread holds S on root, other thread attempts SIX on child (which attempts S+IX on root). S is not compatible with IX @@ -466,7 +466,7 @@ public void t1_root_S_t2_child_SIX_interruptibly() throws InterruptedException, @Test public void t1_root_S_t2_child_X() throws InterruptedException, ExecutionException { final MultiLock root = new MultiLock(); - assertTrue(S.lock(root)); + S.lock(root); final MultiLock child = new HierarchicalMultiLock(root); // notLockable = test thread holds S on root, other thread attempts X on child (which attempts IX on root). S is not compatible with IX @@ -486,7 +486,7 @@ public void t1_root_S_t2_child_X_interruptibly() throws InterruptedException, Ex @Test public void t1_root_SIX_t2_child_IS() throws InterruptedException, ExecutionException { final MultiLock root = new MultiLock(); - assertTrue(SIX.lock(root)); + SIX.lock(root); final MultiLock child = new HierarchicalMultiLock(root); assertOtherThreadLockableChild(child, IS); @@ -504,7 +504,7 @@ public void t1_root_SIX_t2_child_IS_interruptibly() throws InterruptedException, @Test public void t1_root_SIX_t2_child_IX() throws InterruptedException, ExecutionException { final MultiLock root = new MultiLock(); - assertTrue(SIX.lock(root)); + SIX.lock(root); final MultiLock child = new HierarchicalMultiLock(root); // notLockable = test thread holds S+IX on root, other thread attempts IX on child (which attempts IX on root). S is not compatible with IX @@ -524,7 +524,7 @@ public void t1_root_SIX_t2_child_IX_interruptibly() throws InterruptedException, @Test public void t1_root_SIX_t2_child_S() throws InterruptedException, ExecutionException { final MultiLock root = new MultiLock(); - assertTrue(SIX.lock(root)); + SIX.lock(root); final MultiLock child = new HierarchicalMultiLock(root); assertOtherThreadLockableChild(child, S); @@ -542,7 +542,7 @@ public void t1_root_SIX_t2_child_S_interruptibly() throws InterruptedException, @Test public void t1_root_SIX_t2_child_SIX() throws InterruptedException, ExecutionException { final MultiLock root = new MultiLock(); - assertTrue(SIX.lock(root)); + SIX.lock(root); final MultiLock child = new HierarchicalMultiLock(root); // notLockable = test thread holds S+IX on root, other thread attempts SIX on child (which attempts IS+IX on root). S is not compatible with IX @@ -562,7 +562,7 @@ public void t1_root_SIX_t2_child_SIX_interruptibly() throws InterruptedException @Test public void t1_root_SIX_t2_child_X() throws InterruptedException, ExecutionException { final MultiLock root = new MultiLock(); - assertTrue(SIX.lock(root)); + SIX.lock(root); final MultiLock child = new HierarchicalMultiLock(root); // notLockable = test thread holds S+IX on root, other thread attempts X on child (which attempts IX on root). S is not compatible with IX @@ -582,7 +582,7 @@ public void t1_root_SIX_t2_child_X_interruptibly() throws InterruptedException, @Test public void t1_root_X_t2_child_IS() throws InterruptedException, ExecutionException { final MultiLock root = new MultiLock(); - assertTrue(X.lock(root)); + X.lock(root); final MultiLock child = new HierarchicalMultiLock(root); // notLockable = test thread holds X on root, other thread attempts IS on child (which attempts IS on root). X is not compatible with IS @@ -602,7 +602,7 @@ public void t1_root_X_t2_child_IS_interruptibly() throws InterruptedException, E @Test public void t1_root_X_t2_child_IX() throws InterruptedException, ExecutionException { final MultiLock root = new MultiLock(); - assertTrue(X.lock(root)); + X.lock(root); final MultiLock child = new HierarchicalMultiLock(root); // notLockable = test thread holds X on root, other thread attempts IX on child (which attempts IX on root). X is not compatible with IX @@ -622,7 +622,7 @@ public void t1_root_X_t2_child_IX_interruptibly() throws InterruptedException, E @Test public void t1_root_X_t2_child_S() throws InterruptedException, ExecutionException { final MultiLock root = new MultiLock(); - assertTrue(X.lock(root)); + X.lock(root); final MultiLock child = new HierarchicalMultiLock(root); // notLockable = test thread holds X on root, other thread attempts S on child (which attempts IS on root). X is not compatible with IS @@ -642,7 +642,7 @@ public void t1_root_X_t2_child_S_interruptibly() throws InterruptedException, Ex @Test public void t1_root_X_t2_child_SIX() throws InterruptedException, ExecutionException { final MultiLock root = new MultiLock(); - assertTrue(X.lock(root)); + X.lock(root); final MultiLock child = new HierarchicalMultiLock(root); // notLockable = test thread holds X on root, other thread attempts SIX on child (which attempts IS+IX on root). X is not compatible with IS or IX @@ -662,7 +662,7 @@ public void t1_root_X_t2_child_SIX_interruptibly() throws InterruptedException, @Test public void t1_root_X_t2_child_X() throws InterruptedException, ExecutionException { final MultiLock root = new MultiLock(); - assertTrue(X.lock(root)); + X.lock(root); final MultiLock child = new HierarchicalMultiLock(root); // notLockable = test thread holds X on root, other thread attempts X on child (which attempts IX on root). X is not compatible with IX @@ -800,11 +800,9 @@ public RootAndChildLockAcquirer(final MultiLock root, final LockMode rootLockMod @Override public Boolean call() { - if (rootLockMode.lock(root)) { - // NOTE: we intentionally don't combine the two boolean expressions to make debugging the cause of deadlocks simpler - return childLockMode.lock(child); - } - return false; + rootLockMode.lock(root); + childLockMode.lock(child); + return true; } } @@ -840,7 +838,8 @@ public ChildLockAcquirer(final MultiLock child, final LockMode lockMode) { @Override public Boolean call() { - return lockMode.lock(child); + lockMode.lock(child); + return true; } } diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/ReentrancyTest.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/ReentrancyTest.java index 3d3850c..92de145 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/ReentrancyTest.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/ReentrancyTest.java @@ -118,9 +118,8 @@ private Reenter(final MultiLock multiLock, final LockMode lockMode, final int co public Integer call() { int success = 0; for (int i = 0; i < count; i++) { - if (lockMode.lock(multiLock)) { - success++; - } + lockMode.lock(multiLock); + success++; } return success; } diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/UpgradeTest.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/UpgradeTest.java index 549b24e..c93c61f 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/UpgradeTest.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/UpgradeTest.java @@ -163,14 +163,10 @@ private Upgrade(final MultiLock multiLock, final LockMode from, final LockMode t @Override public Boolean call() { - if (from.lock(multiLock)) { - if (to.lock(multiLock)) { - from.unlock(multiLock); - - return true; - } - } - return false; + from.lock(multiLock); + to.lock(multiLock); + from.unlock(multiLock); + return true; } } From 058002b27afb32a57968742f4f2a06e03a7b1ccb Mon Sep 17 00:00:00 2001 From: Adam Retter Date: Wed, 29 Aug 2018 22:59:57 +0530 Subject: [PATCH 11/21] [refactor] Switch tests to JUnit 5 --- pom.xml | 14 +++++++++++--- .../ic/doc/slurp/multilock/CompatibilityTest.java | 6 +++--- .../ac/ic/doc/slurp/multilock/DowngradeTest.java | 6 +++--- .../ac/ic/doc/slurp/multilock/HierarchyTest.java | 7 ++++--- .../ac/ic/doc/slurp/multilock/ReentrancyTest.java | 9 +++++---- .../uk/ac/ic/doc/slurp/multilock/UpgradeTest.java | 6 +++--- 6 files changed, 29 insertions(+), 19 deletions(-) diff --git a/pom.xml b/pom.xml index 3c0a807..c032dc6 100644 --- a/pom.xml +++ b/pom.xml @@ -35,6 +35,7 @@ 1.6 + 1.8 UTF-8 @@ -51,9 +52,9 @@ test - junit - junit - 4.12 + org.junit.jupiter + junit-jupiter-engine + 5.2.0 test @@ -97,6 +98,8 @@ ${java.version} ${java.version} + ${tests.java.version} + ${tests.java.version} ${project.build.sourceEncoding} @@ -181,6 +184,11 @@ true + + org.apache.maven.plugins + maven-surefire-plugin + 2.22.0 + org.apache.maven.plugins maven-release-plugin diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/CompatibilityTest.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/CompatibilityTest.java index 502d6d5..7a80420 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/CompatibilityTest.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/CompatibilityTest.java @@ -1,13 +1,13 @@ package uk.ac.ic.doc.slurp.multilock; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.List; import java.util.concurrent.*; -import static junit.framework.TestCase.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import static uk.ac.ic.doc.slurp.multilock.LockMode.*; /** diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/DowngradeTest.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/DowngradeTest.java index b5989fc..681804a 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/DowngradeTest.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/DowngradeTest.java @@ -1,13 +1,13 @@ package uk.ac.ic.doc.slurp.multilock; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.List; import java.util.concurrent.*; -import static junit.framework.TestCase.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import static uk.ac.ic.doc.slurp.multilock.LockMode.*; /** diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/HierarchyTest.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/HierarchyTest.java index a1550a1..42d767e 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/HierarchyTest.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/HierarchyTest.java @@ -1,13 +1,14 @@ package uk.ac.ic.doc.slurp.multilock; -import org.junit.Test; + +import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.List; import java.util.concurrent.*; -import static junit.framework.TestCase.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import static uk.ac.ic.doc.slurp.multilock.LockMode.*; /** diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/ReentrancyTest.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/ReentrancyTest.java index 92de145..f7e4b21 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/ReentrancyTest.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/ReentrancyTest.java @@ -1,14 +1,15 @@ package uk.ac.ic.doc.slurp.multilock; -import org.junit.Test; + +import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.List; import java.util.concurrent.*; -import static junit.framework.TestCase.assertFalse; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import static uk.ac.ic.doc.slurp.multilock.LockMode.*; /** diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/UpgradeTest.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/UpgradeTest.java index c93c61f..b6f9eac 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/UpgradeTest.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/UpgradeTest.java @@ -1,13 +1,13 @@ package uk.ac.ic.doc.slurp.multilock; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.List; import java.util.concurrent.*; -import static junit.framework.TestCase.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import static uk.ac.ic.doc.slurp.multilock.LockMode.*; /** From d9f7315b0f5bc390ad94f6106cafd805a3cb7416 Mon Sep 17 00:00:00 2001 From: Adam Retter Date: Thu, 30 Aug 2018 00:23:54 +0530 Subject: [PATCH 12/21] [refactor] Remove code duplication in tests --- pom.xml | 9 +- .../slurp/multilock/CompatibilityTest.java | 384 ++++-------------- .../ic/doc/slurp/multilock/DowngradeTest.java | 183 +++------ .../uk/ac/ic/doc/slurp/multilock/Locker.java | 6 + .../doc/slurp/multilock/ReentrancyTest.java | 112 ++--- .../ic/doc/slurp/multilock/UpgradeTest.java | 178 +++----- 6 files changed, 211 insertions(+), 661 deletions(-) create mode 100644 src/test/java/uk/ac/ic/doc/slurp/multilock/Locker.java diff --git a/pom.xml b/pom.xml index c032dc6..9f83bfa 100644 --- a/pom.xml +++ b/pom.xml @@ -36,6 +36,7 @@ 1.6 1.8 + 5.3.0-RC1 UTF-8 @@ -54,7 +55,13 @@ org.junit.jupiter junit-jupiter-engine - 5.2.0 + ${junit.version} + test + + + org.junit.jupiter + junit-jupiter-params + ${junit.version} test diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/CompatibilityTest.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/CompatibilityTest.java index 7a80420..b60c7e8 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/CompatibilityTest.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/CompatibilityTest.java @@ -1,6 +1,10 @@ package uk.ac.ic.doc.slurp.multilock; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import java.util.Arrays; import java.util.List; @@ -37,270 +41,70 @@ */ public class CompatibilityTest { - private static final long LOCK_ACQUISITION_TIMEOUT = 20; // TODO(AR) this might need to be longer on slower machines... - - @Test - public void compatible_IS_IS() throws InterruptedException, ExecutionException { - assertCompatible(IS, IS); - } - - @Test - public void compatible_IS_IS_interruptibly() throws InterruptedException, ExecutionException { - assertCompatibleInterruptibly(IS, IS); - } - - @Test - public void compatible_IS_IX() throws InterruptedException, ExecutionException { - assertCompatible(IS, IX); - } - - @Test - public void compatible_IS_IX_interruptibly() throws InterruptedException, ExecutionException { - assertCompatibleInterruptibly(IS, IX); - } - - @Test - public void compatible_IS_S() throws InterruptedException, ExecutionException { - assertCompatible(IS, S); - } - - @Test - public void compatible_IS_S_interruptibly() throws InterruptedException, ExecutionException { - assertCompatibleInterruptibly(IS, S); - } - - @Test - public void compatible_IS_SIX() throws InterruptedException, ExecutionException { - assertCompatible(IS, SIX); - } - - @Test - public void compatible_IS_SIX_interruptibly() throws InterruptedException, ExecutionException { - assertCompatibleInterruptibly(IS, SIX); - } - - @Test - public void compatible_IS_X() throws InterruptedException, ExecutionException { - assertNotCompatible(IS, X); - } - - @Test - public void compatible_IS_X_interruptibly() throws InterruptedException, ExecutionException { - assertNotCompatibleInterruptibly(IS, X); - } - - @Test - public void compatible_IX_IS() throws InterruptedException, ExecutionException { - assertCompatible(IX, IS); - } - - @Test - public void compatible_IX_IS_interruptibly() throws InterruptedException, ExecutionException { - assertCompatibleInterruptibly(IX, IS); - } - - @Test - public void compatible_IX_IX() throws InterruptedException, ExecutionException { - assertCompatible(IX, IX); - } - - @Test - public void compatible_IX_IX_interruptibly() throws InterruptedException, ExecutionException { - assertCompatibleInterruptibly(IX, IX); - } - - @Test - public void compatible_IX_S() throws InterruptedException, ExecutionException { - assertNotCompatible(IX, S); - } - - @Test - public void compatible_IX_S_interruptibly() throws InterruptedException, ExecutionException { - assertNotCompatibleInterruptibly(IX, S); - } - - @Test - public void compatible_IX_SIX() throws InterruptedException, ExecutionException { - assertNotCompatible(IX, SIX); - } - - @Test - public void compatible_IX_SIX_interruptibly() throws InterruptedException, ExecutionException { - assertNotCompatibleInterruptibly(IX, SIX); - } - - @Test - public void compatible_IX_X() throws InterruptedException, ExecutionException { - assertNotCompatible(IX, X); - } - - @Test - public void compatible_IX_X_interruptibly() throws InterruptedException, ExecutionException { - assertNotCompatibleInterruptibly(IX, X); - } - - @Test - public void compatible_S_IS() throws InterruptedException, ExecutionException { - assertCompatible(S, IS); - } - - @Test - public void compatible_S_IS_interruptibly() throws InterruptedException, ExecutionException { - assertCompatibleInterruptibly(S, IS); - } - - @Test - public void compatible_S_IX() throws InterruptedException, ExecutionException { - assertNotCompatible(S, IX); - } - - @Test - public void compatible_S_IX_interruptibly() throws InterruptedException, ExecutionException { - assertNotCompatibleInterruptibly(S, IX); - } - - @Test - public void compatible_S_S() throws InterruptedException, ExecutionException { - assertCompatible(S, S); - } - - @Test - public void compatible_S_S_interruptibly() throws InterruptedException, ExecutionException { - assertCompatibleInterruptibly(S, S); - } - - @Test - public void compatible_S_SIX() throws InterruptedException, ExecutionException { - assertNotCompatible(S, SIX); - } - - @Test - public void compatible_S_SIX_interruptibly() throws InterruptedException, ExecutionException { - assertNotCompatibleInterruptibly(S, SIX); - } - - @Test - public void compatible_S_X() throws InterruptedException, ExecutionException { - assertNotCompatible(S, X); - } - - @Test - public void compatible_S_X_interruptibly() throws InterruptedException, ExecutionException { - assertNotCompatibleInterruptibly(S, X); - } - - @Test - public void compatible_SIX_IS() throws InterruptedException, ExecutionException { - assertCompatible(SIX, IS); - } - - @Test - public void compatible_SIX_IS_interruptibly() throws InterruptedException, ExecutionException { - assertCompatibleInterruptibly(SIX, IS); - } - - @Test - public void compatible_SIX_IX() throws InterruptedException, ExecutionException { - assertNotCompatible(SIX, IX); - } - - @Test - public void compatible_SIX_IX_interruptibly() throws InterruptedException, ExecutionException { - assertNotCompatibleInterruptibly(SIX, IX); - } - - @Test - public void compatible_SIX_S() throws InterruptedException, ExecutionException { - assertNotCompatible(SIX, S); - } - - @Test - public void compatible_SIX_S_interruptibly() throws InterruptedException, ExecutionException { - assertNotCompatibleInterruptibly(SIX, S); - } - - @Test - public void compatible_SIX_SIX() throws InterruptedException, ExecutionException { - assertNotCompatible(SIX, SIX); - } - - @Test - public void compatible_SIX_SIX_interruptibly() throws InterruptedException, ExecutionException { - assertNotCompatibleInterruptibly(SIX, SIX); - } - - @Test - public void compatible_SIX_X() throws InterruptedException, ExecutionException { - assertNotCompatible(SIX, X); - } - - @Test - public void compatible_SIX_X_interruptibly() throws InterruptedException, ExecutionException { - assertNotCompatibleInterruptibly(SIX, X); - } - - @Test - public void compatible_X_IS() throws InterruptedException, ExecutionException { - assertNotCompatible(X, IS); - } - - @Test - public void compatible_X_IS_interruptibly() throws InterruptedException, ExecutionException { - assertNotCompatibleInterruptibly(X, IS); - } - - @Test - public void compatible_X_IX() throws InterruptedException, ExecutionException { - assertNotCompatible(X, IX); - } - - @Test - public void compatible_X_IX_interruptibly() throws InterruptedException, ExecutionException { - assertNotCompatibleInterruptibly(X, IX); - } - - @Test - public void compatible_X_S() throws InterruptedException, ExecutionException { - assertNotCompatible(X, S); - } - - @Test - public void compatible_X_S_interruptibly() throws InterruptedException, ExecutionException { - assertNotCompatibleInterruptibly(X, S); - } - - @Test - public void compatible_X_SIX() throws InterruptedException, ExecutionException { - assertNotCompatible(X, SIX); - } - - @Test - public void compatible_X_SIX_interruptibly() throws InterruptedException, ExecutionException { - assertNotCompatibleInterruptibly(X, SIX); - } - - @Test - public void compatible_X_X() throws InterruptedException, ExecutionException { - assertNotCompatible(X, X); - } - - @Test - public void compatible_X_X_interruptibly() throws InterruptedException, ExecutionException { - assertNotCompatibleInterruptibly(X, X); + // TODO(AR) this might need to be longer on slower machines... + private static final long LOCK_ACQUISITION_TIMEOUT = 20; + + static List compatibleModesProvider() { + return Arrays.asList( + Arguments.of(IS, IS, true), + Arguments.of(IS, IX, true), + Arguments.of(IS, S, true), + Arguments.of(IS, SIX, true), + Arguments.of(IS, X, false), + + Arguments.of(IX, IS, true), + Arguments.of(IX, IX, true), + Arguments.of(IX, S, false), + Arguments.of(IX, SIX, false), + Arguments.of(IX, X, false), + + Arguments.of(S, IS, true), + Arguments.of(S, IX, false), + Arguments.of(S, S, true), + Arguments.of(S, SIX, false), + Arguments.of(S, X, false), + + Arguments.of(SIX, IS, true), + Arguments.of(SIX, IX, false), + Arguments.of(SIX, S, false), + Arguments.of(SIX, SIX, false), + Arguments.of(SIX, X, false), + + Arguments.of(X, IS, false), + Arguments.of(X, IX, false), + Arguments.of(X, S, false), + Arguments.of(X, SIX, false), + Arguments.of(X, X, false) + ); + } + + @ParameterizedTest(name = "from {0} to {1}") + @DisplayName("Compatible Modes") + @MethodSource("compatibleModesProvider") + public void compatible(final LockMode mode1, final LockMode mode2, final boolean compatible) + throws InterruptedException, ExecutionException { + if (compatible) { + assertCompatible(mode1, mode2, (mode, multiLock) -> { mode.lock(multiLock); return true; }); + } else { + assertNotCompatible(mode1, mode2, (mode, multiLock) -> { mode.lock(multiLock); return true; }); + } } - private static void assertCompatible(final LockMode mode1, final LockMode mode2) throws InterruptedException, ExecutionException { - final List> futures = checkCompatibility(mode1, mode2); - for (final Future future : futures) { - assertTrue(future.isDone()); - assertFalse(future.isCancelled()); - - assertTrue(future.get()); + @ParameterizedTest(name = "from {0} to {1}") + @DisplayName("Compatible Modes Interruptibly") + @MethodSource("compatibleModesProvider") + public void compatibleInterruptibly(final LockMode mode1, final LockMode mode2, final boolean compatible) + throws InterruptedException, ExecutionException { + if (compatible) { + assertCompatible(mode1, mode2, (mode, multiLock) -> { mode.lockInterruptibly(multiLock); return true; }); + } else { + assertNotCompatible(mode1, mode2, (mode, multiLock) -> { mode.lockInterruptibly(multiLock); return true; }); } } - private static void assertCompatibleInterruptibly(final LockMode mode1, final LockMode mode2) throws InterruptedException, ExecutionException { - final List> futures = checkCompatibilityInterruptibly(mode1, mode2); + private static void assertCompatible(final LockMode mode1, final LockMode mode2, final Locker lockFn) + throws InterruptedException, ExecutionException { + final List> futures = checkCompatibility(mode1, mode2, lockFn); for (final Future future : futures) { assertTrue(future.isDone()); assertFalse(future.isCancelled()); @@ -309,8 +113,9 @@ private static void assertCompatibleInterruptibly(final LockMode mode1, final Lo } } - private static void assertNotCompatible(final LockMode mode1, final LockMode mode2) throws InterruptedException, ExecutionException { - final List> futures = checkCompatibility(mode1, mode2); + private static void assertNotCompatible(final LockMode mode1, final LockMode mode2, final Locker lockFn) + throws InterruptedException, ExecutionException { + final List> futures = checkCompatibility(mode1, mode2, lockFn); for (final Future future : futures) { assertTrue(future.isDone()); @@ -318,34 +123,14 @@ private static void assertNotCompatible(final LockMode mode1, final LockMode mod } } - private static void assertNotCompatibleInterruptibly(final LockMode mode1, final LockMode mode2) throws InterruptedException, ExecutionException { - final List> futures = checkCompatibilityInterruptibly(mode1, mode2); - for (final Future future : futures) { - assertTrue(future.isDone()); - - assertTrue(future.isCancelled()); - } - } - - private static List> checkCompatibility(final LockMode mode1, final LockMode mode2) throws InterruptedException { + private static List> checkCompatibility(final LockMode mode1, final LockMode mode2, + final Locker lockFn) throws InterruptedException { final MultiLock multiLock = new MultiLock(); final CountDownLatch latch = new CountDownLatch(2); - final Callable thread1 = new LockAcquirer(multiLock, mode1, latch); - final Callable thread2 = new LockAcquirer(multiLock, mode2, latch); - - final ExecutorService executorService = Executors.newFixedThreadPool(2); - return executorService.invokeAll(Arrays.asList(thread1, thread2), LOCK_ACQUISITION_TIMEOUT, TimeUnit.MILLISECONDS); - } - - private static List> checkCompatibilityInterruptibly(final LockMode mode1, final LockMode mode2) throws InterruptedException { - final MultiLock multiLock = new MultiLock(); - - final CountDownLatch latch = new CountDownLatch(2); - - final Callable thread1 = new LockInterruptiblyAcquirer(multiLock, mode1, latch); - final Callable thread2 = new LockInterruptiblyAcquirer(multiLock, mode2, latch); + final Callable thread1 = new LockAcquirer(multiLock, mode1, lockFn, latch); + final Callable thread2 = new LockAcquirer(multiLock, mode2, lockFn, latch); final ExecutorService executorService = Executors.newFixedThreadPool(2); return executorService.invokeAll(Arrays.asList(thread1, thread2), LOCK_ACQUISITION_TIMEOUT, TimeUnit.MILLISECONDS); @@ -354,39 +139,20 @@ private static List> checkCompatibilityInterruptibly(final LockM private static class LockAcquirer implements Callable { private final MultiLock multiLock; private final LockMode lockMode; + private final Locker lockFn; private final CountDownLatch latch; - public LockAcquirer(final MultiLock multiLock, final LockMode lockMode, final CountDownLatch latch) { + public LockAcquirer(final MultiLock multiLock, final LockMode lockMode, final Locker lockFn, + final CountDownLatch latch) { this.multiLock = multiLock; this.lockMode = lockMode; + this.lockFn = lockFn; this.latch = latch; } @Override public Boolean call() throws Exception { - lockMode.lock(multiLock); - - latch.countDown(); - latch.await(); - - return true; - } - } - - private static class LockInterruptiblyAcquirer implements Callable { - private final MultiLock multiLock; - private final LockMode lockMode; - private final CountDownLatch latch; - - public LockInterruptiblyAcquirer(final MultiLock multiLock, final LockMode lockMode, final CountDownLatch latch) { - this.multiLock = multiLock; - this.lockMode = lockMode; - this.latch = latch; - } - - @Override - public Boolean call() throws InterruptedException { - lockMode.lockInterruptibly(multiLock); + lockFn.lock(lockMode, multiLock); latch.countDown(); latch.await(); diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/DowngradeTest.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/DowngradeTest.java index 681804a..6e315c6 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/DowngradeTest.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/DowngradeTest.java @@ -1,10 +1,17 @@ package uk.ac.ic.doc.slurp.multilock; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import java.util.Arrays; import java.util.List; import java.util.concurrent.*; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Function; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -30,117 +37,47 @@ */ public class DowngradeTest { - private static final long LOCK_ACQUISITION_TIMEOUT = 40; // TODO(AR) this might need to be longer on slower machines... - - @Test - public void downgrade_IX_IS() throws InterruptedException, ExecutionException { - assertDowngradable(IX, IS); - } - - @Test - public void downgrade_IX_IS_interruptibly() throws InterruptedException, ExecutionException { - assertDowngradableInterruptibly(IX, IS); - } - - @Test - public void downgrade_S_IS() throws InterruptedException, ExecutionException { - assertDowngradable(S, IS); - } - - @Test - public void downgrade_S_IS_interruptibly() throws InterruptedException, ExecutionException { - assertDowngradableInterruptibly(S, IS); - } - - @Test - public void downgrade_SIX_IX() throws InterruptedException, ExecutionException { - assertDowngradable(SIX, IX); - } - - @Test - public void downgrade_SIX_IX_interruptibly() throws InterruptedException, ExecutionException { - assertDowngradableInterruptibly(SIX, IX); - } - - @Test - public void downgrade_SIX_S() throws InterruptedException, ExecutionException { - assertDowngradable(SIX, S); - } - - @Test - public void downgrade_SIX_S_interruptibly() throws InterruptedException, ExecutionException { - assertDowngradableInterruptibly(SIX, S); - } - - @Test - public void downgrade_SIX_IS() throws InterruptedException, ExecutionException { - assertDowngradable(SIX, IS); - } - - @Test - public void downgrade_SIX_IS_interruptibly() throws InterruptedException, ExecutionException { - assertDowngradableInterruptibly(SIX, IS); - } - - @Test - public void downgrade_X_SIX() throws InterruptedException, ExecutionException { - assertDowngradable(X, SIX); - } - - @Test - public void downgrade_X_SIX_interruptibly() throws InterruptedException, ExecutionException { - assertDowngradableInterruptibly(X, SIX); - } - - @Test - public void downgrade_X_IX() throws InterruptedException, ExecutionException { - assertDowngradable(X, IX); - } - - @Test - public void downgrade_X_IX_interruptibly() throws InterruptedException, ExecutionException { - assertDowngradableInterruptibly(X, IX); - } - - @Test - public void downgrade_X_S() throws InterruptedException, ExecutionException { - assertDowngradable(X, S); - } - - @Test - public void downgrade_X_S_interruptibly() throws InterruptedException, ExecutionException { - assertDowngradableInterruptibly(X, S); - } - - @Test - public void downgrade_X_IS() throws InterruptedException, ExecutionException { - assertDowngradable(X, IS); - } - - @Test - public void downgrade_X_IS_interruptibly() throws InterruptedException, ExecutionException { - assertDowngradableInterruptibly(X, IS); - } - - private static void assertDowngradable(final LockMode from, final LockMode to) throws InterruptedException, ExecutionException { - final MultiLock multiLock = new MultiLock(); - - final ExecutorService executorService = Executors.newSingleThreadExecutor(); - final List> futures = executorService.invokeAll(Arrays.asList(new Downgrade(multiLock, from, to)), LOCK_ACQUISITION_TIMEOUT, TimeUnit.MILLISECONDS); - - for (final Future future : futures) { - assertTrue(future.isDone()); - assertFalse(future.isCancelled()); - - assertTrue(future.get()); - } - } - - private static void assertDowngradableInterruptibly(final LockMode from, final LockMode to) throws InterruptedException, ExecutionException { + // TODO(AR) this might need to be longer on slower machines... + private static final long LOCK_ACQUISITION_TIMEOUT = 40; + + static List downgradeModesProvider() { + return Arrays.asList( + Arguments.of(IX, IS), + Arguments.of(S, IS), + Arguments.of(SIX, IX), + Arguments.of(SIX, S), + Arguments.of(SIX, IS), + Arguments.of(X, SIX), + Arguments.of(X, IX), + Arguments.of(X, S), + Arguments.of(X, IS) + ); + } + + @ParameterizedTest(name = "from {0} to {1}") + @DisplayName("Downgrade Lock") + @MethodSource("downgradeModesProvider") + public void downgrade(final LockMode fromMode, final LockMode toMode) + throws InterruptedException, ExecutionException { + assertDowngradable(fromMode, toMode, (mode, multiLock) -> { mode.lock(multiLock); return true; }); + } + + @ParameterizedTest(name = "from {0} to {1}") + @DisplayName("Downgrade Lock Interruptibly") + @MethodSource("downgradeModesProvider") + public void downgradeInterruptibly(final LockMode fromMode, final LockMode toMode) + throws InterruptedException, ExecutionException { + assertDowngradable(fromMode, toMode, (mode, multiLock) -> { mode.lockInterruptibly(multiLock); return true; }); + } + + private static void assertDowngradable(final LockMode from, final LockMode to, final Locker lockFn) + throws InterruptedException, ExecutionException { final MultiLock multiLock = new MultiLock(); final ExecutorService executorService = Executors.newSingleThreadExecutor(); - final List> futures = executorService.invokeAll(Arrays.asList(new DowngradeInterruptibly(multiLock, from, to)), LOCK_ACQUISITION_TIMEOUT, TimeUnit.MILLISECONDS); + final List> futures = executorService.invokeAll( + Arrays.asList(new Downgrade(multiLock, from, to, lockFn)), + LOCK_ACQUISITION_TIMEOUT, TimeUnit.MILLISECONDS); for (final Future future : futures) { assertTrue(future.isDone()); @@ -154,40 +91,20 @@ private static class Downgrade implements Callable { private final MultiLock multiLock; private final LockMode from; private final LockMode to; - //private final Function + private final Locker lockFn; - private Downgrade(final MultiLock multiLock, final LockMode from, final LockMode to) { - this.multiLock = multiLock; - this.from = from; - this.to = to; - } - - @Override - public Boolean call() { - from.lock(multiLock); - to.lock(multiLock); - from.unlock(multiLock); - return true; - } - } - - private static class DowngradeInterruptibly implements Callable { - private final MultiLock multiLock; - private final LockMode from; - private final LockMode to; - - private DowngradeInterruptibly(final MultiLock multiLock, final LockMode from, final LockMode to) { + private Downgrade(final MultiLock multiLock, final LockMode from, final LockMode to, final Locker lockFn) { this.multiLock = multiLock; this.from = from; this.to = to; + this.lockFn = lockFn; } @Override public Boolean call() throws InterruptedException { - from.lockInterruptibly(multiLock); - to.lockInterruptibly(multiLock); + lockFn.lock(from, multiLock); + lockFn.lock(to, multiLock); from.unlock(multiLock); - return true; } } diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/Locker.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/Locker.java new file mode 100644 index 0000000..dbfb6f9 --- /dev/null +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/Locker.java @@ -0,0 +1,6 @@ +package uk.ac.ic.doc.slurp.multilock; + +@FunctionalInterface +public interface Locker { + boolean lock(final LockMode lockMode, final MultiLock multiLock) throws InterruptedException; +} diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/ReentrancyTest.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/ReentrancyTest.java index f7e4b21..7dc635b 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/ReentrancyTest.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/ReentrancyTest.java @@ -1,7 +1,11 @@ package uk.ac.ic.doc.slurp.multilock; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.junit.jupiter.params.provider.ValueSource; import java.util.Arrays; import java.util.List; @@ -10,7 +14,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static uk.ac.ic.doc.slurp.multilock.LockMode.*; /** * Tests which check that acquiring the lock @@ -24,77 +27,32 @@ public class ReentrancyTest { private static final int REENTER_COUNT = 10; - private static final long LOCK_ACQUISITIONS_TIMEOUT = 20 * REENTER_COUNT; // TODO(AR) this might need to be longer on slower machines... - @Test - public void reentrant_IS() throws InterruptedException, ExecutionException { - assertReentrant(IS); - } - - @Test - public void reentrant_IS_interruptibly() throws InterruptedException, ExecutionException { - assertReentrantInterruptibly(IS); - } - - @Test - public void reentrant_IX() throws InterruptedException, ExecutionException { - assertReentrant(IX); - } - - @Test - public void reentrant_IX_interruptibly() throws InterruptedException, ExecutionException { - assertReentrantInterruptibly(IX); - } - - @Test - public void reentrant_S() throws InterruptedException, ExecutionException { - assertReentrant(S); - } + // TODO(AR) this might need to be longer on slower machines... + private static final long LOCK_ACQUISITIONS_TIMEOUT = 20 * REENTER_COUNT; - @Test - public void reentrant_S_interruptibly() throws InterruptedException, ExecutionException { - assertReentrantInterruptibly(S); + @ParameterizedTest(name = "{0}") + @DisplayName("Reentrant") + @EnumSource(value = LockMode.class) + public void reentrant(final LockMode lockMode) throws InterruptedException, ExecutionException { + assertReentrant(lockMode, (mode, multiLock) -> { mode.lock(multiLock); return true; }); } - @Test - public void reentrant_SIX() throws InterruptedException, ExecutionException { - assertReentrant(SIX); + @ParameterizedTest(name = "{0}") + @DisplayName("Reentrant Interruptibly") + @EnumSource(value = LockMode.class) + public void reentrantInterruptibly(final LockMode lockMode) throws InterruptedException, ExecutionException { + assertReentrant(lockMode, (mode, multiLock) -> { mode.lockInterruptibly(multiLock); return true; }); } - @Test - public void reentrant_SIX_interruptibly() throws InterruptedException, ExecutionException { - assertReentrantInterruptibly(SIX); - } - - @Test - public void reentrant_X() throws InterruptedException, ExecutionException { - assertReentrant(X); - } - - @Test - public void reentrant_X_interruptibly() throws InterruptedException, ExecutionException { - assertReentrantInterruptibly(X); - } - - private static void assertReentrant(final LockMode lockMode) throws InterruptedException, ExecutionException { + private static void assertReentrant(final LockMode lockMode, final Locker lockFn) + throws InterruptedException, ExecutionException { final MultiLock multiLock = new MultiLock(); final ExecutorService executorService = Executors.newSingleThreadExecutor(); - final List> futures = executorService.invokeAll(Arrays.asList(new Reenter(multiLock, lockMode, REENTER_COUNT)), LOCK_ACQUISITIONS_TIMEOUT, TimeUnit.MILLISECONDS); - - for (final Future future : futures) { - assertTrue(future.isDone()); - assertFalse(future.isCancelled()); - - assertEquals(REENTER_COUNT, (int)future.get()); - } - } - - private static void assertReentrantInterruptibly(final LockMode lockMode) throws InterruptedException, ExecutionException { - final MultiLock multiLock = new MultiLock(); - - final ExecutorService executorService = Executors.newSingleThreadExecutor(); - final List> futures = executorService.invokeAll(Arrays.asList(new ReenterInterruptibly(multiLock, lockMode, REENTER_COUNT)), LOCK_ACQUISITIONS_TIMEOUT, TimeUnit.MILLISECONDS); + final List> futures = executorService.invokeAll( + Arrays.asList(new Reenter(multiLock, lockMode, lockFn, REENTER_COUNT)), + LOCK_ACQUISITIONS_TIMEOUT, TimeUnit.MILLISECONDS); for (final Future future : futures) { assertTrue(future.isDone()); @@ -107,33 +65,13 @@ private static void assertReentrantInterruptibly(final LockMode lockMode) throws private static class Reenter implements Callable { private final MultiLock multiLock; private final LockMode lockMode; + private final Locker lockFn; private final int count; - private Reenter(final MultiLock multiLock, final LockMode lockMode, final int count) { - this.multiLock = multiLock; - this.lockMode = lockMode; - this.count = count; - } - - @Override - public Integer call() { - int success = 0; - for (int i = 0; i < count; i++) { - lockMode.lock(multiLock); - success++; - } - return success; - } - } - - private static class ReenterInterruptibly implements Callable { - private final MultiLock multiLock; - private final LockMode lockMode; - private final int count; - - private ReenterInterruptibly(final MultiLock multiLock, final LockMode lockMode, final int count) { + private Reenter(final MultiLock multiLock, final LockMode lockMode, final Locker lockFn, final int count) { this.multiLock = multiLock; this.lockMode = lockMode; + this.lockFn = lockFn; this.count = count; } @@ -141,7 +79,7 @@ private ReenterInterruptibly(final MultiLock multiLock, final LockMode lockMode, public Integer call() throws InterruptedException { int success = 0; for (int i = 0; i < count; i++) { - lockMode.lockInterruptibly(multiLock); + lockFn.lock(lockMode, multiLock); success++; } return success; diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/UpgradeTest.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/UpgradeTest.java index b6f9eac..6dd4109 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/UpgradeTest.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/UpgradeTest.java @@ -1,6 +1,10 @@ package uk.ac.ic.doc.slurp.multilock; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import java.util.Arrays; import java.util.List; @@ -30,117 +34,47 @@ */ public class UpgradeTest { - private static final long LOCK_ACQUISITION_TIMEOUT = 40; // TODO(AR) this might need to be longer on slower machines... - - @Test - public void upgrade_IS_IX() throws InterruptedException, ExecutionException { - assertUpgradeable(IS, IX); - } - - @Test - public void upgrade_IS_IX_interruptibly() throws InterruptedException, ExecutionException { - assertUpgradeableInterruptibly(IS, IX); - } - - @Test - public void upgrade_IS_S() throws InterruptedException, ExecutionException { - assertUpgradeable(IS, S); - } - - @Test - public void upgrade_IS_S_interruptibly() throws InterruptedException, ExecutionException { - assertUpgradeableInterruptibly(IS, S); - } - - @Test - public void upgrade_IS_SIX() throws InterruptedException, ExecutionException { - assertUpgradeable(IS, SIX); - } - - @Test - public void upgrade_IS_SIX_interruptibly() throws InterruptedException, ExecutionException { - assertUpgradeableInterruptibly(IS, SIX); - } - - @Test - public void upgrade_IS_X() throws InterruptedException, ExecutionException { - assertUpgradeable(IS, X); - } - - @Test - public void upgrade_IS_X_interruptibly() throws InterruptedException, ExecutionException { - assertUpgradeableInterruptibly(IS, X); - } - - @Test - public void upgrade_IX_SIX() throws InterruptedException, ExecutionException { - assertUpgradeable(IX, SIX); - } - - @Test - public void upgrade_IX_SIX_interruptibly() throws InterruptedException, ExecutionException { - assertUpgradeableInterruptibly(IX, SIX); - } - - @Test - public void upgrade_IX_X() throws InterruptedException, ExecutionException { - assertUpgradeable(IX, X); - } - - @Test - public void upgrade_IX_X_interruptibly() throws InterruptedException, ExecutionException { - assertUpgradeableInterruptibly(IX, X); - } - - @Test - public void upgrade_S_SIX() throws InterruptedException, ExecutionException { - assertUpgradeable(S, SIX); - } - - @Test - public void upgrade_S_SIX_interruptibly() throws InterruptedException, ExecutionException { - assertUpgradeableInterruptibly(S, SIX); - } - - @Test - public void upgrade_S_X() throws InterruptedException, ExecutionException { - assertUpgradeable(S, X); - } - - @Test - public void upgrade_S_X_interruptibly() throws InterruptedException, ExecutionException { - assertUpgradeableInterruptibly(S, X); - } - - @Test - public void upgrade_SIX_X() throws InterruptedException, ExecutionException { - assertUpgradeable(SIX, X); - } - - @Test - public void upgrade_SIX_X_interruptibly() throws InterruptedException, ExecutionException { - assertUpgradeableInterruptibly(SIX, X); - } - - private static void assertUpgradeable(final LockMode from, final LockMode to) throws InterruptedException, ExecutionException { - final MultiLock multiLock = new MultiLock(); - - final ExecutorService executorService = Executors.newSingleThreadExecutor(); - final List> futures = executorService.invokeAll(Arrays.asList(new Upgrade(multiLock, from, to)), LOCK_ACQUISITION_TIMEOUT, TimeUnit.MILLISECONDS); - - for (final Future future : futures) { - assertTrue(future.isDone()); - assertFalse(future.isCancelled()); - - assertTrue(future.get()); - } - } - - private static void assertUpgradeableInterruptibly(final LockMode from, final LockMode to) throws InterruptedException, ExecutionException { + // TODO(AR) this might need to be longer on slower machines... + private static final long LOCK_ACQUISITION_TIMEOUT = 40; + + static List upgradeModesProvider() { + return Arrays.asList( + Arguments.of(IS, IX), + Arguments.of(IS, S), + Arguments.of(IS, SIX), + Arguments.of(IS, X), + Arguments.of(IX, SIX), + Arguments.of(IX, X), + Arguments.of(S, SIX), + Arguments.of(S, X), + Arguments.of(SIX, X) + ); + } + + @ParameterizedTest(name = "from {0} to {1}") + @DisplayName("Upgrade Lock") + @MethodSource("upgradeModesProvider") + public void downgrade(final LockMode fromMode, final LockMode toMode) + throws InterruptedException, ExecutionException { + assertUpgradeable(fromMode, toMode, (mode, multiLock) -> { mode.lock(multiLock); return true; }); + } + + @ParameterizedTest(name = "from {0} to {1}") + @DisplayName("Upgrade Lock Interruptibly") + @MethodSource("upgradeModesProvider") + public void downgradeInterruptibly(final LockMode fromMode, final LockMode toMode) + throws InterruptedException, ExecutionException { + assertUpgradeable(fromMode, toMode, (mode, multiLock) -> { mode.lockInterruptibly(multiLock); return true; }); + } + + private static void assertUpgradeable(final LockMode from, final LockMode to, final Locker lockFn) + throws InterruptedException, ExecutionException { final MultiLock multiLock = new MultiLock(); final ExecutorService executorService = Executors.newSingleThreadExecutor(); - final List> futures = executorService.invokeAll(Arrays.asList(new UpgradeInterruptibly(multiLock, from, to)), LOCK_ACQUISITION_TIMEOUT, TimeUnit.MILLISECONDS); + final List> futures = executorService.invokeAll( + Arrays.asList(new Upgrade(multiLock, from, to, lockFn)), + LOCK_ACQUISITION_TIMEOUT, TimeUnit.MILLISECONDS); for (final Future future : futures) { assertTrue(future.isDone()); @@ -154,37 +88,19 @@ private static class Upgrade implements Callable { private final MultiLock multiLock; private final LockMode from; private final LockMode to; + private final Locker lockFn; - private Upgrade(final MultiLock multiLock, final LockMode from, final LockMode to) { - this.multiLock = multiLock; - this.from = from; - this.to = to; - } - - @Override - public Boolean call() { - from.lock(multiLock); - to.lock(multiLock); - from.unlock(multiLock); - return true; - } - } - - private static class UpgradeInterruptibly implements Callable { - private final MultiLock multiLock; - private final LockMode from; - private final LockMode to; - - private UpgradeInterruptibly(final MultiLock multiLock, final LockMode from, final LockMode to) { + private Upgrade(final MultiLock multiLock, final LockMode from, final LockMode to, final Locker lockFn) { this.multiLock = multiLock; this.from = from; this.to = to; + this.lockFn = lockFn; } @Override public Boolean call() throws InterruptedException { - from.lockInterruptibly(multiLock); - to.lockInterruptibly(multiLock); + lockFn.lock(from, multiLock); + lockFn.lock(to, multiLock); from.unlock(multiLock); return true; } From 87a4648a73a4e4b07b707892321799206eb34257 Mon Sep 17 00:00:00 2001 From: Adam Retter Date: Thu, 30 Aug 2018 14:50:20 +0530 Subject: [PATCH 13/21] [feature] Implement try lock methods for MultiLock --- .../multilock/HierarchicalMultiLock.java | 76 +++++++++++++++++++ .../ac/ic/doc/slurp/multilock/LockMode.java | 51 +++++++++++++ .../ac/ic/doc/slurp/multilock/MultiLock.java | 20 ++++- .../slurp/multilock/CompatibilityTest.java | 64 +++++++++++++--- .../ic/doc/slurp/multilock/DowngradeTest.java | 8 ++ .../doc/slurp/multilock/ReentrancyTest.java | 7 ++ .../ic/doc/slurp/multilock/UpgradeTest.java | 8 ++ 7 files changed, 223 insertions(+), 11 deletions(-) diff --git a/src/main/java/uk/ac/ic/doc/slurp/multilock/HierarchicalMultiLock.java b/src/main/java/uk/ac/ic/doc/slurp/multilock/HierarchicalMultiLock.java index 055869a..f629e4e 100644 --- a/src/main/java/uk/ac/ic/doc/slurp/multilock/HierarchicalMultiLock.java +++ b/src/main/java/uk/ac/ic/doc/slurp/multilock/HierarchicalMultiLock.java @@ -26,6 +26,25 @@ public void readLock() { super.readLock(); } + @Override + public boolean tryReadLock() { + if (parent != null) { + if(!parent.tryIntentionReadLock()) { + return false; + } + } + + boolean locked = false; + try { + locked = super.tryReadLock(); + } finally { + if (!locked && parent != null) { + parent.unlockIntentionRead(); + } + } + return locked; + } + @Override public void readLockInterruptibly() throws InterruptedException { if (parent != null) { @@ -50,6 +69,25 @@ public void writeLock() { super.writeLock(); } + @Override + public boolean tryWriteLock() { + if (parent != null) { + if(!parent.tryIntentionWriteLock()) { + return false; + } + } + + boolean locked = false; + try { + locked = super.tryWriteLock(); + } finally { + if (!locked && parent != null) { + parent.unlockIntentionWrite(); + } + } + return locked; + } + @Override public void writeLockInterruptibly() throws InterruptedException { if (parent != null) { @@ -74,6 +112,25 @@ public void intentionReadLock() { super.intentionReadLock(); } + @Override + public boolean tryIntentionReadLock() { + if (parent != null) { + if(!parent.tryIntentionReadLock()) { + return false; + } + } + + boolean locked = false; + try { + locked = super.tryIntentionReadLock(); + } finally { + if (!locked && parent != null) { + parent.unlockIntentionRead(); + } + } + return locked; + } + @Override public void intentionReadLockInterruptibly() throws InterruptedException { if (parent != null) { @@ -98,6 +155,25 @@ public void intentionWriteLock() { super.intentionWriteLock(); } + @Override + public boolean tryIntentionWriteLock() { + if (parent != null) { + if(!parent.tryIntentionWriteLock()) { + return false; + } + } + + boolean locked = false; + try { + locked = super.tryIntentionWriteLock(); + } finally { + if (!locked && parent != null) { + parent.unlockIntentionWrite(); + } + } + return locked; + } + @Override public void intentionWriteLockInterruptibly() throws InterruptedException { if (parent != null) { diff --git a/src/main/java/uk/ac/ic/doc/slurp/multilock/LockMode.java b/src/main/java/uk/ac/ic/doc/slurp/multilock/LockMode.java index 14c328d..799e7d8 100644 --- a/src/main/java/uk/ac/ic/doc/slurp/multilock/LockMode.java +++ b/src/main/java/uk/ac/ic/doc/slurp/multilock/LockMode.java @@ -38,6 +38,17 @@ public void lock(final MultiLock multiLock) { lock(multiLock, this); } + /** + * Attempts to lock the MultiLock with this mode. + * + * @param multiLock the MultiLock object. + * + * @return true if the lock was acquired, false otherwise. + */ + public boolean tryLock(final MultiLock multiLock) { + return tryLock(multiLock, this); + } + /** * Locks the MultiLock with this mode, aborting if interrupted. * @@ -96,6 +107,46 @@ public static void lock(final MultiLock multiLock, final LockMode lockMode) { } } + /** + * Attempts to lock the MultiLock with the provided mode. + * + * @param multiLock the MultiLock object. + * @param lockMode the mode to lock the MultiLock. + * + * @return true if the lock was acquired. + * + * @throws IllegalArgumentException if an unknown mode is provided. + * @throws InterruptedException if the thread was interrupted + */ + public static boolean tryLock(final MultiLock multiLock, final LockMode lockMode) { + switch (lockMode) { + case IS: + return multiLock.tryIntentionReadLock(); + + case IX: + return multiLock.tryIntentionWriteLock(); + + case S: + return multiLock.tryReadLock(); + + case SIX: + if (!multiLock.tryReadLock()) { + return false; + } + if (!multiLock.tryIntentionWriteLock()) { + multiLock.unlockRead(); + return false; + } + return true; + + case X: + return multiLock.tryWriteLock(); + + default: + throw new IllegalArgumentException("Unknown lock mode: " + lockMode); + } + } + /** * Locks the MultiLock with the provided mode, aborting if interrupted. * diff --git a/src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java b/src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java index 2b712fe..c7ff448 100644 --- a/src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java +++ b/src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java @@ -86,7 +86,7 @@ public Condition newCondition() { @Override public boolean tryLock() { - throw new UnsupportedOperationException(); + return tryReadLock(); } @Override @@ -120,7 +120,7 @@ public Condition newCondition() { @Override public boolean tryLock() { - throw new UnsupportedOperationException(); + return tryWriteLock(); } @Override @@ -456,6 +456,10 @@ public void readLock() { sync.acquireShared(S_UNIT); } + public boolean tryReadLock() { + return sync.tryAcquireShared(S_UNIT) >= 0; + } + public void readLockInterruptibly() throws InterruptedException { sync.acquireSharedInterruptibly(S_UNIT); } @@ -464,6 +468,10 @@ public void writeLock() { sync.acquire(X_UNIT); } + public boolean tryWriteLock() { + return sync.tryAcquire(X_UNIT); + } + public void writeLockInterruptibly() throws InterruptedException { sync.acquireInterruptibly(X_UNIT); } @@ -472,6 +480,10 @@ public void intentionReadLock() { sync.acquireShared(IS_UNIT); } + public boolean tryIntentionReadLock() { + return sync.tryAcquireShared(IS_UNIT) >= 0; + } + public void intentionReadLockInterruptibly() throws InterruptedException { sync.acquireSharedInterruptibly(IS_UNIT); } @@ -480,6 +492,10 @@ public void intentionWriteLock() { sync.acquireShared(IX_UNIT); } + public boolean tryIntentionWriteLock() { + return sync.tryAcquireShared(IX_UNIT) >= 0; + } + public void intentionWriteLockInterruptibly() throws InterruptedException { sync.acquireSharedInterruptibly(IX_UNIT); } diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/CompatibilityTest.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/CompatibilityTest.java index b60c7e8..b1a470f 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/CompatibilityTest.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/CompatibilityTest.java @@ -1,7 +1,6 @@ package uk.ac.ic.doc.slurp.multilock; import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @@ -78,7 +77,7 @@ static List compatibleModesProvider() { ); } - @ParameterizedTest(name = "from {0} to {1}") + @ParameterizedTest(name = "{0} and {1}") @DisplayName("Compatible Modes") @MethodSource("compatibleModesProvider") public void compatible(final LockMode mode1, final LockMode mode2, final boolean compatible) @@ -86,11 +85,11 @@ public void compatible(final LockMode mode1, final LockMode mode2, final boolean if (compatible) { assertCompatible(mode1, mode2, (mode, multiLock) -> { mode.lock(multiLock); return true; }); } else { - assertNotCompatible(mode1, mode2, (mode, multiLock) -> { mode.lock(multiLock); return true; }); + assertNotCompatible(mode1, mode2, (mode, multiLock) -> { mode.lock(multiLock); return true; }, true); } } - @ParameterizedTest(name = "from {0} to {1}") + @ParameterizedTest(name = "{0} and {1}") @DisplayName("Compatible Modes Interruptibly") @MethodSource("compatibleModesProvider") public void compatibleInterruptibly(final LockMode mode1, final LockMode mode2, final boolean compatible) @@ -98,10 +97,29 @@ public void compatibleInterruptibly(final LockMode mode1, final LockMode mode2, if (compatible) { assertCompatible(mode1, mode2, (mode, multiLock) -> { mode.lockInterruptibly(multiLock); return true; }); } else { - assertNotCompatible(mode1, mode2, (mode, multiLock) -> { mode.lockInterruptibly(multiLock); return true; }); + assertNotCompatible(mode1, mode2, (mode, multiLock) -> { mode.lockInterruptibly(multiLock); return true; }, true); } } + @ParameterizedTest(name = "{0} and {1}") + @DisplayName("Compatible Modes Try") + @MethodSource("compatibleModesProvider") + public void compatibleTry(final LockMode mode1, final LockMode mode2, final boolean compatible) + throws InterruptedException, ExecutionException { + if (compatible) { + assertCompatible(mode1, mode2, (mode, multiLock) -> mode.tryLock(multiLock)); + } else { + assertNotCompatible(mode1, mode2, (mode, multiLock) -> mode.tryLock(multiLock), false); + } + } + + /** + * Assert that two lock modes are compatible. + * + * @param mode1 the first lock mode + * @param mode2 the second lock mode + * @param lockFn the function for acquiring a lock + */ private static void assertCompatible(final LockMode mode1, final LockMode mode2, final Locker lockFn) throws InterruptedException, ExecutionException { final List> futures = checkCompatibility(mode1, mode2, lockFn); @@ -113,14 +131,42 @@ private static void assertCompatible(final LockMode mode1, final LockMode mode2, } } - private static void assertNotCompatible(final LockMode mode1, final LockMode mode2, final Locker lockFn) + /** + * Assert that two lock modes are not compatible. + * + * @param mode1 the first lock mode + * @param mode2 the second lock mode + * @param lockFn the function for acquiring a lock + * @param blockingAcquisition true if the {@code lockFn} makes lock acquisition calls which block + * until the lock is granted, false otherwise. + */ + private static void assertNotCompatible(final LockMode mode1, final LockMode mode2, final Locker lockFn, + final boolean blockingAcquisition) throws InterruptedException, ExecutionException { final List> futures = checkCompatibility(mode1, mode2, lockFn); + + Boolean locked = null; + for (final Future future : futures) { assertTrue(future.isDone()); - assertTrue(future.isCancelled()); + if (!future.isCancelled()) { + if (locked == null) { + locked = future.get(); + } else { + locked &= future.get(); + } + } } + + /* + * `locked` will be null if all futures were cancelled, + * this is because each of our LockAcquirer(s) only complete + * if all acquisitions succeed. When using blocking acquisitions + * if one acquisition fails, no tasks complete, so all futures + * end up marked as cancelled. See the latch in LockAcquirer#call() + */ + assertTrue((blockingAcquisition && locked == null) || (!blockingAcquisition && !locked)); } private static List> checkCompatibility(final LockMode mode1, final LockMode mode2, @@ -152,12 +198,12 @@ public LockAcquirer(final MultiLock multiLock, final LockMode lockMode, final Lo @Override public Boolean call() throws Exception { - lockFn.lock(lockMode, multiLock); + final boolean locked = lockFn.lock(lockMode, multiLock); latch.countDown(); latch.await(); - return true; + return locked; } } } diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/DowngradeTest.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/DowngradeTest.java index 6e315c6..9e40615 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/DowngradeTest.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/DowngradeTest.java @@ -70,6 +70,14 @@ public void downgradeInterruptibly(final LockMode fromMode, final LockMode toMod assertDowngradable(fromMode, toMode, (mode, multiLock) -> { mode.lockInterruptibly(multiLock); return true; }); } + @ParameterizedTest(name = "from {0} to {1}") + @DisplayName("Downgrade Lock Try") + @MethodSource("downgradeModesProvider") + public void downgradeTry(final LockMode fromMode, final LockMode toMode) + throws InterruptedException, ExecutionException { + assertDowngradable(fromMode, toMode, (mode, multiLock) -> mode.tryLock(multiLock)); + } + private static void assertDowngradable(final LockMode from, final LockMode to, final Locker lockFn) throws InterruptedException, ExecutionException { final MultiLock multiLock = new MultiLock(); diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/ReentrancyTest.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/ReentrancyTest.java index 7dc635b..6143f35 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/ReentrancyTest.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/ReentrancyTest.java @@ -45,6 +45,13 @@ public void reentrantInterruptibly(final LockMode lockMode) throws InterruptedEx assertReentrant(lockMode, (mode, multiLock) -> { mode.lockInterruptibly(multiLock); return true; }); } + @ParameterizedTest(name = "{0}") + @DisplayName("Reentrant Try") + @EnumSource(value = LockMode.class) + public void reentrantTry(final LockMode lockMode) throws InterruptedException, ExecutionException { + assertReentrant(lockMode, (mode, multiLock) -> { mode.tryLock(multiLock); return true; }); + } + private static void assertReentrant(final LockMode lockMode, final Locker lockFn) throws InterruptedException, ExecutionException { final MultiLock multiLock = new MultiLock(); diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/UpgradeTest.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/UpgradeTest.java index 6dd4109..b38d207 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/UpgradeTest.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/UpgradeTest.java @@ -67,6 +67,14 @@ public void downgradeInterruptibly(final LockMode fromMode, final LockMode toMod assertUpgradeable(fromMode, toMode, (mode, multiLock) -> { mode.lockInterruptibly(multiLock); return true; }); } + @ParameterizedTest(name = "from {0} to {1}") + @DisplayName("Upgrade Lock Try") + @MethodSource("upgradeModesProvider") + public void downgradeTry(final LockMode fromMode, final LockMode toMode) + throws InterruptedException, ExecutionException { + assertUpgradeable(fromMode, toMode, (mode, multiLock) -> mode.tryLock(multiLock)); + } + private static void assertUpgradeable(final LockMode from, final LockMode to, final Locker lockFn) throws InterruptedException, ExecutionException { final MultiLock multiLock = new MultiLock(); From 135ecba3d31b0dff6a33fc91fee116b56bdfb865 Mon Sep 17 00:00:00 2001 From: Adam Retter Date: Thu, 30 Aug 2018 14:54:30 +0530 Subject: [PATCH 14/21] [bugfix] Fix a livelock issue in MultiLock.Sync.tryAcquireShared(long). The try method could previously block indefinetly when a thread tried to acquire a mode. Such an example is t1 acquires IX, and then t2 acquires S; causing t2 to spin indefinetly. CompatibilityTest surfaced the issue. --- src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java b/src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java index c7ff448..28b98f4 100644 --- a/src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java +++ b/src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java @@ -306,6 +306,8 @@ else if (arg == IX_UNIT) { cachedHoldCounter = rh; return 1; // still return 1 because IS is always compatible } + } else { + return -1; } } } @@ -332,6 +334,8 @@ else if (arg == S_UNIT) { cachedHoldCounter = rh; return 1; // still return 1 because IS is always compatible } + } else { + return -1; } } } From 248ac3b69f60bf17e0b0b4ad3781af64b8e6d80f Mon Sep 17 00:00:00 2001 From: Adam Retter Date: Thu, 30 Aug 2018 15:37:46 +0530 Subject: [PATCH 15/21] [feature] Implement try lock with timeout methods for MultiLock --- .../multilock/HierarchicalMultiLock.java | 117 ++++++++++++++++++ .../ac/ic/doc/slurp/multilock/LockMode.java | 66 ++++++++++ .../ac/ic/doc/slurp/multilock/MultiLock.java | 20 ++- .../slurp/multilock/CompatibilityTest.java | 29 ++++- .../ac/ic/doc/slurp/multilock/Constants.java | 7 ++ .../ic/doc/slurp/multilock/DowngradeTest.java | 24 ++-- .../doc/slurp/multilock/ReentrancyTest.java | 23 +++- .../ic/doc/slurp/multilock/UpgradeTest.java | 21 +++- 8 files changed, 285 insertions(+), 22 deletions(-) create mode 100644 src/test/java/uk/ac/ic/doc/slurp/multilock/Constants.java diff --git a/src/main/java/uk/ac/ic/doc/slurp/multilock/HierarchicalMultiLock.java b/src/main/java/uk/ac/ic/doc/slurp/multilock/HierarchicalMultiLock.java index f629e4e..a616896 100644 --- a/src/main/java/uk/ac/ic/doc/slurp/multilock/HierarchicalMultiLock.java +++ b/src/main/java/uk/ac/ic/doc/slurp/multilock/HierarchicalMultiLock.java @@ -1,6 +1,7 @@ package uk.ac.ic.doc.slurp.multilock; import javax.annotation.Nullable; +import java.util.concurrent.TimeUnit; public class HierarchicalMultiLock extends MultiLock { @@ -45,6 +46,35 @@ public boolean tryReadLock() { return locked; } + /** + * @param timeout the time to wait for acquiring the locks. The actual time can be 2* this, as the + * timeout is used twice, once for the parent and once for the node. + * @param unit the time unit of the timeout argument + */ + @Override + public boolean tryReadLock(final long timeout, final TimeUnit unit) throws InterruptedException { + if (parent != null) { + if(!parent.tryIntentionReadLock(timeout, unit)) { + return false; + } + } + + boolean locked = false; + try { + locked = super.tryReadLock(timeout, unit); + } catch (final InterruptedException e) { + if (parent != null) { + parent.unlockIntentionRead(); + } + throw e; + } finally { + if (!locked && parent != null) { + parent.unlockIntentionRead(); + } + } + return locked; + } + @Override public void readLockInterruptibly() throws InterruptedException { if (parent != null) { @@ -88,6 +118,35 @@ public boolean tryWriteLock() { return locked; } + /** + * @param timeout the time to wait for acquiring the locks. The actual time can be 2* this, as the + * timeout is used twice, once for the parent and once for the node. + * @param unit the time unit of the timeout argument + */ + @Override + public boolean tryWriteLock(final long timeout, final TimeUnit unit) throws InterruptedException { + if (parent != null) { + if(!parent.tryIntentionWriteLock(timeout, unit)) { + return false; + } + } + + boolean locked = false; + try { + locked = super.tryWriteLock(timeout, unit); + } catch (final InterruptedException e) { + if (parent != null) { + parent.unlockIntentionWrite(); + } + throw e; + } finally { + if (!locked && parent != null) { + parent.unlockIntentionWrite(); + } + } + return locked; + } + @Override public void writeLockInterruptibly() throws InterruptedException { if (parent != null) { @@ -131,6 +190,35 @@ public boolean tryIntentionReadLock() { return locked; } + /** + * @param timeout the time to wait for acquiring the locks. The actual time can be 2* this, as the + * timeout is used twice, once for the parent and once for the node. + * @param unit the time unit of the timeout argument + */ + @Override + public boolean tryIntentionReadLock(final long timeout, final TimeUnit unit) throws InterruptedException { + if (parent != null) { + if(!parent.tryIntentionReadLock(timeout, unit)) { + return false; + } + } + + boolean locked = false; + try { + locked = super.tryIntentionReadLock(timeout, unit); + } catch (final InterruptedException e) { + if (parent != null) { + parent.unlockIntentionRead(); + } + throw e; + } finally { + if (!locked && parent != null) { + parent.unlockIntentionRead(); + } + } + return locked; + } + @Override public void intentionReadLockInterruptibly() throws InterruptedException { if (parent != null) { @@ -174,6 +262,35 @@ public boolean tryIntentionWriteLock() { return locked; } + /** + * @param timeout the time to wait for acquiring the locks. The actual time can be 2* this, as the + * timeout is used twice, once for the parent and once for the node. + * @param unit the time unit of the timeout argument + */ + @Override + public boolean tryIntentionWriteLock(final long timeout, final TimeUnit unit) throws InterruptedException { + if (parent != null) { + if(!parent.tryIntentionWriteLock(timeout, unit)) { + return false; + } + } + + boolean locked = false; + try { + locked = super.tryIntentionWriteLock(timeout, unit); + } catch (final InterruptedException e) { + if (parent != null) { + parent.unlockIntentionWrite(); + } + throw e; + } finally { + if (!locked && parent != null) { + parent.unlockIntentionWrite(); + } + } + return locked; + } + @Override public void intentionWriteLockInterruptibly() throws InterruptedException { if (parent != null) { diff --git a/src/main/java/uk/ac/ic/doc/slurp/multilock/LockMode.java b/src/main/java/uk/ac/ic/doc/slurp/multilock/LockMode.java index 799e7d8..4a07718 100644 --- a/src/main/java/uk/ac/ic/doc/slurp/multilock/LockMode.java +++ b/src/main/java/uk/ac/ic/doc/slurp/multilock/LockMode.java @@ -1,5 +1,7 @@ package uk.ac.ic.doc.slurp.multilock; +import java.util.concurrent.TimeUnit; + /** * An Enumeration of the MultiLock modes. */ @@ -49,6 +51,22 @@ public boolean tryLock(final MultiLock multiLock) { return tryLock(multiLock, this); } + /** + * Attempts to lock the MultiLock with this mode. + * + * @param multiLock the MultiLock object. + * @param timeout the time to wait for acquiring the lock. + * @param unit the time unit of the timeout argument. + * + * @return true if the lock was acquired, false otherwise. + * + * @throws InterruptedException if the thread was interrupted + */ + public boolean tryLock(final MultiLock multiLock, final long timeout, final TimeUnit unit) + throws InterruptedException { + return tryLock(multiLock, this, timeout, unit); + } + /** * Locks the MultiLock with this mode, aborting if interrupted. * @@ -147,6 +165,54 @@ public static boolean tryLock(final MultiLock multiLock, final LockMode lockMode } } + /** + * Attempts to lock the MultiLock with the provided mode. + * + * @param multiLock the MultiLock object. + * @param lockMode the mode to lock the MultiLock. + * @param timeout the time to wait for acquiring the lock. + * @param unit the time unit of the timeout argument. + * + * @return true if the lock was acquired. + * + * @throws IllegalArgumentException if an unknown mode is provided. + * @throws InterruptedException if the thread was interrupted + */ + public static boolean tryLock(final MultiLock multiLock, final LockMode lockMode, final long timeout, + final TimeUnit unit) throws InterruptedException { + switch (lockMode) { + case IS: + return multiLock.tryIntentionReadLock(timeout, unit); + + case IX: + return multiLock.tryIntentionWriteLock(timeout, unit); + + case S: + return multiLock.tryReadLock(timeout, unit); + + case SIX: + if (!multiLock.tryReadLock(timeout, unit)) { + return false; + } + try { + if (!multiLock.tryIntentionWriteLock(timeout, unit)) { + multiLock.unlockRead(); + return false; + } + } catch (final InterruptedException e) { + multiLock.unlockRead(); + throw e; + } + return true; + + case X: + return multiLock.tryWriteLock(timeout, unit); + + default: + throw new IllegalArgumentException("Unknown lock mode: " + lockMode); + } + } + /** * Locks the MultiLock with the provided mode, aborting if interrupted. * diff --git a/src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java b/src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java index 28b98f4..cca905d 100644 --- a/src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java +++ b/src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java @@ -92,7 +92,7 @@ public boolean tryLock() { @Override public boolean tryLock(final long time, final TimeUnit unit) throws InterruptedException { - throw new UnsupportedOperationException(); + return tryReadLock(time, unit); } } @@ -126,7 +126,7 @@ public boolean tryLock() { @Override public boolean tryLock(final long time, final TimeUnit unit) throws InterruptedException { - throw new UnsupportedOperationException(); + return tryWriteLock(time, unit); } } @@ -464,6 +464,10 @@ public boolean tryReadLock() { return sync.tryAcquireShared(S_UNIT) >= 0; } + public boolean tryReadLock(final long timeout, final TimeUnit unit) throws InterruptedException { + return sync.tryAcquireSharedNanos(S_UNIT, unit.toNanos(timeout)); + } + public void readLockInterruptibly() throws InterruptedException { sync.acquireSharedInterruptibly(S_UNIT); } @@ -476,6 +480,10 @@ public boolean tryWriteLock() { return sync.tryAcquire(X_UNIT); } + public boolean tryWriteLock(final long timeout, final TimeUnit unit) throws InterruptedException { + return sync.tryAcquireNanos(X_UNIT, unit.toNanos(timeout)); + } + public void writeLockInterruptibly() throws InterruptedException { sync.acquireInterruptibly(X_UNIT); } @@ -488,6 +496,10 @@ public boolean tryIntentionReadLock() { return sync.tryAcquireShared(IS_UNIT) >= 0; } + public boolean tryIntentionReadLock(final long timeout, final TimeUnit unit) throws InterruptedException { + return sync.tryAcquireSharedNanos(IS_UNIT, unit.toNanos(timeout)); + } + public void intentionReadLockInterruptibly() throws InterruptedException { sync.acquireSharedInterruptibly(IS_UNIT); } @@ -500,6 +512,10 @@ public boolean tryIntentionWriteLock() { return sync.tryAcquireShared(IX_UNIT) >= 0; } + public boolean tryIntentionWriteLock(final long timeout, final TimeUnit unit) throws InterruptedException { + return sync.tryAcquireSharedNanos(IX_UNIT, unit.toNanos(timeout)); + } + public void intentionWriteLockInterruptibly() throws InterruptedException { sync.acquireSharedInterruptibly(IX_UNIT); } diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/CompatibilityTest.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/CompatibilityTest.java index b1a470f..d8bfe91 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/CompatibilityTest.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/CompatibilityTest.java @@ -11,6 +11,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +import static uk.ac.ic.doc.slurp.multilock.Constants.LOCK_ACQUISITION_TIMEOUT; import static uk.ac.ic.doc.slurp.multilock.LockMode.*; /** @@ -40,9 +41,6 @@ */ public class CompatibilityTest { - // TODO(AR) this might need to be longer on slower machines... - private static final long LOCK_ACQUISITION_TIMEOUT = 20; - static List compatibleModesProvider() { return Arrays.asList( Arguments.of(IS, IS, true), @@ -113,6 +111,31 @@ public void compatibleTry(final LockMode mode1, final LockMode mode2, final bool } } + @ParameterizedTest(name = "{0} and {1}") + @DisplayName("Compatible Modes Try (short timeout)") + @MethodSource("compatibleModesProvider") + public void compatibleTryShortTimeout(final LockMode mode1, final LockMode mode2, final boolean compatible) + throws InterruptedException, ExecutionException { + if (compatible) { + assertCompatible(mode1, mode2, (mode, multiLock) -> mode.tryLock(multiLock, LOCK_ACQUISITION_TIMEOUT / 2, TimeUnit.MILLISECONDS)); + } else { + assertNotCompatible(mode1, mode2, (mode, multiLock) -> mode.tryLock(multiLock, LOCK_ACQUISITION_TIMEOUT / 2, TimeUnit.MILLISECONDS), false); + } + } + + @ParameterizedTest(name = "{0} and {1}") + @DisplayName("Compatible Modes Try (long timeout)") + @MethodSource("compatibleModesProvider") + public void compatibleTryLongTimeout(final LockMode mode1, final LockMode mode2, final boolean compatible) + throws InterruptedException, ExecutionException { + if (compatible) { + assertCompatible(mode1, mode2, (mode, multiLock) -> mode.tryLock(multiLock, LOCK_ACQUISITION_TIMEOUT * 2, TimeUnit.MILLISECONDS)); + } else { + // NOTE: we set the blockingAcquisition=true parameter, as the timeout is greater than the ExecutorService's LOCK_ACQUISITION_TIMEOUT + assertNotCompatible(mode1, mode2, (mode, multiLock) -> mode.tryLock(multiLock, LOCK_ACQUISITION_TIMEOUT * 2, TimeUnit.MILLISECONDS), true); + } + } + /** * Assert that two lock modes are compatible. * diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/Constants.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/Constants.java new file mode 100644 index 0000000..11b688f --- /dev/null +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/Constants.java @@ -0,0 +1,7 @@ +package uk.ac.ic.doc.slurp.multilock; + +public interface Constants { + + // TODO(AR) this might need to be longer on slower machines... + long LOCK_ACQUISITION_TIMEOUT = 40; +} diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/DowngradeTest.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/DowngradeTest.java index 9e40615..8fad534 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/DowngradeTest.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/DowngradeTest.java @@ -1,7 +1,6 @@ package uk.ac.ic.doc.slurp.multilock; import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @@ -9,12 +8,10 @@ import java.util.Arrays; import java.util.List; import java.util.concurrent.*; -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.function.Function; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +import static uk.ac.ic.doc.slurp.multilock.Constants.LOCK_ACQUISITION_TIMEOUT; import static uk.ac.ic.doc.slurp.multilock.LockMode.*; /** @@ -37,9 +34,6 @@ */ public class DowngradeTest { - // TODO(AR) this might need to be longer on slower machines... - private static final long LOCK_ACQUISITION_TIMEOUT = 40; - static List downgradeModesProvider() { return Arrays.asList( Arguments.of(IX, IS), @@ -78,6 +72,22 @@ public void downgradeTry(final LockMode fromMode, final LockMode toMode) assertDowngradable(fromMode, toMode, (mode, multiLock) -> mode.tryLock(multiLock)); } + @ParameterizedTest(name = "from {0} to {1}") + @DisplayName("Downgrade Lock Try (short timeout)") + @MethodSource("downgradeModesProvider") + public void downgradeTryShortTimeout(final LockMode fromMode, final LockMode toMode) + throws InterruptedException, ExecutionException { + assertDowngradable(fromMode, toMode, (mode, multiLock) -> mode.tryLock(multiLock, LOCK_ACQUISITION_TIMEOUT / 2, TimeUnit.MILLISECONDS)); + } + + @ParameterizedTest(name = "from {0} to {1}") + @DisplayName("Downgrade Lock Try (long timeout)") + @MethodSource("downgradeModesProvider") + public void downgradeTryLongTimeout(final LockMode fromMode, final LockMode toMode) + throws InterruptedException, ExecutionException { + assertDowngradable(fromMode, toMode, (mode, multiLock) -> mode.tryLock(multiLock, LOCK_ACQUISITION_TIMEOUT * 2, TimeUnit.MILLISECONDS)); + } + private static void assertDowngradable(final LockMode from, final LockMode to, final Locker lockFn) throws InterruptedException, ExecutionException { final MultiLock multiLock = new MultiLock(); diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/ReentrancyTest.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/ReentrancyTest.java index 6143f35..c186cf3 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/ReentrancyTest.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/ReentrancyTest.java @@ -2,10 +2,8 @@ import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; -import org.junit.jupiter.params.provider.ValueSource; import java.util.Arrays; import java.util.List; @@ -14,6 +12,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +import static uk.ac.ic.doc.slurp.multilock.Constants.LOCK_ACQUISITION_TIMEOUT; /** * Tests which check that acquiring the lock @@ -27,9 +26,7 @@ public class ReentrancyTest { private static final int REENTER_COUNT = 10; - - // TODO(AR) this might need to be longer on slower machines... - private static final long LOCK_ACQUISITIONS_TIMEOUT = 20 * REENTER_COUNT; + private static final long RENTER_LOCK_ACQUISITIONS_TIMEOUT = LOCK_ACQUISITION_TIMEOUT * REENTER_COUNT; @ParameterizedTest(name = "{0}") @DisplayName("Reentrant") @@ -52,6 +49,20 @@ public void reentrantTry(final LockMode lockMode) throws InterruptedException, E assertReentrant(lockMode, (mode, multiLock) -> { mode.tryLock(multiLock); return true; }); } + @ParameterizedTest(name = "{0}") + @DisplayName("Reentrant Try (short timeout)") + @EnumSource(value = LockMode.class) + public void reentrantTryShortTimeout(final LockMode lockMode) throws InterruptedException, ExecutionException { + assertReentrant(lockMode, (mode, multiLock) -> { mode.tryLock(multiLock, RENTER_LOCK_ACQUISITIONS_TIMEOUT / 2, TimeUnit.MILLISECONDS); return true; }); + } + + @ParameterizedTest(name = "{0}") + @DisplayName("Reentrant Try (long timeout)") + @EnumSource(value = LockMode.class) + public void reentrantTryLongTimeout(final LockMode lockMode) throws InterruptedException, ExecutionException { + assertReentrant(lockMode, (mode, multiLock) -> { mode.tryLock(multiLock, RENTER_LOCK_ACQUISITIONS_TIMEOUT * 2, TimeUnit.MILLISECONDS); return true; }); + } + private static void assertReentrant(final LockMode lockMode, final Locker lockFn) throws InterruptedException, ExecutionException { final MultiLock multiLock = new MultiLock(); @@ -59,7 +70,7 @@ private static void assertReentrant(final LockMode lockMode, final Locker lockFn final ExecutorService executorService = Executors.newSingleThreadExecutor(); final List> futures = executorService.invokeAll( Arrays.asList(new Reenter(multiLock, lockMode, lockFn, REENTER_COUNT)), - LOCK_ACQUISITIONS_TIMEOUT, TimeUnit.MILLISECONDS); + RENTER_LOCK_ACQUISITIONS_TIMEOUT, TimeUnit.MILLISECONDS); for (final Future future : futures) { assertTrue(future.isDone()); diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/UpgradeTest.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/UpgradeTest.java index b38d207..caa63d3 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/UpgradeTest.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/UpgradeTest.java @@ -1,7 +1,6 @@ package uk.ac.ic.doc.slurp.multilock; import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @@ -12,6 +11,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +import static uk.ac.ic.doc.slurp.multilock.Constants.LOCK_ACQUISITION_TIMEOUT; import static uk.ac.ic.doc.slurp.multilock.LockMode.*; /** @@ -34,9 +34,6 @@ */ public class UpgradeTest { - // TODO(AR) this might need to be longer on slower machines... - private static final long LOCK_ACQUISITION_TIMEOUT = 40; - static List upgradeModesProvider() { return Arrays.asList( Arguments.of(IS, IX), @@ -75,6 +72,22 @@ public void downgradeTry(final LockMode fromMode, final LockMode toMode) assertUpgradeable(fromMode, toMode, (mode, multiLock) -> mode.tryLock(multiLock)); } + @ParameterizedTest(name = "from {0} to {1}") + @DisplayName("Upgrade Lock Try (short timeout)") + @MethodSource("upgradeModesProvider") + public void downgradeTryShortTimeout(final LockMode fromMode, final LockMode toMode) + throws InterruptedException, ExecutionException { + assertUpgradeable(fromMode, toMode, (mode, multiLock) -> mode.tryLock(multiLock, LOCK_ACQUISITION_TIMEOUT / 2, TimeUnit.MILLISECONDS)); + } + + @ParameterizedTest(name = "from {0} to {1}") + @DisplayName("Upgrade Lock Try (long timeout)") + @MethodSource("upgradeModesProvider") + public void downgradeTryLongTimeout(final LockMode fromMode, final LockMode toMode) + throws InterruptedException, ExecutionException { + assertUpgradeable(fromMode, toMode, (mode, multiLock) -> mode.tryLock(multiLock, LOCK_ACQUISITION_TIMEOUT * 2, TimeUnit.MILLISECONDS)); + } + private static void assertUpgradeable(final LockMode from, final LockMode to, final Locker lockFn) throws InterruptedException, ExecutionException { final MultiLock multiLock = new MultiLock(); From b20a49d1aedfec2613437f635a0f1137418bcaee Mon Sep 17 00:00:00 2001 From: Adam Retter Date: Thu, 27 Jun 2019 12:37:10 +0100 Subject: [PATCH 16/21] [feature] Facilities for checking status --- .../ac/ic/doc/slurp/multilock/MultiLock.java | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java b/src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java index cca905d..a56bae2 100644 --- a/src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java +++ b/src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java @@ -340,6 +340,8 @@ else if (arg == S_UNIT) { } } } + + } private boolean updateState(long c, long arg, Thread current) { @@ -454,6 +456,11 @@ final int getWriteHoldCount() { //if (count == 0) readHolds.remove(); return count; } + + @Override + public boolean isHeldExclusively() { + return super.isHeldExclusively(); + } } public void readLock() { @@ -624,6 +631,31 @@ public int getWriteHoldCount() { return sync.getWriteHoldCount(); } + + + /** + * Queries whether any threads are waiting to acquire the read or + * write lock. Note that because cancellations may occur at any + * time, a {@code true} return does not guarantee that any other + * thread will ever acquire a lock. This method is designed + * primarily for use in monitoring of the system state. + * + * @return {@code true} if there may be other threads waiting to + * acquire the lock + */ + public final boolean hasQueuedThreads() { + return sync.hasQueuedThreads(); + } + + /** + * {@see java.util.concurrent.locks.AbstractQueuedLongSynchronizer#isHeldExclusively()} + * + * @return true if the current thread holds the write lock. + */ + public boolean isWriteLockedByCurrentThread() { + return sync.isHeldExclusively(); + } + /** * Returns the thread id for the given thread. We must access * this directly rather than via method Thread.getId() because From 02966970792a2c63916a50e818303f27fa57586e Mon Sep 17 00:00:00 2001 From: Adam Retter Date: Thu, 27 Jun 2019 12:38:51 +0100 Subject: [PATCH 17/21] [refactor] Prepare to publish as Evolved Binary --- .travis.yml | 19 +++++++++++ README.md | 31 ++++++++++++++--- pom.xml | 34 +++++++++++-------- .../ic/doc/slurp/multilock/HierarchyTest.java | 21 ++++++------ 4 files changed, 76 insertions(+), 29 deletions(-) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..dc609a6 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,19 @@ +language: java + +dist: xenial + +matrix: + include: + - jdk: openjdk8 + - jdk: openjdk9 + - jdk: openjdk10 + - jdk: openjdk11 + - jdk: openjdk12 + +cache: + directories: + - $HOME/.m2 + +install: mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V + +script: mvn verify -B diff --git a/README.md b/README.md index 3587d3b..d556541 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,30 @@ +# MutiLock + +[![Build Status](https://travis-ci.com/evolvedbinary/multilock.svg?branch=master)](https://travis-ci.com/evolvedbinary/mulitlock) + Java implementation of Fast Multi-Level locks. -**NOTE:** This is a Mavenized port of of the original https://github.com/kgudka/java-multilocks with a few additions. +**NOTE:** This is a Mavenized port of the [original source code](https://github.com/kgudka/java-multilocks) +with a few additions and modifications. + +The original source code was licensed under *The BSD 2-Clause License*, +as *Copyright (c) 2010-2016 Khilan Gudka*. + +We release our modifications under the same license but +*Copyright (c) 2017-2019 Evolved Binary Ltd*. + +The original paper by Khilan Gudka and Susan Eisenbach entitled `Fast Multi-Level Locks for Java` +can be found here: https://www.cl.cam.ac.uk/~kg365/pubs/ec2-fastlocks.pdf + + +## Maven Coordinates -This is the source-code accompanying our EC^2 2010 Workshop on Exploiting -Concurrency Efficiently and Correctly position paper. +The artifact is available from Maven Central as: -The paper can be found here: -http://pubs.doc.ic.ac.uk/fast-multi-level-locks/ +```xml + + com.evolvedbinary.multilock + multilock + 1.0.1 + +``` diff --git a/pom.xml b/pom.xml index 9f83bfa..f4680a1 100644 --- a/pom.xml +++ b/pom.xml @@ -7,22 +7,22 @@ org.sonatype.oss oss-parent - 7 + 9 - uk.ac.ic.doc.slurp.multilock + com.evolvedbinary.multilock multilock - 1.0-SNAPSHOT + 1.0.1-SNAPSHOT Multilock Fast Multi-Level Locks for Java - http://pubs.doc.ic.ac.uk/fast-multi-level-locks/ + https://www.cl.cam.ac.uk/~kg365/pubs/ec2-fastlocks.pdf 2010 - SLURP, Department of Computing, Imperial College London - http://slurp.doc.ic.ac.uk/ + Evolved Binary + https://evolvedbinary.com @@ -33,10 +33,16 @@ + + scm:git:https://github.com/evolvedbinary/multilock.git + scm:git:https://github.com/evolvedbinary/multilock.git + scm:git:https://github.com/evolvedbinary/multilock.git + + - 1.6 + 1.8 1.8 - 5.3.0-RC1 + 5.5.0-RC2 UTF-8 @@ -47,9 +53,9 @@ 3.0.2 - org.deucestm - deuce-annotations - 1.0-SNAPSHOT + com.evolvedbinary.thirdparty.org.deucestm + deucestm-annotations + 1.3.0 test @@ -101,7 +107,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.6.1 + 3.8.1 ${java.version} ${java.version} @@ -134,7 +140,7 @@ org.apache.maven.plugins maven-jar-plugin - 3.0.2 + 3.1.2 @@ -154,7 +160,7 @@ org.apache.maven.plugins maven-source-plugin - 3.0.1 + 3.1.0 diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/HierarchyTest.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/HierarchyTest.java index 42d767e..9e4c94e 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/HierarchyTest.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/HierarchyTest.java @@ -1,6 +1,7 @@ package uk.ac.ic.doc.slurp.multilock; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import java.util.Arrays; @@ -47,13 +48,13 @@ public void t1_root_IS_t1_child_IS_interruptibly() throws ExecutionException, In assertLockRootThenChildInterruptibly(IS, IS); } - // TODO - page 7 of Gray's paper - Granularity of Locks in a Shared Data Base + @Disabled("TODO - page 7 of Gray's paper - Granularity of Locks in a Shared Data Base") @Test public void t1_root_IS_t1_child_IX() throws ExecutionException, InterruptedException { assertLockRootThenNotChild(IS, IX); } - // TODO - page 7 of Gray's paper - Granularity of Locks in a Shared Data Base + @Disabled("TODO - page 7 of Gray's paper - Granularity of Locks in a Shared Data Base") @Test public void t1_root_IS_t1_child_IX_interruptibly() throws ExecutionException, InterruptedException { assertLockRootThenNotChildInterruptibly(IS, IX); @@ -69,25 +70,25 @@ public void t1_root_IS_t1_child_S_interruptibly() throws ExecutionException, Int assertLockRootThenChildInterruptibly(IS, S); } - // TODO - page 7 of Gray's paper - Granularity of Locks in a Shared Data Base + @Disabled("TODO - page 7 of Gray's paper - Granularity of Locks in a Shared Data Base") @Test public void t1_root_IS_t1_child_SIX() throws ExecutionException, InterruptedException { assertLockRootThenNotChild(IS, SIX); } - // TODO - page 7 of Gray's paper - Granularity of Locks in a Shared Data Base + @Disabled("TODO - page 7 of Gray's paper - Granularity of Locks in a Shared Data Base") @Test public void t1_root_IS_t1_child_SIX_interruptibly() throws ExecutionException, InterruptedException { assertLockRootThenNotChildInterruptibly(IS, SIX); } - // TODO - page 7 of Gray's paper - Granularity of Locks in a Shared Data Base + @Disabled("TODO - page 7 of Gray's paper - Granularity of Locks in a Shared Data Base") @Test public void t1_root_IS_t1_child_X() throws ExecutionException, InterruptedException { assertLockRootThenNotChild(IS, X); } - // TODO - page 7 of Gray's paper - Granularity of Locks in a Shared Data Base + @Disabled("TODO - page 7 of Gray's paper - Granularity of Locks in a Shared Data Base") @Test public void t1_root_IS_t1_child_X_interruptibly() throws ExecutionException, InterruptedException { assertLockRootThenNotChildInterruptibly(IS, X); @@ -145,13 +146,13 @@ public void t1_root_IX_t1_child_X_interruptibly() throws ExecutionException, Int //TODO(AR) need tests for t1_root_S_child... -- I think it's likely that S on root should only be able to get S on child - // TODO - page 7 of Gray's paper - Granularity of Locks in a Shared Data Base + @Disabled("TODO - page 7 of Gray's paper - Granularity of Locks in a Shared Data Base") @Test public void t1_root_SIX_t1_child_IS() throws ExecutionException, InterruptedException { assertLockRootThenNotChild(SIX, IS); } - // TODO - page 7 of Gray's paper - Granularity of Locks in a Shared Data Base + @Disabled("TODO - page 7 of Gray's paper - Granularity of Locks in a Shared Data Base") @Test public void t1_root_SIX_t1_child_IS_interruptibly() throws ExecutionException, InterruptedException { assertLockRootThenNotChildInterruptibly(SIX, IS); @@ -167,13 +168,13 @@ public void t1_root_SIX_t1_child_IX_interruptibly() throws ExecutionException, I assertLockRootThenChildInterruptibly(SIX, IX); } - // TODO - page 7 of Gray's paper - Granularity of Locks in a Shared Data Base + @Disabled("TODO - page 7 of Gray's paper - Granularity of Locks in a Shared Data Base") @Test public void t1_root_SIX_t1_child_S() throws ExecutionException, InterruptedException { assertLockRootThenNotChild(SIX, S); } - // TODO - page 7 of Gray's paper - Granularity of Locks in a Shared Data Base + @Disabled("TODO - page 7 of Gray's paper - Granularity of Locks in a Shared Data Base") @Test public void t1_root_SIX_t1_child_S_interruptibly() throws ExecutionException, InterruptedException { assertLockRootThenNotChildInterruptibly(SIX, S); From aafe5210205dbd79e8b5382215dacd51e9489946 Mon Sep 17 00:00:00 2001 From: Adam Retter Date: Thu, 27 Jun 2019 12:43:33 +0100 Subject: [PATCH 18/21] [refactor] Update the license with regards to our modifications --- BSD-2.txt | 26 +++++++++++ LICENSE | 26 +++++++++++ pom.xml | 4 +- .../multilock/HierarchicalMultiLock.java | 28 ++++++++++++ .../ac/ic/doc/slurp/multilock/LockMode.java | 28 ++++++++++++ .../ac/ic/doc/slurp/multilock/MultiLock.java | 44 ++++++++++--------- .../slurp/multilock/CompatibilityTest.java | 28 ++++++++++++ .../ac/ic/doc/slurp/multilock/Constants.java | 28 ++++++++++++ .../ic/doc/slurp/multilock/DowngradeTest.java | 28 ++++++++++++ .../ic/doc/slurp/multilock/HierarchyTest.java | 28 ++++++++++++ .../ac/ic/doc/slurp/multilock/Lockable.java | 44 ++++++++++--------- .../uk/ac/ic/doc/slurp/multilock/Locker.java | 28 ++++++++++++ .../doc/slurp/multilock/ReentrancyTest.java | 28 ++++++++++++ .../ic/doc/slurp/multilock/UpgradeTest.java | 28 ++++++++++++ .../ic/doc/slurp/multilock/bank/BankTest.java | 44 ++++++++++--------- .../multilock/bank/BankTestMultiLock.java | 44 ++++++++++--------- .../slurp/multilock/bank/BankTestRWLock.java | 44 ++++++++++--------- .../doc/slurp/multilock/bank/BankTestSTM.java | 44 ++++++++++--------- .../ic/doc/slurp/multilock/bank/Locker.java | 44 ++++++++++--------- .../slurp/multilock/counter/CounterTest.java | 44 ++++++++++--------- .../counter/CounterTestMultiLock.java | 44 ++++++++++--------- .../multilock/counter/CounterTestRWLock.java | 44 ++++++++++--------- .../multilock/counter/CounterTestSTM.java | 44 ++++++++++--------- 23 files changed, 560 insertions(+), 232 deletions(-) create mode 100644 BSD-2.txt create mode 100644 LICENSE diff --git a/BSD-2.txt b/BSD-2.txt new file mode 100644 index 0000000..4e8d900 --- /dev/null +++ b/BSD-2.txt @@ -0,0 +1,26 @@ +Copyright © ${project.inceptionYear}, ${owner} +All rights reserved. + +Modifications and additions from the original source code at +https://github.com/kgudka/java-multilocks are +Copyright © ${modifications.year}, ${modifications.owner} + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b63f466 --- /dev/null +++ b/LICENSE @@ -0,0 +1,26 @@ +Copyright © 2010, Khilan Gudka +All rights reserved. + +Modifications and additions from the original source code at +https://github.com/kgudka/java-multilocks are +Copyright © 2017, Evolved Binary Ltd + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/pom.xml b/pom.xml index f4680a1..dbd18c7 100644 --- a/pom.xml +++ b/pom.xml @@ -80,12 +80,14 @@ 3.0 true -
com/mycila/maven/plugin/license/templates/BSD-2.txt
+
BSD-2.txt
true true true Khilan Gudka + 2017 + ${project.organization.name} pom.xml diff --git a/src/main/java/uk/ac/ic/doc/slurp/multilock/HierarchicalMultiLock.java b/src/main/java/uk/ac/ic/doc/slurp/multilock/HierarchicalMultiLock.java index a616896..9229d23 100644 --- a/src/main/java/uk/ac/ic/doc/slurp/multilock/HierarchicalMultiLock.java +++ b/src/main/java/uk/ac/ic/doc/slurp/multilock/HierarchicalMultiLock.java @@ -1,3 +1,31 @@ +/** + * Copyright © 2010, Khilan Gudka + * All rights reserved. + * + * Modifications and additions from the original source code at + * https://github.com/kgudka/java-multilocks are + * Copyright © 2017, Evolved Binary + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ package uk.ac.ic.doc.slurp.multilock; import javax.annotation.Nullable; diff --git a/src/main/java/uk/ac/ic/doc/slurp/multilock/LockMode.java b/src/main/java/uk/ac/ic/doc/slurp/multilock/LockMode.java index 4a07718..f417dfd 100644 --- a/src/main/java/uk/ac/ic/doc/slurp/multilock/LockMode.java +++ b/src/main/java/uk/ac/ic/doc/slurp/multilock/LockMode.java @@ -1,3 +1,31 @@ +/** + * Copyright © 2010, Khilan Gudka + * All rights reserved. + * + * Modifications and additions from the original source code at + * https://github.com/kgudka/java-multilocks are + * Copyright © 2017, Evolved Binary + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ package uk.ac.ic.doc.slurp.multilock; import java.util.concurrent.TimeUnit; diff --git a/src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java b/src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java index a56bae2..06a7514 100644 --- a/src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java +++ b/src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java @@ -1,29 +1,31 @@ -/* - * Copyright (c) 2010-2016 Khilan Gudka +/** + * Copyright © 2010, Khilan Gudka * All rights reserved. * + * Modifications and additions from the original source code at + * https://github.com/kgudka/java-multilocks are + * Copyright © 2017, Evolved Binary + * * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - package uk.ac.ic.doc.slurp.multilock; diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/CompatibilityTest.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/CompatibilityTest.java index d8bfe91..0ca27e6 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/CompatibilityTest.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/CompatibilityTest.java @@ -1,3 +1,31 @@ +/** + * Copyright © 2010, Khilan Gudka + * All rights reserved. + * + * Modifications and additions from the original source code at + * https://github.com/kgudka/java-multilocks are + * Copyright © 2017, Evolved Binary + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ package uk.ac.ic.doc.slurp.multilock; import org.junit.jupiter.api.DisplayName; diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/Constants.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/Constants.java index 11b688f..bc44b3a 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/Constants.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/Constants.java @@ -1,3 +1,31 @@ +/** + * Copyright © 2010, Khilan Gudka + * All rights reserved. + * + * Modifications and additions from the original source code at + * https://github.com/kgudka/java-multilocks are + * Copyright © 2017, Evolved Binary + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ package uk.ac.ic.doc.slurp.multilock; public interface Constants { diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/DowngradeTest.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/DowngradeTest.java index 8fad534..09fdbb8 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/DowngradeTest.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/DowngradeTest.java @@ -1,3 +1,31 @@ +/** + * Copyright © 2010, Khilan Gudka + * All rights reserved. + * + * Modifications and additions from the original source code at + * https://github.com/kgudka/java-multilocks are + * Copyright © 2017, Evolved Binary + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ package uk.ac.ic.doc.slurp.multilock; import org.junit.jupiter.api.DisplayName; diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/HierarchyTest.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/HierarchyTest.java index 9e4c94e..5699c10 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/HierarchyTest.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/HierarchyTest.java @@ -1,3 +1,31 @@ +/** + * Copyright © 2010, Khilan Gudka + * All rights reserved. + * + * Modifications and additions from the original source code at + * https://github.com/kgudka/java-multilocks are + * Copyright © 2017, Evolved Binary + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ package uk.ac.ic.doc.slurp.multilock; diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/Lockable.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/Lockable.java index e94de00..9e39906 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/Lockable.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/Lockable.java @@ -1,29 +1,31 @@ -/* - * Copyright (c) 2010-2016 Khilan Gudka +/** + * Copyright © 2010, Khilan Gudka * All rights reserved. * + * Modifications and additions from the original source code at + * https://github.com/kgudka/java-multilocks are + * Copyright © 2017, Evolved Binary + * * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - package uk.ac.ic.doc.slurp.multilock; import java.util.concurrent.locks.*; diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/Locker.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/Locker.java index dbfb6f9..9c06bc4 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/Locker.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/Locker.java @@ -1,3 +1,31 @@ +/** + * Copyright © 2010, Khilan Gudka + * All rights reserved. + * + * Modifications and additions from the original source code at + * https://github.com/kgudka/java-multilocks are + * Copyright © 2017, Evolved Binary + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ package uk.ac.ic.doc.slurp.multilock; @FunctionalInterface diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/ReentrancyTest.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/ReentrancyTest.java index c186cf3..0c3fe9f 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/ReentrancyTest.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/ReentrancyTest.java @@ -1,3 +1,31 @@ +/** + * Copyright © 2010, Khilan Gudka + * All rights reserved. + * + * Modifications and additions from the original source code at + * https://github.com/kgudka/java-multilocks are + * Copyright © 2017, Evolved Binary + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ package uk.ac.ic.doc.slurp.multilock; diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/UpgradeTest.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/UpgradeTest.java index caa63d3..c3d894b 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/UpgradeTest.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/UpgradeTest.java @@ -1,3 +1,31 @@ +/** + * Copyright © 2010, Khilan Gudka + * All rights reserved. + * + * Modifications and additions from the original source code at + * https://github.com/kgudka/java-multilocks are + * Copyright © 2017, Evolved Binary + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ package uk.ac.ic.doc.slurp.multilock; import org.junit.jupiter.api.DisplayName; diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/bank/BankTest.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/bank/BankTest.java index 64731bf..f6462a9 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/bank/BankTest.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/bank/BankTest.java @@ -1,29 +1,31 @@ -/* - * Copyright (c) 2010-2016 Khilan Gudka +/** + * Copyright © 2010, Khilan Gudka * All rights reserved. * + * Modifications and additions from the original source code at + * https://github.com/kgudka/java-multilocks are + * Copyright © 2017, Evolved Binary + * * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - package uk.ac.ic.doc.slurp.multilock.bank; import java.io.FileNotFoundException; diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/bank/BankTestMultiLock.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/bank/BankTestMultiLock.java index bbad377..d1d1427 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/bank/BankTestMultiLock.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/bank/BankTestMultiLock.java @@ -1,29 +1,31 @@ -/* - * Copyright (c) 2010-2016 Khilan Gudka +/** + * Copyright © 2010, Khilan Gudka * All rights reserved. * + * Modifications and additions from the original source code at + * https://github.com/kgudka/java-multilocks are + * Copyright © 2017, Evolved Binary + * * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - package uk.ac.ic.doc.slurp.multilock.bank; import java.io.FileNotFoundException; diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/bank/BankTestRWLock.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/bank/BankTestRWLock.java index 9b77559..59c5341 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/bank/BankTestRWLock.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/bank/BankTestRWLock.java @@ -1,29 +1,31 @@ -/* - * Copyright (c) 2010-2016 Khilan Gudka +/** + * Copyright © 2010, Khilan Gudka * All rights reserved. * + * Modifications and additions from the original source code at + * https://github.com/kgudka/java-multilocks are + * Copyright © 2017, Evolved Binary + * * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - package uk.ac.ic.doc.slurp.multilock.bank; import java.io.FileNotFoundException; diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/bank/BankTestSTM.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/bank/BankTestSTM.java index 0b13b52..fcde445 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/bank/BankTestSTM.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/bank/BankTestSTM.java @@ -1,29 +1,31 @@ -/* - * Copyright (c) 2010-2016 Khilan Gudka +/** + * Copyright © 2010, Khilan Gudka * All rights reserved. * + * Modifications and additions from the original source code at + * https://github.com/kgudka/java-multilocks are + * Copyright © 2017, Evolved Binary + * * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - package uk.ac.ic.doc.slurp.multilock.bank; import java.io.FileNotFoundException; diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/bank/Locker.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/bank/Locker.java index 221e447..55c3e5a 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/bank/Locker.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/bank/Locker.java @@ -1,29 +1,31 @@ -/* - * Copyright (c) 2010-2016 Khilan Gudka +/** + * Copyright © 2010, Khilan Gudka * All rights reserved. * + * Modifications and additions from the original source code at + * https://github.com/kgudka/java-multilocks are + * Copyright © 2017, Evolved Binary + * * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - package uk.ac.ic.doc.slurp.multilock.bank; public interface Locker { diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/counter/CounterTest.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/counter/CounterTest.java index 3ad0501..f13c963 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/counter/CounterTest.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/counter/CounterTest.java @@ -1,29 +1,31 @@ -/* - * Copyright (c) 2010-2016 Khilan Gudka +/** + * Copyright © 2010, Khilan Gudka * All rights reserved. * + * Modifications and additions from the original source code at + * https://github.com/kgudka/java-multilocks are + * Copyright © 2017, Evolved Binary + * * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - package uk.ac.ic.doc.slurp.multilock.counter; import uk.ac.ic.doc.slurp.multilock.Lockable; diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/counter/CounterTestMultiLock.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/counter/CounterTestMultiLock.java index 3d098cd..b820b83 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/counter/CounterTestMultiLock.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/counter/CounterTestMultiLock.java @@ -1,29 +1,31 @@ -/* - * Copyright (c) 2010-2016 Khilan Gudka +/** + * Copyright © 2010, Khilan Gudka * All rights reserved. * + * Modifications and additions from the original source code at + * https://github.com/kgudka/java-multilocks are + * Copyright © 2017, Evolved Binary + * * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - package uk.ac.ic.doc.slurp.multilock.counter; public class CounterTestMultiLock extends CounterTest { diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/counter/CounterTestRWLock.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/counter/CounterTestRWLock.java index ca8f005..5939c01 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/counter/CounterTestRWLock.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/counter/CounterTestRWLock.java @@ -1,29 +1,31 @@ -/* - * Copyright (c) 2010-2016 Khilan Gudka +/** + * Copyright © 2010, Khilan Gudka * All rights reserved. * + * Modifications and additions from the original source code at + * https://github.com/kgudka/java-multilocks are + * Copyright © 2017, Evolved Binary + * * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - package uk.ac.ic.doc.slurp.multilock.counter; public class CounterTestRWLock extends CounterTest { diff --git a/src/test/java/uk/ac/ic/doc/slurp/multilock/counter/CounterTestSTM.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/counter/CounterTestSTM.java index 9a1b676..8792926 100644 --- a/src/test/java/uk/ac/ic/doc/slurp/multilock/counter/CounterTestSTM.java +++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/counter/CounterTestSTM.java @@ -1,29 +1,31 @@ -/* - * Copyright (c) 2010-2016 Khilan Gudka +/** + * Copyright © 2010, Khilan Gudka * All rights reserved. * + * Modifications and additions from the original source code at + * https://github.com/kgudka/java-multilocks are + * Copyright © 2017, Evolved Binary + * * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - package uk.ac.ic.doc.slurp.multilock.counter; import org.deuce.Atomic; From 4f787745d325fa86e0b340c4e16df659408b0d9e Mon Sep 17 00:00:00 2001 From: Adam Retter Date: Thu, 27 Jun 2019 13:42:35 +0100 Subject: [PATCH 19/21] [doc] Fix javadoc --- src/main/java/uk/ac/ic/doc/slurp/multilock/LockMode.java | 3 --- src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/java/uk/ac/ic/doc/slurp/multilock/LockMode.java b/src/main/java/uk/ac/ic/doc/slurp/multilock/LockMode.java index f417dfd..e0e91a8 100644 --- a/src/main/java/uk/ac/ic/doc/slurp/multilock/LockMode.java +++ b/src/main/java/uk/ac/ic/doc/slurp/multilock/LockMode.java @@ -121,8 +121,6 @@ public void unlock(final MultiLock multiLock) { * @param multiLock the MultiLock object. * @param lockMode the mode to lock the MultiLock. * - * @return true if the lock succeeded, false otherwise. - * * @throws IllegalArgumentException if an unknown mode is provided. */ public static void lock(final MultiLock multiLock, final LockMode lockMode) { @@ -162,7 +160,6 @@ public static void lock(final MultiLock multiLock, final LockMode lockMode) { * @return true if the lock was acquired. * * @throws IllegalArgumentException if an unknown mode is provided. - * @throws InterruptedException if the thread was interrupted */ public static boolean tryLock(final MultiLock multiLock, final LockMode lockMode) { switch (lockMode) { diff --git a/src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java b/src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java index 06a7514..468e317 100644 --- a/src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java +++ b/src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java @@ -650,7 +650,7 @@ public final boolean hasQueuedThreads() { } /** - * {@see java.util.concurrent.locks.AbstractQueuedLongSynchronizer#isHeldExclusively()} + * See {@link java.util.concurrent.locks.AbstractQueuedLongSynchronizer#isHeldExclusively()} * * @return true if the current thread holds the write lock. */ From fc9a6b306d98e92a6ac6e1a997097a6203ec3f83 Mon Sep 17 00:00:00 2001 From: Adam Retter Date: Thu, 27 Jun 2019 13:44:32 +0100 Subject: [PATCH 20/21] [maven-release-plugin] prepare release 1.0.1 --- pom.xml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index dbd18c7..731d182 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,5 @@ - + 4.0.0 @@ -13,7 +11,7 @@ com.evolvedbinary.multilock multilock - 1.0.1-SNAPSHOT + 1.0.1 Multilock Fast Multi-Level Locks for Java @@ -37,7 +35,8 @@ scm:git:https://github.com/evolvedbinary/multilock.git scm:git:https://github.com/evolvedbinary/multilock.git scm:git:https://github.com/evolvedbinary/multilock.git - + 1.0.1 + 1.8 From 4ce10642ee21ddf6e65f150b4e9db7ffed03291c Mon Sep 17 00:00:00 2001 From: Adam Retter Date: Thu, 27 Jun 2019 13:44:40 +0100 Subject: [PATCH 21/21] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 731d182..76929b4 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ com.evolvedbinary.multilock multilock - 1.0.1 + 1.0.2-SNAPSHOT Multilock Fast Multi-Level Locks for Java @@ -35,7 +35,7 @@ scm:git:https://github.com/evolvedbinary/multilock.git scm:git:https://github.com/evolvedbinary/multilock.git scm:git:https://github.com/evolvedbinary/multilock.git - 1.0.1 + HEAD