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/.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/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/README.md b/README.md
index 32e2f0b..d556541 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,30 @@
+# MutiLock
+
+[](https://travis-ci.com/evolvedbinary/mulitlock)
+
Java implementation of Fast Multi-Level locks.
-This is the source-code accompanying our EC^2 2010 Workshop on Exploiting
-Concurrency Efficiently and Correctly position paper.
+**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
+
+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/multilock/MultiLock.java b/multilock/MultiLock.java
deleted file mode 100644
index edc33a9..0000000
--- a/multilock/MultiLock.java
+++ /dev/null
@@ -1,375 +0,0 @@
-/*
- * Copyright (c) 2010-2016 Khilan Gudka
- * All rights reserved.
- *
- * 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 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.
- */
-
-package multilock;
-
-
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.locks.*;
-
-public class MultiLock {
-
- // individual field masks
- static final long X_FIELD = 0xFFFF000000000000L;
- static final long S_FIELD = 0x0000FFFF00000000L;
- static final long IX_FIELD = 0x00000000FFFF0000L;
- static final long IS_FIELD = 0x000000000000FFFFL;
- static final long NON_X_FIELDS = ~X_FIELD;
-
- // bit patterns for inc/dec individual fields
- static final long X_UNIT = 0x0001000000000000L;
- static final long S_UNIT = 0x0000000100000000L;
- static final long IX_UNIT = 0x0000000000010000L;
- static final long IS_UNIT = 0x0000000000000001L;
-
- final MultiLock owner;
- final Sync sync;
-
- final ReadLock readLock;
- final WriteLock writeLock;
-
- public MultiLock(MultiLock o) {
- owner = o;
- sync = new Sync();
- readLock = new ReadLock();
- writeLock = new WriteLock();
- }
-
- class ReadLock implements Lock {
-
- public void lock() {
- lockRead();
- }
-
- public void unlock() {
- unlockRead();
- }
-
- public void lockInterruptibly() throws InterruptedException {
- throw new UnsupportedOperationException();
- }
-
- public Condition newCondition() {
- throw new UnsupportedOperationException();
- }
-
- public boolean tryLock() {
- throw new UnsupportedOperationException();
- }
-
- public boolean tryLock(long time, TimeUnit unit)
- throws InterruptedException {
- throw new UnsupportedOperationException();
- }
-
- }
-
- class WriteLock implements Lock {
-
- public void lock() {
- lockWrite();
- }
-
- public void unlock() {
- unlockWrite();
- }
-
- public void lockInterruptibly() throws InterruptedException {
- throw new UnsupportedOperationException();
- }
-
- public Condition newCondition() {
- throw new UnsupportedOperationException();
- }
-
- public boolean tryLock() {
- throw new UnsupportedOperationException();
- }
-
- public boolean tryLock(long time, TimeUnit unit)
- throws InterruptedException {
- throw new UnsupportedOperationException();
- }
-
- }
-
- public Lock readLock() { return readLock; }
-
- public Lock writeLock() { return writeLock; }
-
- static class Sync extends AbstractQueuedLongSynchronizer {
-
- static long xCount(long c) { return (c & X_FIELD) >>> 48; }
- static long sCount(long c) { return (c & S_FIELD) >> 32; }
- static long ixCount(long c) { return (c & IX_FIELD) >> 16; }
- static long isCount(long c) { return c & IS_FIELD; }
-
- // store's per-thread state
- static class HoldCounter {
- final long tid = Thread.currentThread().getId();
- long state = 0;
- }
-
- static class ThreadLocalHoldCounter extends ThreadLocal {
- @Override
- protected HoldCounter initialValue() {
- return new HoldCounter();
- }
- }
-
- final ThreadLocalHoldCounter holdCounts;
-
- HoldCounter cachedHoldCounter;
-
- Sync() {
- holdCounts = new ThreadLocalHoldCounter();
- setState(getState()); // ensures visibility of holdCounts
- }
-
- @Override
- protected boolean tryAcquire(long arg) {
- Thread current = Thread.currentThread();
- long c = getState();
- if (c != 0) {
- long x = c & X_FIELD;
- // (Note: if c != 0 and x == 0 then non-exclusive count != 0)
- if (x == 0) {
- // Check non-exclusive counts are only for current.
- // i.e. are we upgrading?
- HoldCounter rh = cachedHoldCounter;
- if (rh == null || rh.tid != current.getId())
- rh = holdCounts.get();
- long group = c - rh.state;
- if ((group & NON_X_FIELDS) != 0) {
- // current thread is not only non-exclusive user
- return false;
- }
- }
- else if (current != getExclusiveOwnerThread()) {
- return false;
- }
- }
- if (!compareAndSetState(c, c + X_UNIT))
- return false;
- setExclusiveOwnerThread(current);
- return true;
- }
-
- @Override
- protected boolean tryRelease(long arg) {
- long nextc = getState() - arg;
- if (Thread.currentThread() != getExclusiveOwnerThread()) {
- throw new IllegalMonitorStateException();
- }
- if ((nextc & X_FIELD) == 0) {
- setExclusiveOwnerThread(null);
- setState(nextc);
- return true;
- }
- else {
- setState(nextc);
- return false;
- }
- }
-
- @Override
- protected long tryAcquireShared(long arg) {
- Thread current = Thread.currentThread();
- for (;;) {
- long c = getState();
- // someone else already is X
- if ((c & X_FIELD) != 0 &&
- getExclusiveOwnerThread() != current)
- return -1;
- // either no X or current is X
- if (getExclusiveOwnerThread() == current) {
- if (updateState(c, arg, current)) {
- return 0;
- }
- }
- else if (arg == IS_UNIT) { // IS is compatible with S, IX, IS
- if (updateState(c, arg, current)) {
- return 1;
- }
- }
- else if (arg == IX_UNIT) {
- // two cases: S == 0 (ok) and S != 0 (thread check)
- long s = c & S_FIELD;
- if (s == 0) {
- // ok
- if (updateState(c, arg, current)) {
- return 1;
- }
- }
- else {
- // Check if current thread is the only S
- // TODO: optimise so that re-entrant IX locking is fast
- HoldCounter rh = null; //cachedHoldCounter;
- if (rh == null || rh.tid != current.getId())
- rh = holdCounts.get();
- long group = c - rh.state;
- if ((group & S_FIELD) == 0) {
- // current is only S
- if (compareAndSetState(c, c + arg)) {
- rh.state += arg;
- cachedHoldCounter = rh;
- return 1; // still return 1 because IS is always compatible
- }
- }
- }
- }
- else if (arg == S_UNIT) {
- // two cases: IX == 0 (ok) and IX != 0 (thread check)
- long ix = c & IX_FIELD;
- if (ix == 0) {
- // ok
- if (updateState(c, arg, current)) {
- return 1;
- }
- }
- else {
- // check if current thread is the only IX
- // TODO: optimise so that re-entrant S locking is fast
- HoldCounter rh = cachedHoldCounter;
- if (rh == null || rh.tid != current.getId())
- rh = holdCounts.get();
- long group = c - rh.state;
- if ((group & IX_FIELD) == 0) {
- // current is only IX
- if (compareAndSetState(c, c + arg)) {
- rh.state += arg;
- cachedHoldCounter = rh;
- return 1; // still return 1 because IS is always compatible
- }
- }
- }
- }
- }
- }
-
- private boolean updateState(long c, long arg, Thread current) {
- if (compareAndSetState(c, c + arg)) {
- HoldCounter rh = cachedHoldCounter;
- if (rh == null || rh.tid != current.getId())
- cachedHoldCounter = rh = holdCounts.get();
- rh.state += arg;
- return true;
- }
- return false;
- }
-
- @Override
- protected boolean tryReleaseShared(long arg) {
- HoldCounter rh = cachedHoldCounter;
- Thread current = Thread.currentThread();
- if (rh == null || rh.tid != current.getId())
- rh = holdCounts.get();
- rh.state -= arg;
- for (;;) {
- long c = getState();
- long nextc = c - arg;
- if (compareAndSetState(c, nextc)) {
- if ((nextc & X_FIELD) == 0) {
- if (arg == S_UNIT) {
- return (nextc & S_FIELD) == 0;
- }
- if (arg == IS_UNIT) {
- return (nextc & IS_FIELD) == 0;
- }
- if (arg == IX_UNIT) {
- return (nextc & IX_FIELD) == 0;
- }
- }
- else {
- return false;
- }
- }
- }
- }
-
- }
-
- 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/pom.xml b/pom.xml
new file mode 100644
index 0000000..76929b4
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,225 @@
+
+
+ 4.0.0
+
+
+ org.sonatype.oss
+ oss-parent
+ 9
+
+
+
+ com.evolvedbinary.multilock
+ multilock
+ 1.0.2-SNAPSHOT
+
+ Multilock
+ Fast Multi-Level Locks for Java
+ https://www.cl.cam.ac.uk/~kg365/pubs/ec2-fastlocks.pdf
+ 2010
+
+
+ Evolved Binary
+ https://evolvedbinary.com
+
+
+
+
+ The BSD 2-Clause License
+ http://www.opensource.org/licenses/BSD-2-Clause
+ repo
+
+
+
+
+ scm:git:https://github.com/evolvedbinary/multilock.git
+ scm:git:https://github.com/evolvedbinary/multilock.git
+ scm:git:https://github.com/evolvedbinary/multilock.git
+ HEAD
+
+
+
+ 1.8
+ 1.8
+ 5.5.0-RC2
+ UTF-8
+
+
+
+
+ com.google.code.findbugs
+ jsr305
+ 3.0.2
+
+
+ com.evolvedbinary.thirdparty.org.deucestm
+ deucestm-annotations
+ 1.3.0
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ ${junit.version}
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-params
+ ${junit.version}
+ test
+
+
+
+
+
+
+ com.mycila
+ license-maven-plugin
+ 3.0
+ true
+
+
+ true
+ true
+ true
+
+ Khilan Gudka
+ 2017
+ ${project.organization.name}
+
+
+ pom.xml
+ README.md
+ LICENSE
+
+ ${project.build.sourceEncoding}
+
+
+
+ check-headers
+ verify
+
+ check
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.1
+
+ ${java.version}
+ ${java.version}
+ ${tests.java.version}
+ ${tests.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.1.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.1.0
+
+
+
+ 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-surefire-plugin
+ 2.22.0
+
+
+ org.apache.maven.plugins
+ maven-release-plugin
+ 2.5.3
+
+ forked-path
+ true
+ @{project.version}
+
+
+
+
+
+
+
+ clojars.org
+ http://clojars.org/repo
+
+
+
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..9229d23
--- /dev/null
+++ b/src/main/java/uk/ac/ic/doc/slurp/multilock/HierarchicalMultiLock.java
@@ -0,0 +1,369 @@
+/**
+ * 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;
+import java.util.concurrent.TimeUnit;
+
+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 void readLock() {
+ if (parent != null) {
+ parent.intentionReadLock();
+ }
+ 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;
+ }
+
+ /**
+ * @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) {
+ parent.intentionReadLockInterruptibly();
+ }
+
+ try {
+ super.readLockInterruptibly();
+ } catch (final InterruptedException e) {
+ if (parent != null) {
+ parent.unlockIntentionRead();
+ }
+ throw e;
+ }
+ }
+
+ @Override
+ public void writeLock() {
+ if (parent != null) {
+ parent.intentionWriteLock();
+ }
+ 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;
+ }
+
+ /**
+ * @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) {
+ parent.intentionWriteLockInterruptibly();
+ }
+
+ try {
+ super.writeLockInterruptibly();
+ } catch (final InterruptedException e) {
+ if (parent != null) {
+ parent.unlockIntentionWrite();
+ }
+ throw e;
+ }
+ }
+
+ @Override
+ public void intentionReadLock() {
+ if (parent != null) {
+ parent.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;
+ }
+
+ /**
+ * @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) {
+ parent.intentionReadLockInterruptibly();
+ }
+
+ try {
+ super.intentionReadLockInterruptibly();
+ } catch (final InterruptedException e) {
+ if (parent != null) {
+ parent.unlockIntentionRead();
+ }
+ throw e;
+ }
+ }
+
+ @Override
+ public void intentionWriteLock() {
+ if (parent != null) {
+ parent.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;
+ }
+
+ /**
+ * @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) {
+ parent.intentionWriteLockInterruptibly();
+ }
+
+ try {
+ super.intentionWriteLockInterruptibly();
+ } catch (final InterruptedException e) {
+ if (parent != null) {
+ parent.unlockIntentionWrite();
+ }
+ throw e;
+ }
+ }
+
+ @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/LockMode.java b/src/main/java/uk/ac/ic/doc/slurp/multilock/LockMode.java
new file mode 100644
index 0000000..e0e91a8
--- /dev/null
+++ b/src/main/java/uk/ac/ic/doc/slurp/multilock/LockMode.java
@@ -0,0 +1,318 @@
+/**
+ * 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;
+
+/**
+ * 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.
+ */
+ 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);
+ }
+
+ /**
+ * 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.
+ *
+ * @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.
+ *
+ * @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.
+ *
+ * @throws IllegalArgumentException if an unknown mode is provided.
+ */
+ public static void lock(final MultiLock multiLock, final LockMode lockMode) {
+ switch (lockMode) {
+ case IS:
+ multiLock.intentionReadLock();
+ break;
+
+ case IX:
+ multiLock.intentionWriteLock();
+ break;
+
+ case S:
+ multiLock.readLock();
+ break;
+
+ case SIX:
+ multiLock.readLock();
+ multiLock.intentionWriteLock();
+ break;
+
+ case X:
+ multiLock.writeLock();
+ break;
+
+ default:
+ throw new IllegalArgumentException("Unknown lock mode: " + 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.
+ */
+ 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);
+ }
+ }
+
+ /**
+ * 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.
+ *
+ * @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.
+ *
+ * @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/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java b/src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java
new file mode 100644
index 0000000..468e317
--- /dev/null
+++ b/src/main/java/uk/ac/ic/doc/slurp/multilock/MultiLock.java
@@ -0,0 +1,687 @@
+/**
+ * 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 sun.misc.Unsafe;
+
+import java.lang.reflect.Field;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.*;
+
+public class MultiLock {
+
+ // individual field masks
+ static final long X_FIELD = 0xFFFF000000000000L;
+ static final long S_FIELD = 0x0000FFFF00000000L;
+ static final long IX_FIELD = 0x00000000FFFF0000L;
+ static final long IS_FIELD = 0x000000000000FFFFL;
+ static final long NON_X_FIELDS = ~X_FIELD;
+
+ // bit patterns for inc/dec individual fields
+ static final long X_UNIT = 0x0001000000000000L;
+ static final long S_UNIT = 0x0000000100000000L;
+ static final long IX_UNIT = 0x0000000000010000L;
+ static final long IS_UNIT = 0x0000000000000001L;
+
+ final Sync sync;
+
+ // views
+ transient ReadLockView readLockView;
+ transient WriteLockView writeLockView;
+ transient ReadWriteLockView readWriteLockView;
+
+ /**
+ * Constructs a MultiLock.
+ */
+ public MultiLock() {
+ this.sync = new Sync();
+ }
+
+ final class ReadLockView implements Lock {
+
+ @Override
+ public void lock() {
+ readLock();
+ }
+
+ @Override
+ public void unlock() {
+ unlockRead();
+ }
+
+ @Override
+ public void lockInterruptibly() throws InterruptedException {
+ readLockInterruptibly();
+ }
+
+ @Override
+ public Condition newCondition() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean tryLock() {
+ return tryReadLock();
+ }
+
+ @Override
+ public boolean tryLock(final long time, final TimeUnit unit)
+ throws InterruptedException {
+ return tryReadLock(time, unit);
+ }
+ }
+
+ final class WriteLockView implements Lock {
+
+ @Override
+ public void lock() {
+ writeLock();
+ }
+
+ @Override
+ public void unlock() {
+ unlockWrite();
+ }
+
+ @Override
+ public void lockInterruptibly() throws InterruptedException {
+ writeLockInterruptibly();
+ }
+
+ @Override
+ public Condition newCondition() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean tryLock() {
+ return tryWriteLock();
+ }
+
+ @Override
+ public boolean tryLock(final long time, final TimeUnit unit)
+ throws InterruptedException {
+ return tryWriteLock(time, unit);
+ }
+ }
+
+ 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()));
+ }
+
+ static class Sync extends AbstractQueuedLongSynchronizer {
+
+ static long xCount(long c) { return (c & X_FIELD) >>> 48; }
+ static long sCount(long c) { return (c & S_FIELD) >> 32; }
+ static long ixCount(long c) { return (c & IX_FIELD) >> 16; }
+ static long isCount(long c) { return c & IS_FIELD; }
+
+ // store's per-thread state
+ static class HoldCounter {
+ final long tid = Thread.currentThread().getId();
+ long state = 0;
+ }
+
+ static class ThreadLocalHoldCounter extends ThreadLocal {
+ @Override
+ protected HoldCounter initialValue() {
+ return new HoldCounter();
+ }
+ }
+
+ final ThreadLocalHoldCounter holdCounts;
+
+ HoldCounter cachedHoldCounter;
+
+ Sync() {
+ holdCounts = new ThreadLocalHoldCounter();
+ setState(getState()); // ensures visibility of holdCounts
+ }
+
+ @Override
+ protected boolean tryAcquire(long arg) {
+ Thread current = Thread.currentThread();
+ long c = getState();
+ if (c != 0) {
+ long x = c & X_FIELD;
+ // (Note: if c != 0 and x == 0 then non-exclusive count != 0)
+ if (x == 0) {
+ // Check non-exclusive counts are only for current.
+ // i.e. are we upgrading?
+ HoldCounter rh = cachedHoldCounter;
+ if (rh == null || rh.tid != current.getId())
+ rh = holdCounts.get();
+ long group = c - rh.state;
+ if ((group & NON_X_FIELDS) != 0) {
+ // current thread is not only non-exclusive user
+ return false;
+ }
+ }
+ else if (current != getExclusiveOwnerThread()) {
+ return false;
+ }
+ }
+ if (!compareAndSetState(c, c + X_UNIT))
+ return false;
+ setExclusiveOwnerThread(current);
+ return true;
+ }
+
+ @Override
+ protected boolean tryRelease(long arg) {
+ long nextc = getState() - arg;
+ if (Thread.currentThread() != getExclusiveOwnerThread()) {
+ throw new IllegalMonitorStateException();
+ }
+ if ((nextc & X_FIELD) == 0) {
+ setExclusiveOwnerThread(null);
+ setState(nextc);
+ return true;
+ }
+ else {
+ setState(nextc);
+ return false;
+ }
+ }
+
+ @Override
+ protected long tryAcquireShared(long arg) {
+ Thread current = Thread.currentThread();
+ for (;;) {
+ long c = getState();
+ // someone else already is X
+ if ((c & X_FIELD) != 0 &&
+ getExclusiveOwnerThread() != current)
+ return -1;
+ // either no X or current is X
+ if (getExclusiveOwnerThread() == current) {
+ if (updateState(c, arg, current)) {
+ return 0;
+ }
+ }
+ else if (arg == IS_UNIT) { // IS is compatible with S, IX, IS
+ if (updateState(c, arg, current)) {
+ return 1;
+ }
+ }
+ else if (arg == IX_UNIT) {
+ // two cases: S == 0 (ok) and S != 0 (thread check)
+ long s = c & S_FIELD;
+ if (s == 0) {
+ // ok
+ if (updateState(c, arg, current)) {
+ return 1;
+ }
+ }
+ else {
+ // Check if current thread is the only S
+ // TODO: optimise so that re-entrant IX locking is fast
+ HoldCounter rh = null; //cachedHoldCounter;
+ if (rh == null || rh.tid != current.getId())
+ rh = holdCounts.get();
+ long group = c - rh.state;
+ if ((group & S_FIELD) == 0) {
+ // current is only S
+ if (compareAndSetState(c, c + arg)) {
+ rh.state += arg;
+ cachedHoldCounter = rh;
+ return 1; // still return 1 because IS is always compatible
+ }
+ } else {
+ return -1;
+ }
+ }
+ }
+ else if (arg == S_UNIT) {
+ // two cases: IX == 0 (ok) and IX != 0 (thread check)
+ long ix = c & IX_FIELD;
+ if (ix == 0) {
+ // ok
+ if (updateState(c, arg, current)) {
+ return 1;
+ }
+ }
+ else {
+ // check if current thread is the only IX
+ // TODO: optimise so that re-entrant S locking is fast
+ HoldCounter rh = cachedHoldCounter;
+ if (rh == null || rh.tid != current.getId())
+ rh = holdCounts.get();
+ long group = c - rh.state;
+ if ((group & IX_FIELD) == 0) {
+ // current is only IX
+ if (compareAndSetState(c, c + arg)) {
+ rh.state += arg;
+ cachedHoldCounter = rh;
+ return 1; // still return 1 because IS is always compatible
+ }
+ } else {
+ return -1;
+ }
+ }
+ }
+ }
+
+
+ }
+
+ private boolean updateState(long c, long arg, Thread current) {
+ if (compareAndSetState(c, c + arg)) {
+ HoldCounter rh = cachedHoldCounter;
+ if (rh == null || rh.tid != current.getId())
+ cachedHoldCounter = rh = holdCounts.get();
+ rh.state += arg;
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ protected boolean tryReleaseShared(long arg) {
+ HoldCounter rh = cachedHoldCounter;
+ Thread current = Thread.currentThread();
+ if (rh == null || rh.tid != current.getId())
+ rh = holdCounts.get();
+ rh.state -= arg;
+ for (;;) {
+ long c = getState();
+ long nextc = c - arg;
+ if (compareAndSetState(c, nextc)) {
+ if ((nextc & X_FIELD) == 0) {
+ if (arg == S_UNIT) {
+ return (nextc & S_FIELD) == 0;
+ }
+ if (arg == IS_UNIT) {
+ return (nextc & IS_FIELD) == 0;
+ }
+ if (arg == IX_UNIT) {
+ return (nextc & IX_FIELD) == 0;
+ }
+ }
+ else {
+ return false;
+ }
+ }
+ }
+ }
+
+ 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;
+ }
+
+ @Override
+ public boolean isHeldExclusively() {
+ return super.isHeldExclusively();
+ }
+ }
+
+ public void readLock() {
+ sync.acquireShared(S_UNIT);
+ }
+
+ 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);
+ }
+
+ public void writeLock() {
+ sync.acquire(X_UNIT);
+ }
+
+ 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);
+ }
+
+ public void intentionReadLock() {
+ sync.acquireShared(IS_UNIT);
+ }
+
+ 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);
+ }
+
+ public void intentionWriteLock() {
+ sync.acquireShared(IX_UNIT);
+ }
+
+ 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);
+ }
+
+ public void unlockRead() {
+ sync.releaseShared(S_UNIT);
+ }
+
+ public void unlockWrite() {
+ sync.release(X_UNIT);
+ }
+
+ public void unlockIntentionRead() {
+ sync.releaseShared(IS_UNIT);
+ }
+
+ public void unlockIntentionWrite() {
+ sync.releaseShared(IX_UNIT);
+ }
+
+ /**
+ * 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();
+ }
+
+
+
+ /**
+ * 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 {@link 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
+ * 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);
+ }
+ }
+}
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..0ca27e6
--- /dev/null
+++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/CompatibilityTest.java
@@ -0,0 +1,260 @@
+/**
+ * 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;
+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 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.*;
+
+/**
+ * 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 {
+
+ 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 = "{0} and {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; }, true);
+ }
+ }
+
+ @ParameterizedTest(name = "{0} and {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; }, 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);
+ }
+ }
+
+ @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.
+ *
+ * @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);
+ for (final Future future : futures) {
+ assertTrue(future.isDone());
+ assertFalse(future.isCancelled());
+
+ assertTrue(future.get());
+ }
+ }
+
+ /**
+ * 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());
+
+ 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,
+ final Locker lockFn) throws InterruptedException {
+ final MultiLock multiLock = new MultiLock();
+
+ final CountDownLatch latch = new CountDownLatch(2);
+
+ 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);
+ }
+
+ 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 Locker lockFn,
+ final CountDownLatch latch) {
+ this.multiLock = multiLock;
+ this.lockMode = lockMode;
+ this.lockFn = lockFn;
+ this.latch = latch;
+ }
+
+ @Override
+ public Boolean call() throws Exception {
+ final boolean locked = lockFn.lock(lockMode, multiLock);
+
+ latch.countDown();
+ latch.await();
+
+ return locked;
+ }
+ }
+}
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..bc44b3a
--- /dev/null
+++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/Constants.java
@@ -0,0 +1,35 @@
+/**
+ * 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 {
+
+ // 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
new file mode 100644
index 0000000..09fdbb8
--- /dev/null
+++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/DowngradeTest.java
@@ -0,0 +1,157 @@
+/**
+ * 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;
+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 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.*;
+
+/**
+ * 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 {
+
+ 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; });
+ }
+
+ @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));
+ }
+
+ @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();
+
+ final ExecutorService executorService = Executors.newSingleThreadExecutor();
+ 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());
+ 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 final Locker lockFn;
+
+ 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 {
+ 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/HierarchyTest.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/HierarchyTest.java
new file mode 100644
index 0000000..5699c10
--- /dev/null
+++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/HierarchyTest.java
@@ -0,0 +1,891 @@
+/**
+ * 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.Disabled;
+import org.junit.jupiter.api.Test;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.*;
+
+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.*;
+
+/**
+ * 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);
+ }
+
+ @Test
+ public void t1_root_IS_t1_child_IS_interruptibly() throws ExecutionException, InterruptedException {
+ assertLockRootThenChildInterruptibly(IS, IS);
+ }
+
+ @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);
+ }
+
+ @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);
+ }
+
+ @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);
+ }
+
+ @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);
+ }
+
+ @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);
+ }
+
+ @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);
+ }
+
+ @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);
+ }
+
+ @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
+
+ @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);
+ }
+
+ @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);
+ }
+
+ @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);
+ }
+
+ @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);
+ }
+
+ @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);
+ }
+
+ @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
+
+
+ /* bi-threaded tests below */
+
+ @Test
+ public void t1_root_IS_t2_child_IS() throws InterruptedException, ExecutionException {
+ final MultiLock root = new MultiLock();
+ IS.lock(root);
+
+ final MultiLock child = new HierarchicalMultiLock(root);
+ assertOtherThreadLockableChild(child, IS);
+
+ }
+
+ @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();
+ IS.lock(root);
+
+ final MultiLock child = new HierarchicalMultiLock(root);
+ 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();
+ IS.lock(root);
+
+ final MultiLock child = new HierarchicalMultiLock(root);
+ 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();
+ IS.lock(root);
+
+ final MultiLock child = new HierarchicalMultiLock(root);
+ 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();
+ IS.lock(root);
+
+ final MultiLock child = new HierarchicalMultiLock(root);
+ 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();
+ IX.lock(root);
+
+ final MultiLock child = new HierarchicalMultiLock(root);
+ assertOtherThreadLockableChild(child, IS);
+
+ }
+
+ @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();
+ IX.lock(root);
+
+ final MultiLock child = new HierarchicalMultiLock(root);
+ 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();
+ IX.lock(root);
+
+ final MultiLock child = new HierarchicalMultiLock(root);
+ 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();
+ IX.lock(root);
+
+ final MultiLock child = new HierarchicalMultiLock(root);
+ 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();
+ IX.lock(root);
+
+ final MultiLock child = new HierarchicalMultiLock(root);
+ 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();
+ S.lock(root);
+
+ 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
+ public void t1_root_S_t2_child_IX() throws InterruptedException, ExecutionException {
+ final MultiLock root = new MultiLock();
+ 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
+ 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();
+ S.lock(root);
+
+ final MultiLock child = new HierarchicalMultiLock(root);
+ 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();
+ 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
+ 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();
+ 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
+ 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();
+ SIX.lock(root);
+
+ 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
+ public void t1_root_SIX_t2_child_IX() throws InterruptedException, ExecutionException {
+ final MultiLock root = new MultiLock();
+ 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
+ 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();
+ SIX.lock(root);
+
+ final MultiLock child = new HierarchicalMultiLock(root);
+ 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();
+ 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
+ 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();
+ 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
+ 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();
+ 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
+ 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
+ public void t1_root_X_t2_child_IX() throws InterruptedException, ExecutionException {
+ final MultiLock root = new MultiLock();
+ 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
+ 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();
+ 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
+ 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();
+ 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
+ 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();
+ 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
+ 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);
+
+ 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 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);
+
+ 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 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);
+
+ for (final Future future : futures) {
+ assertTrue(future.isDone());
+ assertFalse(future.isCancelled());
+
+ assertTrue(future.get());
+ }
+ }
+
+ 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);
+
+ for (final Future future : futures) {
+ assertTrue(future.isDone());
+
+ assertTrue(future.isCancelled());
+ }
+ }
+
+ 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;
+ 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() {
+ rootLockMode.lock(root);
+ childLockMode.lock(child);
+ return true;
+ }
+ }
+
+ 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;
+
+ public ChildLockAcquirer(final MultiLock child, final LockMode lockMode) {
+ this.child = child;
+ this.lockMode = lockMode;
+ }
+
+ @Override
+ public Boolean call() {
+ lockMode.lock(child);
+ return true;
+ }
+ }
+
+ 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/Lockable.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/Lockable.java
new file mode 100644
index 0000000..9e39906
--- /dev/null
+++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/Lockable.java
@@ -0,0 +1,38 @@
+/**
+ * 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.locks.*;
+
+public abstract class Lockable {
+ 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/Locker.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/Locker.java
new file mode 100644
index 0000000..9c06bc4
--- /dev/null
+++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/Locker.java
@@ -0,0 +1,34 @@
+/**
+ * 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
+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
new file mode 100644
index 0000000..0c3fe9f
--- /dev/null
+++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/ReentrancyTest.java
@@ -0,0 +1,134 @@
+/**
+ * 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;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.*;
+
+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
+ * 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 RENTER_LOCK_ACQUISITIONS_TIMEOUT = LOCK_ACQUISITION_TIMEOUT * REENTER_COUNT;
+
+ @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; });
+ }
+
+ @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; });
+ }
+
+ @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; });
+ }
+
+ @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();
+
+ final ExecutorService executorService = Executors.newSingleThreadExecutor();
+ final List> futures = executorService.invokeAll(
+ Arrays.asList(new Reenter(multiLock, lockMode, lockFn, REENTER_COUNT)),
+ RENTER_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 Locker lockFn;
+ private 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;
+ }
+
+ @Override
+ public Integer call() throws InterruptedException {
+ int success = 0;
+ for (int i = 0; i < count; i++) {
+ 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
new file mode 100644
index 0000000..c3d894b
--- /dev/null
+++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/UpgradeTest.java
@@ -0,0 +1,157 @@
+/**
+ * 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;
+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 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.*;
+
+/**
+ * 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 {
+
+ 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; });
+ }
+
+ @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));
+ }
+
+ @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();
+
+ final ExecutorService executorService = Executors.newSingleThreadExecutor();
+ 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());
+ 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 final Locker lockFn;
+
+ 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 {
+ lockFn.lock(from, multiLock);
+ lockFn.lock(to, multiLock);
+ from.unlock(multiLock);
+ return true;
+ }
+ }
+}
diff --git a/test/bank/BankTest.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/bank/BankTest.java
similarity index 81%
rename from test/bank/BankTest.java
rename to src/test/java/uk/ac/ic/doc/slurp/multilock/bank/BankTest.java
index ee9b698..f6462a9 100644
--- a/test/bank/BankTest.java
+++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/bank/BankTest.java
@@ -1,36 +1,37 @@
-/*
- * 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 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 59%
rename from test/bank/BankTestMultiLock.java
rename to src/test/java/uk/ac/ic/doc/slurp/multilock/bank/BankTestMultiLock.java
index 6733a7a..d1d1427 100644
--- a/test/bank/BankTestMultiLock.java
+++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/bank/BankTestMultiLock.java
@@ -1,30 +1,32 @@
-/*
- * 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 test.bank;
+package uk.ac.ic.doc.slurp.multilock.bank;
import java.io.FileNotFoundException;
import java.util.Random;
@@ -35,16 +37,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 +55,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 +74,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/test/bank/BankTestRWLock.java b/src/test/java/uk/ac/ic/doc/slurp/multilock/bank/BankTestRWLock.java
similarity index 73%
rename from test/bank/BankTestRWLock.java
rename to src/test/java/uk/ac/ic/doc/slurp/multilock/bank/BankTestRWLock.java
index 4f2a989..59c5341 100644
--- a/test/bank/BankTestRWLock.java
+++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/bank/BankTestRWLock.java
@@ -1,30 +1,32 @@
-/*
- * 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 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 62%
rename from test/bank/BankTestSTM.java
rename to src/test/java/uk/ac/ic/doc/slurp/multilock/bank/BankTestSTM.java
index 00444d0..fcde445 100644
--- a/test/bank/BankTestSTM.java
+++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/bank/BankTestSTM.java
@@ -1,30 +1,32 @@
-/*
- * 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 test.bank;
+package uk.ac.ic.doc.slurp.multilock.bank;
import java.io.FileNotFoundException;
import java.util.Random;
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
new file mode 100644
index 0000000..55c3e5a
--- /dev/null
+++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/bank/Locker.java
@@ -0,0 +1,33 @@
+/**
+ * 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.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 59%
rename from test/counter/CounterTest.java
rename to src/test/java/uk/ac/ic/doc/slurp/multilock/counter/CounterTest.java
index 805608e..f13c963 100644
--- a/test/counter/CounterTest.java
+++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/counter/CounterTest.java
@@ -1,32 +1,34 @@
-/*
- * 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;
-package test.counter;
-
-import test.Lockable;
+import uk.ac.ic.doc.slurp.multilock.Lockable;
class Counter extends Lockable {
long value = 0;
@@ -76,8 +78,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/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
new file mode 100644
index 0000000..b820b83
--- /dev/null
+++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/counter/CounterTestMultiLock.java
@@ -0,0 +1,49 @@
+/**
+ * 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.counter;
+
+public class CounterTestMultiLock extends CounterTest {
+
+ @Override
+ public void inc(Counter c) {
+ c.mlock.writeLock();
+ c.inc();
+ c.mlock.unlockWrite();
+ }
+
+ /**
+ * @param args
+ * @throws InterruptedException
+ */
+ public static void main(String[] args) throws InterruptedException {
+ CounterTest c = new CounterTestMultiLock();
+ c.runExperiment();
+ }
+
+}
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
new file mode 100644
index 0000000..5939c01
--- /dev/null
+++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/counter/CounterTestRWLock.java
@@ -0,0 +1,49 @@
+/**
+ * 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.counter;
+
+public class CounterTestRWLock extends CounterTest {
+
+ @Override
+ public void inc(Counter c) {
+ c.wlock.lock();
+ c.inc();
+ c.wlock.unlock();
+ }
+
+ /**
+ * @param args
+ * @throws InterruptedException
+ */
+ public static void main(String[] args) throws InterruptedException {
+ CounterTest c = new CounterTestRWLock();
+ c.runExperiment();
+ }
+
+}
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
new file mode 100644
index 0000000..8792926
--- /dev/null
+++ b/src/test/java/uk/ac/ic/doc/slurp/multilock/counter/CounterTestSTM.java
@@ -0,0 +1,50 @@
+/**
+ * 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.counter;
+
+import org.deuce.Atomic;
+
+public class CounterTestSTM extends CounterTest {
+
+ @Override
+ @Atomic
+ public void inc(Counter c) {
+ c.inc();
+ }
+
+ /**
+ * @param args
+ * @throws InterruptedException
+ */
+ public static void main(String[] args) throws InterruptedException {
+ CounterTest c = new CounterTestSTM();
+ c.runExperiment();
+ }
+
+}
diff --git a/test/Lockable.java b/test/Lockable.java
deleted file mode 100644
index cf84305..0000000
--- a/test/Lockable.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (c) 2010-2016 Khilan Gudka
- * All rights reserved.
- *
- * 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 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.
- */
-
-package test;
-
-import java.util.concurrent.locks.*;
-
-import multilock.MultiLock;
-
-public abstract class Lockable {
- public MultiLock mlock = new MultiLock(null);
- public ReentrantReadWriteLock rwlock = new ReentrantReadWriteLock();
- public Lock rlock = rwlock.readLock();
- public Lock wlock = rwlock.writeLock();
-}
diff --git a/test/bank/Locker.java b/test/bank/Locker.java
deleted file mode 100644
index 1798049..0000000
--- a/test/bank/Locker.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (c) 2010-2016 Khilan Gudka
- * All rights reserved.
- *
- * 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 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.
- */
-
-package test.bank;
-
-public interface Locker {
-
-}
diff --git a/test/counter/CounterTestMultiLock.java b/test/counter/CounterTestMultiLock.java
deleted file mode 100644
index 7162da5..0000000
--- a/test/counter/CounterTestMultiLock.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (c) 2010-2016 Khilan Gudka
- * All rights reserved.
- *
- * 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 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.
- */
-
-package test.counter;
-
-public class CounterTestMultiLock extends CounterTest {
-
- @Override
- public void inc(Counter c) {
- c.mlock.lockWrite();
- c.inc();
- c.mlock.unlockWrite();
- }
-
- /**
- * @param args
- * @throws InterruptedException
- */
- public static void main(String[] args) throws InterruptedException {
- CounterTest c = new CounterTestMultiLock();
- c.runExperiment();
- }
-
-}
diff --git a/test/counter/CounterTestRWLock.java b/test/counter/CounterTestRWLock.java
deleted file mode 100644
index b3de308..0000000
--- a/test/counter/CounterTestRWLock.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (c) 2010-2016 Khilan Gudka
- * All rights reserved.
- *
- * 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 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.
- */
-
-package test.counter;
-
-public class CounterTestRWLock extends CounterTest {
-
- @Override
- public void inc(Counter c) {
- c.wlock.lock();
- c.inc();
- c.wlock.unlock();
- }
-
- /**
- * @param args
- * @throws InterruptedException
- */
- public static void main(String[] args) throws InterruptedException {
- CounterTest c = new CounterTestRWLock();
- c.runExperiment();
- }
-
-}
diff --git a/test/counter/CounterTestSTM.java b/test/counter/CounterTestSTM.java
deleted file mode 100644
index c94545a..0000000
--- a/test/counter/CounterTestSTM.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (c) 2010-2016 Khilan Gudka
- * All rights reserved.
- *
- * 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 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.
- */
-
-package test.counter;
-
-import org.deuce.Atomic;
-
-public class CounterTestSTM extends CounterTest {
-
- @Override
- @Atomic
- public void inc(Counter c) {
- c.inc();
- }
-
- /**
- * @param args
- * @throws InterruptedException
- */
- public static void main(String[] args) throws InterruptedException {
- CounterTest c = new CounterTestSTM();
- c.runExperiment();
- }
-
-}