Skip to content

Commit

Permalink
synchronised the internal writeLock(..) to readLock(..) lock semantics
Browse files Browse the repository at this point in the history
  • Loading branch information
RalphSteinhagen committed Feb 18, 2021
1 parent de48a77 commit 467b35e
Showing 1 changed file with 21 additions and 29 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package de.gsi.dataset.locks;

import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.StampedLock;
import java.util.function.Supplier;

Expand All @@ -18,7 +17,7 @@
* [..] some other code [..]
* lock.writeUnLock(); // restores isAutoNotification state
* </pre>
*
* <p>
* However, the recommended usage is using the lock guard primitives, e.g.
*
* <pre>
Expand All @@ -27,7 +26,7 @@
* return retVal; // N.B. optional return - here: assumes Objects or boxed primitives
* });
* </pre>
*
* <p>
* Alternatively the best performing option for frequent simple reads without major data processing
*
* <pre>
Expand All @@ -36,16 +35,17 @@
* return retVal; // N.B. optional return - here: assumes Objects or boxed primitives
* });
* </pre>
*
* <p>
* The latter assumes infrequent writes (e.g. a single writer thread) and frequent unobstructed reads (ie. many reader
* threads). The lock internally acquires the data w/o explicitly locking, checks afterwards if the data has potentially
* changed a write-lock acquiring thread, and as a automatic fall-back uses the guaranteed (but more expensive) read
* lock to assure that the read data structure is consistent.
*
* @author rstein
* @param <D> generics reference, usually to <code>&lt;? extends DataSet&gt;</code>
* @author rstein
*/
@SuppressWarnings({ "PMD.DoNotUseThreads", "PMD.CommentSize", "PMD.TooManyMethods" }) // Runnable used as functional interface
@SuppressWarnings({ "PMD.DoNotUseThreads", "PMD.CommentSize", "PMD.TooManyMethods" })
// Runnable used as functional interface
public class DefaultDataSetLock<D extends DataSet> implements DataSetLock<D> {
private static final long serialVersionUID = 1L;
private final transient StampedLock stampedLock = new StampedLock();
Expand All @@ -54,7 +54,8 @@ public class DefaultDataSetLock<D extends DataSet> implements DataSetLock<D> {
private transient Thread writeLockedByThread; // NOPMD
private final transient Object readerCountLock = new Object();
private int readerCount;
private final transient AtomicInteger writerCount = new AtomicInteger(0);
private final transient Object writerCountLock = new Object();
private int writerCount;
private final transient AtomicBoolean autoNotifyState = new AtomicBoolean(true);
private final transient D dataSet;

Expand Down Expand Up @@ -87,9 +88,9 @@ public D downGradeWriteLock() {
throw new IllegalStateException("cannot downconvert lock - tryConvertToReadLock return '0'");
}
synchronized (readerCountLock) {
synchronized (stampedLock) {
synchronized (writerCountLock) {
readerCount++;
this.writerCount.getAndDecrement();
writerCount--;
if ((lastReadStamp == 0) && stampedLock.isReadLocked() && (getReaderCount() > 1)) {
stampedLock.unlockRead(lastReadStamp);
}
Expand All @@ -100,20 +101,6 @@ public D downGradeWriteLock() {
return dataSet;
}

/**
* @return the last stored auto-notification state
*/
public boolean getLastStoredAutoNotificationState() { // NOPMD
return autoNotifyState.get();
}

/**
* @return the internal StampedLock object
*/
public StampedLock getLockObject() {
return stampedLock;
}

/**
* @return number of readers presently locked on this data set
*/
Expand All @@ -127,7 +114,9 @@ public int getReaderCount() {
* @return number of writers presently locked on this data set (N.B. all from the same thread)
*/
public int getWriterCount() {
return writerCount.get();
synchronized (writerCountLock) {
return writerCount;
}
}

@Override
Expand Down Expand Up @@ -226,7 +215,9 @@ public D writeLock() {
autoNotifyState.set(dataSet.autoNotification().getAndSet(false));
}
}
writerCount.incrementAndGet();
synchronized (writerCountLock) {
writerCount++;
}
return dataSet;
}

Expand Down Expand Up @@ -261,17 +252,18 @@ public <R> R writeLockGuard(final Supplier<R> writing) {

@Override
public D writeUnLock() {
if (writerCount.decrementAndGet() == 0) {
synchronized (stampedLock) {
synchronized (writerCountLock) {
writerCount--;
if (writerCount == 0) {
final long temp = lastWriteStamp;
lastWriteStamp = 0;
// restore present auto-notify state
dataSet.autoNotification().set(autoNotifyState.get());
writeLockedByThread = null; // NOPMD
stampedLock.unlockWrite(temp);
} else if (writerCount < 0) {
throw new IllegalStateException("write lock already unlocked");
}
} else if (writerCount.get() < 0) {
throw new IllegalStateException("write lock alread unlocked");
}
return dataSet;
}
Expand Down

0 comments on commit 467b35e

Please sign in to comment.