diff --git a/src/main/java/net/openhft/chronicle/map/locks/ChronicleStampedLock.java b/src/main/java/net/openhft/chronicle/map/locks/ChronicleStampedLock.java
new file mode 100644
index 000000000..39cb78bfd
--- /dev/null
+++ b/src/main/java/net/openhft/chronicle/map/locks/ChronicleStampedLock.java
@@ -0,0 +1,518 @@
+package net.openhft.chronicle.map.locks;
+
+import net.openhft.chronicle.core.values.LongValue;
+import net.openhft.chronicle.map.ChronicleMap;
+import net.openhft.chronicle.map.ChronicleMapBuilder;
+import net.openhft.chronicle.values.Values;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.concurrent.locks.StampedLock;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import static net.openhft.chronicle.values.Values.newNativeReference;
+
+/**
+ * ben.cotton@rutgers.edu
+ *
+ * A totally hacked impl, awaiting OpenHFT's official ChronicleStampedLock API
+ *
+ * A usable 'reference' impl will more properly belong in the Chronicle-Algorithms/ repo
+ *
+ * NOTE: This impl only provides off-Heap capability for the tryXXXXX() family of
+ * j.u.c.l.StampedLock methosds.
+ */
+public class ChronicleStampedLock extends StampedLock {
+
+ private static final Logger lg = Logger.getLogger(
+ "net.openhft.chronicle.map.fromdocs.acid.revelations.ChonicleStampedLock"
+ );
+ ChronicleMap chm; //custody of StampedLock semantics
+ ChronicleMap chmR; //Chronicle AtomicLong re: Reader set custody
+ ChronicleMap chmW; //Chronicle AtomicLong re: Reader set custody
+ ChronicleStampedLockVOInterface offHeapLock =
+ newNativeReference(
+ ChronicleStampedLockVOInterface.class
+ ); //the off-heap ChronicleStampedLock
+ ChronicleStampedLockVOInterface lastWriterT =
+ newNativeReference(
+ ChronicleStampedLockVOInterface.class
+ ); //needed to facilitate validate(stamp)
+ LongValue readLockHolderCount = Values.newNativeReference(
+ LongValue.class
+ ); //ReaderSet cardinality
+ LongValue writeLockHolderCount = Values.newNativeReference(
+ LongValue.class
+ ); //WriterSet cardinality
+
+ ChronicleStampedLock(String chronicelStampedLockLocality) { // path of Operand set i.e. /dev/shm/
+ try {
+ chm = offHeapLock(chronicelStampedLockLocality);
+ chmR = offHeapLockReaderCount(chronicelStampedLockLocality + "=ReaderCount");
+ chmW = offHeapLockReaderCount(chronicelStampedLockLocality + "=WriterCount");
+ chm.acquireUsing("Stamp ", offHeapLock); // K="Stamp ", V=OffHeapLock impl
+ chm.acquireUsing("LastWriterTime ", lastWriterT); //needed for validate
+ chmR.acquireUsing("ReaderCount ", readLockHolderCount);
+ chmW.acquireUsing("WriterCount ", readLockHolderCount);
+ lg.log(Level.FINE,
+ " ,@t=" + System.currentTimeMillis() +
+ " ChronicleStampedLock constructed" +
+ ","
+ );
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ static ChronicleMap offHeapLock(String operand)
+ throws IOException {
+
+ return ChronicleMapBuilder.of(
+ String.class,
+ ChronicleStampedLockVOInterface.class)
+ .entries(16)
+ .averageKeySize("123456789".length())
+ .createPersistedTo(
+ new File(
+ operand
+ )
+ );
+ }
+
+ static ChronicleMap offHeapLockReaderCount(String operand)
+ throws IOException {
+
+ return ChronicleMapBuilder.of(String.class, LongValue.class)
+ .entries(16)
+ .averageKeySize("123456789".length())
+ .createPersistedTo(
+ new File(
+ operand
+ )
+ );
+ }
+
+ public void closeChronicle() {
+ //chm.close();
+ //chmR.close();
+ //chmW.close();
+ }
+
+ @Override
+ public long tryOptimisticRead() {
+ (offHeapLock = chm.get("Stamp ")).setEntryLockState(0L);
+ long t = System.currentTimeMillis();
+ chm.put("Stamp ", offHeapLock);
+ lg.log(Level.FINE,
+ " ,@t=" + System.currentTimeMillis() +
+ " ChronicleStampedLock saw stamp=[" + 0L + "]" +
+ " tryOptmisticRead() returning stamp=" +
+ t +
+ ","
+ );
+ return (t);
+ }
+
+ @Override
+ public boolean validate(long stamp) {
+ offHeapLock = chm.get("Stamp ");
+ lastWriterT = chm.get("LastWriterTime ");
+ boolean ret = false;
+ /**
+ * If *any* Writer interacted with the offHeapLock,
+ * since event=tryOptimisticRead(),
+ * then FAIL the validate() invoke.
+ *
+ * Upon FAILURE, the thread on tryOptimisticRead() must apply
+ * its PESSIMISTIC policy (Thread
+ * has endured a DIRTY_READ.)
+ *
+ */
+ if (lastWriterT.getEntryLockState() > stamp || offHeapLock.getEntryLockState() < 0L) {
+ ret = Boolean.FALSE;
+ } else {
+ ret = Boolean.TRUE;
+ }
+ lg.log(Level.FINE,
+ " ,@t=" + System.currentTimeMillis() +
+ " ChronicleStampedLock validate(" +
+ stamp + ") returned =[" +
+ ret + "] " +
+ ","
+ );
+ lg.log(Level.FINE,
+ " ,@t=" + System.currentTimeMillis() +
+ " ChronicleStampedLock LastWriterT=[" +
+ lastWriterT.getEntryLockState() + "] " +
+ ","
+ );
+ return ret;
+ }
+
+ @Override
+ public long tryConvertToReadLock(long stamp) {
+
+ try {
+ throw new Exception("not supported in ChronicleStampedLock's reference impl.");
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return (0L);
+
+ }
+
+ @Override
+ public long tryConvertToWriteLock(long stamp) {
+ try {
+ throw new Exception("not supported in ChronicleStampedLock's reference impl.");
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return (0L);
+ }
+
+ @Override
+ public long tryWriteLock() {
+ long l = 0L;
+
+ offHeapLock = chm.get("Stamp ");
+ lastWriterT = chm.get("LastWriterTime ");
+ writeLockHolderCount = chmW.get("WriterCount ");
+
+ l = offHeapLock.getEntryLockState();
+
+ if (l != 0L)
+ return 0L;
+
+ do {
+ lg.log(Level.FINE,
+ " ,@t=" + System.currentTimeMillis() +
+ " ChronicleStampedLock tryWriteLock() ?WAITING " +
+ " on offHeapLock.unlock(" +
+ offHeapLock.getEntryLockState() +
+ ") " +
+ " readerCount=[" +
+ (readLockHolderCount =
+ chmR.get("ReaderCount ")).getVolatileValue() +
+ "] " +
+ " writerCount=[" +
+ (writeLockHolderCount =
+ chmW.get("WriterCount ")).getVolatileValue() +
+ "] " +
+ ","
+ );
+ offHeapLock = chm.get("Stamp ");
+ l = offHeapLock.getEntryLockState();
+ try {
+ Thread.sleep((long) (1000 * Math.random()));
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ } while (
+ readLockHolderCount.getVolatileValue() > 0 ||
+ (writeLockHolderCount =
+ chmW.get("WriterCount "))
+ .getVolatileValue() > 0
+ );
+
+ writeLockHolderCount.addAtomicValue(+1);
+ chmW.put("WriterCount ", writeLockHolderCount);
+ lg.log(Level.FINE,
+ " ,@t=" + System.currentTimeMillis() +
+ " ChronicleStampedLock trywriteLock() ++ writeSeekers=[" +
+ +writeLockHolderCount.getVolatileValue() + "] " +
+ "..,"
+ );
+ lg.log(Level.FINE,
+ " ,@t=" + System.currentTimeMillis() +
+ " ChronicleStampedLock tryWriteLock() PROCEEDING " +
+ " ,"
+ );
+ long t = System.currentTimeMillis();
+ offHeapLock.setEntryLockState(-t); //negative ==> Writer holds StampedLock
+ lastWriterT.setEntryLockState(t);
+ chm.put("Stamp ", offHeapLock);
+ chm.put("LastWriterTime ", lastWriterT);
+ lg.log(Level.FINE,
+ " ,@t=" + t +
+ " ChronicleStampedLock tryWriteLock() returned stamp=" +
+ offHeapLock.getEntryLockState() +
+ ","
+ );
+ return (offHeapLock.getEntryLockState());
+ }
+
+ @Override
+ public long tryReadLock() {
+ long l = 0L;
+
+ offHeapLock = chm.get("Stamp ");
+ readLockHolderCount = chmR.get("ReaderCount ");
+
+ l = (offHeapLock = chm.get("Stamp ")).getEntryLockState();
+ if (l < 0L)
+ return (0);
+ do {
+ lg.log(Level.FINE,
+ " ,@t=" + System.currentTimeMillis() +
+ " ChronicleStampedLock tryReadLock() ?WAITING " +
+ " on offHeapLock.unlock(" +
+ offHeapLock.getEntryLockState() +
+ ") " +
+ " readerCount=[" + readLockHolderCount.getVolatileValue() + "] " +
+ " writerCount=[" +
+ (writeLockHolderCount = chmW.get("WriterCount ")).getVolatileValue() +
+ "] " +
+ ","
+ );
+ l = (offHeapLock = chm.get("Stamp ")).getEntryLockState();
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ } while (l < 0L);
+
+ lg.log(Level.FINE,
+ " ,@t=" + System.currentTimeMillis() +
+ " ChronicleStampedLock tryReadLock() PROCEEDING " +
+ //" readerCount=[" + readerCount.getReaderCount() + "]" +
+ " readerCount=[" +
+ (readLockHolderCount = chmR.get("ReaderCount ")).getVolatileValue() + "]" +
+ " BEFORE addAtomic(1) " +
+ ","
+ );
+
+ readLockHolderCount.addAtomicValue(1); // INCREMENT the cardinality of the Reader set
+ chmR.put("ReaderCount ", readLockHolderCount); // and make it IPC visible
+
+ offHeapLock.setEntryLockState(
+ readLockHolderCount.getVolatileValue()
+ ); // assign the Lock to most-recent Reader
+
+ chm.put("Stamp ", offHeapLock); // make it IPC visible
+ lg.log(Level.FINE,
+ " ,@t=" + System.currentTimeMillis() +
+ " ChronicleStampedLock tryReadLock() returned stamp=" +
+ offHeapLock.getEntryLockState() +
+ " readerCount=[" + readLockHolderCount.getVolatileValue() + "]" +
+ " AFTER addAtomic(1) " +
+ ","
+ );
+ return (offHeapLock.getEntryLockState());
+ }
+
+ @Override
+ public long writeLock() {
+ long l = 0L;
+
+ offHeapLock = chm.get("Stamp ");
+ lastWriterT = chm.get("LastWriterTime ");
+ writeLockHolderCount = chmW.get("WriterCount ");
+
+ do {
+ lg.log(Level.FINE,
+ " ,@t=" + System.currentTimeMillis() +
+ " ChronicleStampedLock writeLock() ?WAITING " +
+ " on offHeapLock.unlock(" +
+ offHeapLock.getEntryLockState() +
+ ") " +
+ " readerCount=[" +
+ (readLockHolderCount =
+ chmR.get("ReaderCount ")).getVolatileValue() +
+ "] " +
+ " writerCount=[" +
+ (writeLockHolderCount =
+ chmW.get("WriterCount ")).getVolatileValue() +
+ "] " +
+ ","
+ );
+ offHeapLock = chm.get("Stamp ");
+ l = offHeapLock.getEntryLockState();
+ try {
+ Thread.sleep((long) (1000 * Math.random()));
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ } while (
+ l != 0L ||
+ readLockHolderCount.getVolatileValue() > 0 ||
+ (writeLockHolderCount = chmW.get("WriterCount ")).getVolatileValue() > 0
+ );
+
+ writeLockHolderCount.addAtomicValue(+1);
+ chmW.put("WriterCount ", writeLockHolderCount);
+ lg.log(Level.FINE,
+ " ,@t=" + System.currentTimeMillis() +
+ " ChronicleStampedLock writeLock() ++ writeSeekers=[" +
+ +writeLockHolderCount.getVolatileValue() + "] " +
+ "..,"
+ );
+
+ lg.log(Level.FINE,
+ " ,@t=" + System.currentTimeMillis() +
+ " ChronicleStampedLock writeLock() PROCEEDING " +
+ " ,"
+ );
+ long t = System.currentTimeMillis();
+ offHeapLock.setEntryLockState(-t); //negative ==> Writer holds StampedLock
+ lastWriterT.setEntryLockState(t);
+ chm.put("Stamp ", offHeapLock);
+ chm.put("LastWriterTime ", lastWriterT);
+ lg.log(Level.FINE,
+ " ,@t=" + t +
+ " ChronicleStampedLock writeLock() returned stamp=" +
+ offHeapLock.getEntryLockState() +
+ ","
+ );
+ return (offHeapLock.getEntryLockState());
+ }
+
+ @Override
+ public long readLock() {
+ long l = 0L;
+
+ offHeapLock = chm.get("Stamp ");
+ readLockHolderCount = chmR.get("ReaderCount ");
+
+ do {
+ lg.log(Level.FINE,
+ " ,@t=" + System.currentTimeMillis() +
+ " ChronicleStampedLock readLock() ?WAITING " +
+ " on offHeapLock.unlock(" +
+ offHeapLock.getEntryLockState() +
+ ") " +
+ " readerCount=[" + readLockHolderCount.getVolatileValue() + "] " +
+ " writerCount=[" +
+ (writeLockHolderCount = chmW.get("WriterCount ")).getVolatileValue() +
+ "] " +
+ ","
+ );
+ l = (offHeapLock = chm.get("Stamp ")).getEntryLockState();
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ } while (l < 0L);
+
+ lg.log(Level.FINE,
+ " ,@t=" + System.currentTimeMillis() +
+ " ChronicleStampedLock readLock() PROCEEDING " +
+ //" readerCount=[" + Count.getReaderCount() + "]" +
+ " readerCount=[" +
+ (readLockHolderCount = chmR.get("ReaderCount ")).getVolatileValue() + "]" +
+ " BEFORE addAtomic(1) " +
+ ","
+ );
+
+ readLockHolderCount.addAtomicValue(1); // INCREMENT the cardinality of the Reader set
+ chmR.put("ReaderCount ", readLockHolderCount); // and make it IPC visible
+
+ offHeapLock.setEntryLockState(
+ readLockHolderCount.getVolatileValue()
+ ); // assign the Lock to most-recent Reader
+
+ chm.put("Stamp ", offHeapLock); // make it IPC visible
+
+ lg.log(Level.FINE,
+ " ,@t=" + System.currentTimeMillis() +
+ " ChronicleStampedLock readLock() returned stamp=" +
+ offHeapLock.getEntryLockState() +
+ " readerCount=[" + readLockHolderCount.getVolatileValue() + "]" +
+ " AFTER addAtomic(1) " +
+ ","
+ );
+ return (offHeapLock.getEntryLockState());
+ }
+
+ @Override
+ public void unlock(long stamp) {
+ if (stamp < 0L) {
+ unlockWrite(stamp);
+ } else if (stamp > 0L) {
+ unlockRead(stamp);
+ } else {
+ // lock available
+ }
+ }
+
+ @Override
+ public void unlockRead(long stamp) {
+ offHeapLock = chm.get("Stamp ");
+ readLockHolderCount = chmR.get("ReaderCount ");
+
+ lg.log(Level.FINE,
+ " ,@t=" + System.currentTimeMillis() +
+ " ChronicleStampedLock unlockRead(" + stamp + ") unlocking.." +
+ "ReaderCount=[" +
+ (readLockHolderCount = chmR.get("ReaderCount ")).getVolatileValue() + "]" +
+ " BEFORE addAtomic(-1) " +
+ ","
+ );
+ readLockHolderCount.addAtomicValue(-1); // DECREMENT the cardinality of the Reader set
+ chmR.put("ReaderCount ", readLockHolderCount); // make it IPC visible
+ lg.log(Level.FINE,
+ " ,@t=" + System.currentTimeMillis() +
+ " ChronicleStampedLock unlockRead(" + stamp + ") unlocking.." +
+ "ReaderCount=[" +
+ (readLockHolderCount = chmR.get("ReaderCount ")).getVolatileValue() + "]" +
+ " AFTER addAtomic(-1) " +
+ ","
+ );
+ offHeapLock.setEntryLockState(
+ (readLockHolderCount = chmR.get("ReaderCount ")).getVolatileValue()
+ );
+ lg.log(Level.FINE,
+ " ,@t=" + System.currentTimeMillis() +
+ "offHeapLock=[" + offHeapLock.getEntryLockState() + "]" +
+ ","
+ );
+ chm.put("Stamp ", offHeapLock);
+ chmR.put("ReaderCount ", readLockHolderCount);
+ }
+
+ @Override
+ public void unlockWrite(long stamp) {
+ lg.log(Level.FINE,
+ " ,@t=" + System.currentTimeMillis() +
+ " ChronicleStampedLock unlockWrite(" + stamp + ") unlocking..,"
+ );
+ offHeapLock = chm.get("Stamp ");
+ offHeapLock.setEntryLockState(0L);
+ chm.put("Stamp ", offHeapLock);
+
+ writeLockHolderCount = chmW.get("WriterCount ");
+ writeLockHolderCount.addAtomicValue(-1);
+ chmW.put("WriterCount ", writeLockHolderCount);
+ lg.log(Level.FINE,
+ " ,@t=" + System.currentTimeMillis() +
+ " ChronicleStampedLock unlockWrite(" + stamp + ") -- writeSeekers=[" +
+ +writeLockHolderCount.getVolatileValue() + "] " +
+ "..,"
+ );
+
+ lg.log(Level.FINE,
+ " ,@t=" + System.currentTimeMillis() +
+ " ChronicleStampedLock unlockWrite(" +
+ (offHeapLock = chm.get("Stamp ")).getEntryLockState() +
+ ") unlocked. set to Zero" +
+ ","
+ );
+ }
+
+ @Override
+ public int getReadLockCount() {
+
+ return ((int) chmR.get("ReaderCount ").getVolatileValue());
+ }
+
+ @Override
+ public boolean isReadLocked() {
+ if (this.getReadLockCount() > 0)
+ return (Boolean.TRUE);
+ else
+ return (Boolean.FALSE);
+ }
+}
diff --git a/src/main/java/net/openhft/chronicle/map/locks/ChronicleStampedLockVOInterface.java b/src/main/java/net/openhft/chronicle/map/locks/ChronicleStampedLockVOInterface.java
new file mode 100644
index 000000000..7c0564623
--- /dev/null
+++ b/src/main/java/net/openhft/chronicle/map/locks/ChronicleStampedLockVOInterface.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2012-2018 Chronicle Map Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.openhft.chronicle.map.locks;
+
+import net.openhft.chronicle.values.Group;
+
+interface ChronicleStampedLockVOInterface {
+
+ @Group(0)
+ long getEntryLockState();
+
+ void setEntryLockState(long entryLockState);
+//
+// @Group(1)
+// long getReaderCount();
+//
+// void setReaderCount(long rc); /* time in millis */
+//
+// long addAtomicReaderCount(long toAdd);
+//
+
+}
diff --git a/src/test/java/net/openhft/chronicle/map/locks/BondVOInterface.java b/src/test/java/net/openhft/chronicle/map/locks/BondVOInterface.java
new file mode 100644
index 000000000..f6cdc9f5e
--- /dev/null
+++ b/src/test/java/net/openhft/chronicle/map/locks/BondVOInterface.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2012-2018 Chronicle Map Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.openhft.chronicle.map.locks;
+
+import net.openhft.chronicle.values.Array;
+import net.openhft.chronicle.values.Group;
+import net.openhft.chronicle.values.MaxUtf8Length;
+
+public interface BondVOInterface {
+
+ @Group(0)
+ long getEntryLockState();
+
+ void setEntryLockState(long entryLockState);
+
+ @Group(1)
+ long getIssueDate();
+
+ void setIssueDate(long issueDate); /* time in millis */
+
+ @Group(1)
+ long getMaturityDate();
+
+ void setMaturityDate(long maturityDate); /* time in millis */
+
+ long addAtomicMaturityDate(long toAdd);
+
+ @Group(1)
+ boolean compareAndSwapCoupon(double expected, double value);
+
+ double getCoupon();
+
+ void setCoupon(double coupon);
+
+ double addAtomicCoupon(double toAdd);
+
+ @Group(1)
+ String getSymbol();
+
+ void setSymbol(@MaxUtf8Length(20) String symbol);
+
+ // OpenHFT Off-Heap array[ ] processing notice ‘At’ suffix
+ @Group(1)
+ @Array(length = 7)
+ void setMarketPxIntraDayHistoryAt(int tradingDayHour, MarketPx mPx);
+
+ /* 7 Hours in the Trading Day:
+ * index_0 = 9.30am,
+ * index_1 = 10.30am,
+ …,
+ * index_6 = 4.30pm
+ */
+
+ MarketPx getMarketPxIntraDayHistoryAt(int tradingDayHour);
+
+ /* nested interface - empowering an Off-Heap hierarchical “TIER of prices”
+ as array[ ] value */
+ interface MarketPx {
+ double getCallPx();
+
+ void setCallPx(double px);
+
+ double getParPx();
+
+ void setParPx(double px);
+
+ double getMaturityPx();
+
+ void setMaturityPx(double px);
+
+ double getBidPx();
+
+ void setBidPx(double px);
+
+ double getAskPx();
+
+ void setAskPx(double px);
+ }
+}
diff --git a/src/test/java/net/openhft/chronicle/map/locks/DirtyReadIntolerant.java b/src/test/java/net/openhft/chronicle/map/locks/DirtyReadIntolerant.java
new file mode 100644
index 000000000..9aa1a9f44
--- /dev/null
+++ b/src/test/java/net/openhft/chronicle/map/locks/DirtyReadIntolerant.java
@@ -0,0 +1,99 @@
+package net.openhft.chronicle.map.locks;
+
+import net.openhft.chronicle.map.ChronicleMap;
+
+import java.util.concurrent.locks.StampedLock;
+
+import static net.openhft.chronicle.values.Values.newNativeReference;
+
+public class DirtyReadIntolerant {
+ public static void main(String[] args) {
+ try {
+ String isoLevel = args[0];
+ long sleepMock = Long.parseLong(args[1]);
+ long holdTime = Long.parseLong(args[2]);
+ /**
+ * ben.cotton@rutgers.edu START
+ */
+ ChronicleMap chm =
+ DirtyReadTolerance.offHeap(
+ args[3]
+ + "OPERAND_CHRONICLE_MAP"
+ );
+ Double coupon = 0.00;
+ BondVOInterface bond = newNativeReference(BondVOInterface.class);
+ //BondVOInterface cslMock = newNativeReference(BondVOInterface.class); //mock'd
+ long stamp = 0;
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadIntolerant ENTERING offHeapLock.readLock()"
+ );
+ StampedLock offHeapLock =
+ new ChronicleStampedLock(
+ args[3]
+ + "OPERAND_ChronicleStampedLock"
+ );
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadIntolerant sleeping " + sleepMock + " seconds"
+ );
+ Thread.sleep(sleepMock * 1_000);
+ while ((stamp = offHeapLock.readLock()) < 0) {
+ }
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadIntolerant ENTERED offHeapLock.readLock() " +
+ " stamp=[" +
+ stamp +
+ "]"
+ );
+ try {
+ chm.acquireUsing("369604101", bond);
+ //chm.acquireUsing("Offender ", cslMock); //mock'd
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadIntolerant calling chm.get('369604101').getCoupon()"
+ );
+ bond = chm.get("369604101");
+ coupon = bond.getCoupon();
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadIntolerant coupon=[" + coupon + "] read."
+ );
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadIntolerant sleeping " + holdTime + " seconds"
+ );
+
+ Thread.sleep(holdTime * 1_000);
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadIntolerant awakening"
+ );
+
+ } finally {
+ offHeapLock.unlockRead(stamp);
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadIntolerant offHeapLock.unlockRead(" +
+ stamp + ") completed."
+ );
+
+ }
+ /**
+ * ben.cotton@rutgers.edu END
+ */
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadIntolerant got() coupon=" +
+ coupon + " "
+ );
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadIntolerant COMMITTED"
+ );
+ } catch (Exception throwables) {
+ throwables.printStackTrace();
+ }
+ }
+}
diff --git a/src/test/java/net/openhft/chronicle/map/locks/DirtyReadIntolerant_ReaderReader_Test.java b/src/test/java/net/openhft/chronicle/map/locks/DirtyReadIntolerant_ReaderReader_Test.java
new file mode 100644
index 000000000..b78f5007a
--- /dev/null
+++ b/src/test/java/net/openhft/chronicle/map/locks/DirtyReadIntolerant_ReaderReader_Test.java
@@ -0,0 +1,120 @@
+package net.openhft.chronicle.map.locks;
+
+import net.openhft.chronicle.core.OS;
+import net.openhft.chronicle.map.ChronicleMap;
+import org.junit.Assert;
+import org.junit.Test;
+
+import static net.openhft.chronicle.values.Values.newNativeReference;
+
+/**
+ * This Test efforts to ensure that a READERS-only set of requests to access the CSL
+ * is always granted (i.e. csl.tryReadLock() ALWAYS returns true)
+ */
+
+public class DirtyReadIntolerant_ReaderReader_Test {
+
+ @Test
+ public void main() {
+ try {
+ long sleepMock = Long.parseLong("5");
+ long holdTime = Long.parseLong("25");
+
+ Thread tooThread = new Thread(new ReaderToo());
+ tooThread.start();
+
+ ChronicleMap chm =
+ DirtyReadTolerance.offHeap(
+ OS.TARGET + "/shm-OPERAND_CHRONICLE_MAP"
+ );
+ Double coupon = 0.00;
+ BondVOInterface bond = newNativeReference(BondVOInterface.class);
+ //BondVOInterface cslMock = newNativeReference(BondVOInterface.class); //mock'd
+ long stamp = 0;
+ System.out.println(
+ " " +
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadIntolerant ENTERING offHeapLock.readLock()"
+ );
+ ChronicleStampedLock offHeapLock =
+ new ChronicleStampedLock(
+ OS.TARGET + "/shm-"
+ + "OPERAND_ChronicleStampedLock"
+ );
+ System.out.println(
+ " " +
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadIntolerant sleeping " + sleepMock + " seconds"
+ );
+ Thread.sleep(sleepMock * 1_000);
+ while ((stamp = offHeapLock.tryReadLock()) < 0) {
+ Assert.assertEquals(Boolean.TRUE, false); // we failed!
+ }
+ Assert.assertEquals(Boolean.TRUE, true); // we passed!
+ System.out.println(
+ " " +
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadIntolerant ENTERED offHeapLock.readLock() " +
+ " stamp=[" +
+ stamp +
+ "]"
+ );
+ try {
+ chm.acquireUsing("369604101", bond);
+ //chm.acquireUsing("Offender ", cslMock); //mock'd
+ System.out.println(
+ " " +
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadIntolerant calling chm.get('369604101').getCoupon()"
+ );
+ bond = (BondVOInterface) chm.get("369604101");
+ coupon = bond.getCoupon();
+ System.out.println(
+ " " +
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadIntolerant coupon=[" + coupon + "] read."
+ );
+ System.out.println(
+ " " +
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadIntolerant sleeping " + holdTime + " seconds"
+ );
+
+ Thread.sleep(holdTime * 1_000);
+ System.out.println(
+ " " +
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadIntolerant awakening"
+ );
+
+ } finally {
+ offHeapLock.unlockRead(stamp);
+ System.out.println(
+ " " +
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadIntolerant offHeapLock.unlockRead(" +
+ stamp + ") completed."
+ );
+
+ }
+ /**
+ * ben.cotton@rutgers.edu END
+ */
+ System.out.println(
+ " " +
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadIntolerant got() coupon=" +
+ coupon + " "
+ );
+ System.out.println(
+ " " +
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadIntolerant COMMITTED"
+ );
+ chm.close();
+ offHeapLock.closeChronicle();
+ } catch (Exception throwables) {
+ throwables.printStackTrace();
+ }
+ }
+}
diff --git a/src/test/java/net/openhft/chronicle/map/locks/DirtyReadOffender.java b/src/test/java/net/openhft/chronicle/map/locks/DirtyReadOffender.java
new file mode 100644
index 000000000..ca9f08ef8
--- /dev/null
+++ b/src/test/java/net/openhft/chronicle/map/locks/DirtyReadOffender.java
@@ -0,0 +1,112 @@
+package net.openhft.chronicle.map.locks;
+
+import net.openhft.chronicle.map.ChronicleMap;
+
+import java.util.Scanner;
+import java.util.concurrent.locks.StampedLock;
+
+import static net.openhft.chronicle.values.Values.newNativeReference;
+
+//import net.openhft.chronicle.map.fromdocs.BondVOInterface;
+
+
+public class DirtyReadOffender {
+
+ public static void main(String[] args) {
+ Scanner sc = new Scanner(System.in);
+
+ try {
+ String isoLevel = args[0];
+ long sleepT = Long.parseLong(args[1]);
+ long holdTime = Long.parseLong(args[2]);
+
+ ChronicleMap chm =
+ DirtyReadTolerance.offHeap(
+ args[3]
+ + "OPERAND_CHRONICLE_MAP"
+ );
+ System.out.println(
+ " @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender established chm "
+ );
+ StampedLock offHeapLock = new ChronicleStampedLock(
+ args[3]
+ + "OPERAND_ChronicleStampedLock"
+ );
+ BondVOInterface bond = newNativeReference(BondVOInterface.class);
+ //BondVOInterface cslMock = newNativeReference(BondVOInterface.class);
+ chm.acquireUsing("369604101", bond);
+ //chm.acquireUsing("Offender ", cslMock); // mock ChronicleStampLock
+ System.out.println(
+ " @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender sleeping " + sleepT + " seconds "
+ );
+ Thread.sleep(sleepT * 1_000);
+ System.out.println(
+ " @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender awakening "
+ );
+ /**
+ * ben.cotton@rutgers.edu ... anticipate Chronicle (www.OpenHFT.net)
+ * providing a j.u.c.l.StampedLock API for off-heap enthusiasts
+ *
+ * START
+ *
+ */
+ long stamp = 0;
+ System.out.println(
+ " @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender ACQUIRING offHeapLock.writeLock();"
+ );
+ while ((stamp = offHeapLock.writeLock()) == 0) {
+ }
+ System.out.println(
+ " @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender ACQUIRED offHeapLock.writeLock();"
+ );
+ try {
+ double newCoupon = 3.5 + Math.random();
+ System.out.println(
+ " @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender " +
+ " calling chm.put('369604101'," + newCoupon + ") "
+ );
+ bond.setCoupon(newCoupon);
+ chm.put("369604101", bond);
+ //cslMock.setEntryLockState(System.currentTimeMillis()); //mock'd
+ // chm.put("Offender ",cslMock); //mock'd
+ System.out.println(
+ " @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender coupon=[" +
+ bond.getCoupon() +
+ "] written. "
+ );
+ } finally {
+ System.out.println(
+ " @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender sleeping " + holdTime + " seconds "
+ );
+ Thread.sleep(holdTime * 1_000);
+ offHeapLock.unlockWrite(stamp);
+ System.out.println(
+ " @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender called " +
+ "offHeapLock.unlockWrite(" + stamp + ");"
+ );
+ }
+ /**
+ * ben.cotton@rutgers.edu
+ *
+ * END
+ *
+ */
+ } catch (Exception throwables) {
+ throwables.printStackTrace();
+ } finally {
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadOffender COMMITTED"
+ );
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/net/openhft/chronicle/map/locks/DirtyReadOffenderIPCTest.java b/src/test/java/net/openhft/chronicle/map/locks/DirtyReadOffenderIPCTest.java
new file mode 100644
index 000000000..c90f3d493
--- /dev/null
+++ b/src/test/java/net/openhft/chronicle/map/locks/DirtyReadOffenderIPCTest.java
@@ -0,0 +1,118 @@
+package net.openhft.chronicle.map.locks;
+
+import net.openhft.chronicle.core.OS;
+import net.openhft.chronicle.map.ChronicleMap;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.concurrent.locks.StampedLock;
+
+import static net.openhft.chronicle.values.Values.newNativeReference;
+
+public class DirtyReadOffenderIPCTest implements Runnable {
+ @Before
+ public void setUp() throws Exception {
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ }
+
+ @Test
+ public void run() {
+
+ try {
+ long sleepT = Long.parseLong("5");
+ long holdTime = Long.parseLong("10");
+
+ ChronicleMap chm =
+ DirtyReadTolerance.offHeap(
+ OS.TARGET + "/shm-"
+ + "OPERAND_CHRONICLE_MAP"
+ );
+ System.out.println(
+ "..... @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender established chm "
+ );
+ StampedLock offHeapLock = new ChronicleStampedLock(
+ OS.TARGET + "/shm-"
+ + "OPERAND_ChronicleStampedLock"
+ );
+ BondVOInterface bond = newNativeReference(BondVOInterface.class);
+
+ chm.acquireUsing("369604101", bond);
+ System.out.println(
+ "..... @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender sleeping " + sleepT + " seconds "
+ );
+ Thread.sleep(sleepT * 1_000);
+ System.out.println(
+ "..... @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender awakening "
+ );
+ /**
+ * ben.cotton@rutgers.edu ... anticipate Chronicle (www.OpenHFT.net)
+ * providing a j.u.c.l.StampedLock API for off-heap enthusiasts
+ *
+ * START
+ *
+ */
+ long stamp = 0;
+ System.out.println(
+ "..... @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender ACQUIRING offHeapLock.writeLock();"
+ );
+ while ((stamp = offHeapLock.writeLock()) == 0) {
+ ;
+ }
+ System.out.println(
+ "..... @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender ACQUIRED offHeapLock.writeLock();"
+ );
+ try {
+ double newCoupon = 3.5 + Math.random();
+ System.out.println(
+ "..... @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender " +
+ " calling chm.put('369604101'," + newCoupon + ") "
+ );
+ bond.setCoupon(newCoupon);
+ chm.put("369604101", bond);
+ //cslMock.setEntryLockState(System.currentTimeMillis()); //mock'd
+ // chm.put("Offender ",cslMock); //mock'd
+ System.out.println(
+ "..... @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender coupon=[" +
+ bond.getCoupon() +
+ "] written. "
+ );
+ } finally {
+ System.out.println(
+ "..... @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender sleeping " + holdTime + " seconds "
+ );
+ Thread.sleep(holdTime * 1_000);
+ offHeapLock.unlockWrite(stamp);
+ System.out.println(
+ "..... @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender called " +
+ "offHeapLock.unlockWrite(" + stamp + ");"
+ );
+ }
+ /**
+ * ben.cotton@rutgers.edu
+ *
+ * END
+ *
+ */
+ } catch (Exception throwables) {
+ throwables.printStackTrace();
+ } finally {
+ System.out.println(
+ "..... @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender COMMITTED"
+ );
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/net/openhft/chronicle/map/locks/DirtyReadOffenderTest.java b/src/test/java/net/openhft/chronicle/map/locks/DirtyReadOffenderTest.java
new file mode 100644
index 000000000..9876020f0
--- /dev/null
+++ b/src/test/java/net/openhft/chronicle/map/locks/DirtyReadOffenderTest.java
@@ -0,0 +1,109 @@
+package net.openhft.chronicle.map.locks;
+
+import net.openhft.chronicle.core.OS;
+import net.openhft.chronicle.map.ChronicleMap;
+
+import java.util.concurrent.locks.StampedLock;
+
+import static net.openhft.chronicle.values.Values.newNativeReference;
+
+class DirtyReadOffenderTest implements Runnable {
+
+ public void run() {
+
+ try {
+ long sleepT = Long.parseLong("5");
+ long holdTime = Long.parseLong("10");
+
+ ChronicleMap chm =
+ DirtyReadTolerance.offHeap(
+ OS.TARGET + "/shm-"
+ + "OPERAND_CHRONICLE_MAP"
+ );
+ System.out.println(
+ "..... @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender established chm "
+ );
+ StampedLock offHeapLock = new ChronicleStampedLock(
+ OS.TARGET + "/shm-"
+ + "OPERAND_ChronicleStampedLock"
+ );
+ BondVOInterface bond = newNativeReference(BondVOInterface.class);
+
+ chm.acquireUsing("369604101", bond);
+ System.out.println(
+ "..... @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender sleeping " + sleepT + " seconds "
+ );
+ Thread.sleep(sleepT * 1_000);
+ System.out.println(
+ "..... @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender awakening "
+ );
+ /**
+ * ben.cotton@rutgers.edu ... anticipate Chronicle (www.OpenHFT.net)
+ * providing a j.u.c.l.StampedLock API for off-heap enthusiasts
+ *
+ * START
+ *
+ */
+ long stamp = 0;
+ System.out.println(
+ "..... @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender ACQUIRING offHeapLock.writeLock();"
+ );
+ while ((stamp = offHeapLock.writeLock()) == 0) {
+ ;
+ }
+ System.out.println(
+ "..... @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender ACQUIRED offHeapLock.writeLock();"
+ );
+ try {
+ double newCoupon = 3.5 + Math.random();
+ System.out.println(
+ "..... @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender " +
+ " calling chm.put('369604101'," + newCoupon + ") "
+ );
+ bond.setCoupon(newCoupon);
+ chm.put("369604101", bond);
+ //cslMock.setEntryLockState(System.currentTimeMillis()); //mock'd
+ // chm.put("Offender ",cslMock); //mock'd
+ System.out.println(
+ "..... @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender coupon=[" +
+ bond.getCoupon() +
+ "] written. "
+ );
+ } finally {
+ System.out.println(
+ "..... @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender sleeping " + holdTime + " seconds "
+ );
+ Thread.sleep(holdTime * 1_000);
+ offHeapLock.unlockWrite(stamp);
+ System.out.println(
+ "..... @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender called " +
+ "offHeapLock.unlockWrite(" + stamp + ");"
+ );
+ }
+ /**
+ * ben.cotton@rutgers.edu
+ *
+ * END
+ *
+ */
+ chm.close();
+ } catch (Exception throwables) {
+ throwables.printStackTrace();
+ } finally {
+ System.out.println(
+ "..... @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender COMMITTED"
+ );
+
+ }
+ }
+}
diff --git a/src/test/java/net/openhft/chronicle/map/locks/DirtyReadOffender_ReaderWriterTest.java b/src/test/java/net/openhft/chronicle/map/locks/DirtyReadOffender_ReaderWriterTest.java
new file mode 100644
index 000000000..19d51bed5
--- /dev/null
+++ b/src/test/java/net/openhft/chronicle/map/locks/DirtyReadOffender_ReaderWriterTest.java
@@ -0,0 +1,143 @@
+package net.openhft.chronicle.map.locks;
+
+import net.openhft.chronicle.core.OS;
+import net.openhft.chronicle.map.ChronicleMap;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import static net.openhft.chronicle.values.Values.newNativeReference;
+
+public class DirtyReadOffender_ReaderWriterTest {
+
+ @Before
+ public void setUp() throws Exception {
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ }
+
+ @Test
+ public void main() {
+ try {
+ long sleepT = Long.parseLong("8");
+ long holdTime = Long.parseLong("20");
+
+ Thread tooThread = new Thread(new ReaderToo());
+ tooThread.start();
+
+ ChronicleMap chm =
+ DirtyReadTolerance.offHeap(
+ OS.TARGET + "/shm-" +
+ "OPERAND_CHRONICLE_MAP"
+ );
+ System.out.println(
+ " " +
+ " @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender established chm "
+ );
+ ChronicleStampedLock offHeapLock = new ChronicleStampedLock(
+ OS.TARGET + "/shm-"
+ + "OPERAND_ChronicleStampedLock"
+ );
+ Assert.assertNotEquals(offHeapLock, null);
+ BondVOInterface bond = newNativeReference(BondVOInterface.class);
+ //BondVOInterface cslMock = newNativeReference(BondVOInterface.class);
+ chm.acquireUsing("369604101", bond);
+ //chm.acquireUsing("Offender ", cslMock); // mock ChronicleStampLock
+ System.out.println(
+ " " +
+ " @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender sleeping " + sleepT + " seconds "
+ );
+ Thread.sleep(sleepT * 1_000);
+ System.out.println(
+ " " +
+ " @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender awakening "
+ );
+ /**
+ * ben.cotton@rutgers.edu ... anticipate Chronicle (www.OpenHFT.net)
+ * providing a j.u.c.l.StampedLock API for off-heap enthusiasts
+ *
+ * START
+ *
+ */
+ long stamp = 0;
+ System.out.println(
+ " " +
+ " @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender ACQUIRING offHeapLock.tryWriteLock();"
+ );
+ int blockedByHoldingReaderCount = 0;
+ while ((stamp = offHeapLock.tryWriteLock()) == 0) { //Ben?
+ Thread.sleep(1_000);
+ ++blockedByHoldingReaderCount;
+ System.out.println(
+ " " +
+ " @t=" + System.currentTimeMillis() +
+ " WAITING in offHeapLock.tryWriteLock()... "
+ );
+
+ }
+ Assert.assertNotEquals(blockedByHoldingReaderCount, 0);
+ System.out.println(
+ " " +
+ " @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender ACQUIRED offHeapLock.tryWriteLock();"
+ );
+ try {
+ double newCoupon = 3.5 + Math.random();
+ System.out.println(
+ " " +
+ " @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender " +
+ " calling chm.put('369604101'," + newCoupon + ") "
+ );
+ bond.setCoupon(newCoupon);
+ chm.put("369604101", bond);
+ //cslMock.setEntryLockState(System.currentTimeMillis()); //mock'd
+ // chm.put("Offender ",cslMock); //mock'd
+ System.out.println(
+ " " +
+ " @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender coupon=[" +
+ bond.getCoupon() +
+ "] written. "
+ );
+ } finally {
+ System.out.println(
+ " " +
+ " @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender sleeping " + holdTime + " seconds "
+ );
+ Thread.sleep(holdTime * 1_000);
+ offHeapLock.unlockWrite(stamp);
+ System.out.println(
+ " " +
+ " @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender called " +
+ "offHeapLock.unlockWrite(" + stamp + ");"
+ );
+ }
+ /**
+ * ben.cotton@rutgers.edu
+ *
+ * END
+ *
+ */
+ chm.close();
+ offHeapLock.closeChronicle();
+ } catch (Exception throwables) {
+ throwables.printStackTrace();
+ } finally {
+ System.out.println(
+ " " +
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadOffender COMMITTED"
+ );
+ }
+ }
+}
diff --git a/src/test/java/net/openhft/chronicle/map/locks/DirtyReadOffender_WriterReaderTest.java b/src/test/java/net/openhft/chronicle/map/locks/DirtyReadOffender_WriterReaderTest.java
new file mode 100644
index 000000000..0981ee85e
--- /dev/null
+++ b/src/test/java/net/openhft/chronicle/map/locks/DirtyReadOffender_WriterReaderTest.java
@@ -0,0 +1,143 @@
+package net.openhft.chronicle.map.locks;
+
+import net.openhft.chronicle.core.OS;
+import net.openhft.chronicle.map.ChronicleMap;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import static net.openhft.chronicle.values.Values.newNativeReference;
+
+public class DirtyReadOffender_WriterReaderTest {
+
+ @Before
+ public void setUp() throws Exception {
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ }
+
+ @Test
+ public void main() {
+ try {
+ long sleepT = Long.parseLong("8");
+ long holdTime = Long.parseLong("20");
+
+ Thread tooThread = new Thread(new WriterToo());
+ tooThread.start();
+
+ ChronicleMap chm =
+ DirtyReadTolerance.offHeap(
+ OS.TARGET + "/shm-" +
+ "OPERAND_CHRONICLE_MAP"
+ );
+ System.out.println(
+ " " +
+ " @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender established chm "
+ );
+ ChronicleStampedLock offHeapLock = new ChronicleStampedLock(
+ OS.TARGET + "/shm-"
+ + "OPERAND_ChronicleStampedLock"
+ );
+ Assert.assertNotEquals(offHeapLock, null);
+ BondVOInterface bond = newNativeReference(BondVOInterface.class);
+ //BondVOInterface cslMock = newNativeReference(BondVOInterface.class);
+ chm.acquireUsing("369604101", bond);
+ //chm.acquireUsing("Offender ", cslMock); // mock ChronicleStampLock
+ System.out.println(
+ " " +
+ " @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender sleeping " + sleepT + " seconds "
+ );
+ Thread.sleep(sleepT * 1_000);
+ System.out.println(
+ " " +
+ " @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender awakening "
+ );
+ /**
+ * ben.cotton@rutgers.edu ... anticipate Chronicle (www.OpenHFT.net)
+ * providing a j.u.c.l.StampedLock API for off-heap enthusiasts
+ *
+ * START
+ *
+ */
+ long stamp = 0;
+ System.out.println(
+ " " +
+ " @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender ACQUIRING offHeapLock.tryReadLock();"
+ );
+ int blockedByHoldingWriterCount = 0;
+ while ((stamp = offHeapLock.tryReadLock()) == 0) {
+ Thread.sleep(1_000);
+ ++blockedByHoldingWriterCount;
+ System.out.println(
+ " " +
+ " @t=" + System.currentTimeMillis() +
+ " WAITING in offHeapLock.tryReadLock()... "
+ );
+
+ }
+ Assert.assertNotEquals(blockedByHoldingWriterCount, 0);
+ System.out.println(
+ " " +
+ " @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender ACQUIRED offHeapLock.tryReadLock();"
+ );
+ try {
+ double newCoupon = 3.5 + Math.random();
+ System.out.println(
+ " " +
+ " @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender " +
+ " calling chm.put('369604101'," + newCoupon + ") "
+ );
+ bond.setCoupon(newCoupon);
+ chm.put("369604101", bond);
+ //cslMock.setEntryLockState(System.currentTimeMillis()); //mock'd
+ // chm.put("Offender ",cslMock); //mock'd
+ System.out.println(
+ " " +
+ " @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender coupon=[" +
+ bond.getCoupon() +
+ "] written. "
+ );
+ } finally {
+ System.out.println(
+ " " +
+ " @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender sleeping " + holdTime + " seconds "
+ );
+ Thread.sleep(holdTime * 1_000);
+ offHeapLock.unlockRead(stamp);
+ System.out.println(
+ " " +
+ " @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender called " +
+ "offHeapLock.unlockWrite(" + stamp + ");"
+ );
+ }
+ /**
+ * ben.cotton@rutgers.edu
+ *
+ * END
+ *
+ */
+ chm.close();
+ offHeapLock.closeChronicle();
+ } catch (Exception throwables) {
+ throwables.printStackTrace();
+ } finally {
+ System.out.println(
+ " " +
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadOffender COMMITTED"
+ );
+ }
+ }
+}
diff --git a/src/test/java/net/openhft/chronicle/map/locks/DirtyReadOffender_WriterWriterTest.java b/src/test/java/net/openhft/chronicle/map/locks/DirtyReadOffender_WriterWriterTest.java
new file mode 100644
index 000000000..1b5433c4e
--- /dev/null
+++ b/src/test/java/net/openhft/chronicle/map/locks/DirtyReadOffender_WriterWriterTest.java
@@ -0,0 +1,143 @@
+package net.openhft.chronicle.map.locks;
+
+import net.openhft.chronicle.core.OS;
+import net.openhft.chronicle.map.ChronicleMap;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import static net.openhft.chronicle.values.Values.newNativeReference;
+
+public class DirtyReadOffender_WriterWriterTest {
+
+ @Before
+ public void setUp() throws Exception {
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ }
+
+ @Test
+ public void main() {
+ try {
+ long sleepT = Long.parseLong("8");
+ long holdTime = Long.parseLong("20");
+
+ Thread tooThread = new Thread(new WriterToo());
+ tooThread.start();
+
+ ChronicleMap chm =
+ DirtyReadTolerance.offHeap(
+ OS.TARGET + "/shm-" +
+ "OPERAND_CHRONICLE_MAP"
+ );
+ System.out.println(
+ " " +
+ " @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender established chm "
+ );
+ ChronicleStampedLock offHeapLock = new ChronicleStampedLock(
+ OS.TARGET + "/shm-"
+ + "OPERAND_ChronicleStampedLock"
+ );
+ Assert.assertNotEquals(offHeapLock, null);
+ BondVOInterface bond = newNativeReference(BondVOInterface.class);
+ //BondVOInterface cslMock = newNativeReference(BondVOInterface.class);
+ chm.acquireUsing("369604101", bond);
+ //chm.acquireUsing("Offender ", cslMock); // mock ChronicleStampLock
+ System.out.println(
+ " " +
+ " @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender sleeping " + sleepT + " seconds "
+ );
+ Thread.sleep(sleepT * 1_000);
+ System.out.println(
+ " " +
+ " @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender awakening "
+ );
+ /**
+ * ben.cotton@rutgers.edu ... anticipate Chronicle (www.OpenHFT.net)
+ * providing a j.u.c.l.StampedLock API for off-heap enthusiasts
+ *
+ * START
+ *
+ */
+ long stamp = 0;
+ System.out.println(
+ " " +
+ " @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender ACQUIRING offHeapLock.writeLock();"
+ );
+ int blockedByHoldingWriterCount = 0;
+ while ((stamp = offHeapLock.tryWriteLock()) == 0) {
+ Thread.sleep(1_000);
+ ++blockedByHoldingWriterCount;
+ System.out.println(
+ " " +
+ " @t=" + System.currentTimeMillis() +
+ " WAITING in offHeapLock.tryWriteLock()... "
+ );
+
+ }
+ Assert.assertNotEquals(blockedByHoldingWriterCount, 0);
+ System.out.println(
+ " " +
+ " @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender ACQUIRED offHeapLock.writeLock();"
+ );
+ try {
+ double newCoupon = 3.5 + Math.random();
+ System.out.println(
+ " " +
+ " @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender " +
+ " calling chm.put('369604101'," + newCoupon + ") "
+ );
+ bond.setCoupon(newCoupon);
+ chm.put("369604101", bond);
+ //cslMock.setEntryLockState(System.currentTimeMillis()); //mock'd
+ // chm.put("Offender ",cslMock); //mock'd
+ System.out.println(
+ " " +
+ " @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender coupon=[" +
+ bond.getCoupon() +
+ "] written. "
+ );
+ } finally {
+ System.out.println(
+ " " +
+ " @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender sleeping " + holdTime + " seconds "
+ );
+ Thread.sleep(holdTime * 1_000);
+ offHeapLock.unlockWrite(stamp);
+ System.out.println(
+ " " +
+ " @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender called " +
+ "offHeapLock.unlockWrite(" + stamp + ");"
+ );
+ }
+ /**
+ * ben.cotton@rutgers.edu
+ *
+ * END
+ *
+ */
+ chm.close();
+ offHeapLock.closeChronicle();
+ } catch (Exception throwables) {
+ throwables.printStackTrace();
+ } finally {
+ System.out.println(
+ " " +
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadOffender COMMITTED"
+ );
+ }
+ }
+}
diff --git a/src/test/java/net/openhft/chronicle/map/locks/DirtyReadTolerance.java b/src/test/java/net/openhft/chronicle/map/locks/DirtyReadTolerance.java
new file mode 100644
index 000000000..accce31c7
--- /dev/null
+++ b/src/test/java/net/openhft/chronicle/map/locks/DirtyReadTolerance.java
@@ -0,0 +1,33 @@
+package net.openhft.chronicle.map.locks;
+
+import net.openhft.chronicle.map.ChronicleMap;
+import net.openhft.chronicle.map.ChronicleMapBuilder;
+
+import java.io.File;
+import java.io.IOException;
+
+public class DirtyReadTolerance {
+
+ public static void main(String[] args) throws Exception {
+ System.out.println("DirtyReadOffender,chrAig[" + args[0] + "]coupon=3.50,DirtyReadVictim");
+ String isoLevel = args[0];
+
+ ChronicleMap chm =
+ DirtyReadTolerance.offHeap(
+ args[2]
+ + "OPERAND_CHRONICLE_MAP"
+ );
+ System.out.println(",Established chm,");
+ }
+
+ static ChronicleMap offHeap(String operand) throws IOException {
+
+ return ChronicleMapBuilder.of(String.class, BondVOInterface.class)
+ .entries(16)
+ .averageKeySize("123456789".length())
+ .createPersistedTo(
+ new File(operand)
+ );
+ //.create();
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/net/openhft/chronicle/map/locks/DirtyReadVictim.java b/src/test/java/net/openhft/chronicle/map/locks/DirtyReadVictim.java
new file mode 100644
index 000000000..ad596d243
--- /dev/null
+++ b/src/test/java/net/openhft/chronicle/map/locks/DirtyReadVictim.java
@@ -0,0 +1,122 @@
+package net.openhft.chronicle.map.locks;
+
+import net.openhft.chronicle.map.ChronicleMap;
+
+import java.util.concurrent.locks.StampedLock;
+
+import static net.openhft.chronicle.values.Values.newNativeReference;
+
+public class DirtyReadVictim {
+
+ public static void main(String[] args) {
+ try {
+ String isoLevel = args[0];
+ long sleepMock = Long.parseLong(args[1]);
+ long holdTime = Long.parseLong(args[2]);
+ /**
+ * ben.cotton@rutgers.edu START
+ */
+ ChronicleMap chm =
+ DirtyReadTolerance.offHeap(
+ args[3]
+ + "OPERAND_CHRONICLE_MAP"
+ );
+ Double coupon = 0.00;
+ BondVOInterface bond = newNativeReference(BondVOInterface.class);
+ //BondVOInterface cslMock = newNativeReference(BondVOInterface.class); //mock'd
+ long stamp = 0;
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadVictim ENTERING offHeapLock.tryOptimisticRead()"
+ );
+ StampedLock offHeapLock =
+ new ChronicleStampedLock(
+ args[3]
+ + "OPERAND_ChronicleStampedLock"
+ );
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadVictim sleeping " + sleepMock + " seconds"
+ );
+
+ Thread.sleep(sleepMock * 1_000);
+ while ((stamp = offHeapLock.tryOptimisticRead()) < 0) {
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadVictim waiting for unlockWrite()... " +
+ ""
+ );
+ Thread.sleep(1000);
+ }
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadVictim ENTERED offHeapLock.tryOptimisticRead() " +
+ " stamp=[" +
+ stamp +
+ "]"
+ );
+ try {
+ chm.acquireUsing("369604101", bond);
+ //chm.acquireUsing("Offender ", cslMock); //mock'd
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadVictim calling chm.get('369604101').getCoupon()"
+ );
+ bond = chm.get("369604101");
+ coupon = bond.getCoupon();
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadVictim coupon=[" + coupon + "] read."
+ );
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadVictim sleeping " + holdTime + " seconds"
+ );
+
+ Thread.sleep(holdTime * 1_000);
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadVictim awakening"
+ );
+
+ } finally {
+ if (offHeapLock.validate(stamp)) {
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadVictim OPTIMISTICALLY_READ coupon=" +
+ coupon + " " +
+ "stamp = [" + stamp + "]"
+ );
+ } else {
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadVictim FAILED offHeapLock.validate(stamp) " +
+ " must apply PESSIMISTIC_POLICY (dirty read endured)" +
+ " coupon=[" + coupon + "] is *DIRTY*. " +
+ " stamp = [" + stamp + "]"
+ );
+
+ }
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadVictim EXITED offHeapLock.tryOptimisticRead()"
+ );
+
+ }
+ /**
+ * ben.cotton@rutgers.edu END
+ */
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadVictim got() coupon=" +
+ coupon + " "
+ );
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadVictim COMMITTED"
+ );
+ } catch (Exception throwables) {
+ throwables.printStackTrace();
+ }
+ }
+}
diff --git a/src/test/java/net/openhft/chronicle/map/locks/DirtyReadVictimIPCTest.java b/src/test/java/net/openhft/chronicle/map/locks/DirtyReadVictimIPCTest.java
new file mode 100644
index 000000000..10586405f
--- /dev/null
+++ b/src/test/java/net/openhft/chronicle/map/locks/DirtyReadVictimIPCTest.java
@@ -0,0 +1,249 @@
+package net.openhft.chronicle.map.locks;
+
+import net.openhft.chronicle.core.OS;
+import net.openhft.chronicle.map.ChronicleMap;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+
+import static net.openhft.chronicle.values.Values.newNativeReference;
+
+public class DirtyReadVictimIPCTest {
+
+ @Before
+ public void setUp() throws Exception {
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ }
+
+ /**
+ * ben.cotton@rutgers.edu -- should we even try to Test IPC compliance via this hack?
+ *
+ * Conjecture: Ben believes that: iff the operand set's members
+ * are all Chronicle Off-Heap resouces
+ * (e.g. Chronicle LongValue.class), then we may test IPC safety via an on-Heap j.l.Runnable
+ * operator bridge.
+ *
+ * this belief is STRICTLY conjecture.
+ *
+ * @throws IOException
+ */
+
+ @Test
+ public void mainOptimisticNegative() throws IOException {
+ try {
+ System.out.println("\n***** Optimistic (-) Test\n");
+// ProcessBuilder pb = new ProcessBuilder(
+// "/usr/bin/mkdir -p "+
+// " C:\\Users\\buddy\\dev\\shm\\ "
+// );
+
+// Process p = pb.start();
+// Scanner scan = new Scanner(p.getInputStream());
+// while (scan.hasNext()) {
+// System.out.println(
+// " ,,@t=" +
+// System.currentTimeMillis() +
+// " DirtyReadVictimTest CALLING [" +
+// scan.next() +
+// "]"
+// );
+// }
+// Thread.sleep(1_000);
+// p.destroyForcibly();
+// System.out.println(
+// " ,,@t=" +
+// System.currentTimeMillis() +
+// " DirtyReadVictimTest called [\n" +
+// "mkdir -p " +
+// " C:\\Users\\buddy\\dev\\shm\\ " +
+// "\n" +
+// "]"
+// );
+
+ /**
+ * ben.cotton@rutgers.edu START
+ */
+
+ ChronicleMap chm =
+ DirtyReadTolerance.offHeap(
+ OS.TARGET + "/shm-OPERAND_CHRONICLE_MAP"
+ );
+ Double coupon = 0.00;
+ BondVOInterface bond = newNativeReference(BondVOInterface.class);
+ long stamp;
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadVictim CALLING offHeapLock.tryOptimisticRead()"
+ );
+ ChronicleStampedLock offHeapLock = new ChronicleStampedLock(
+ OS.TARGET + "/shm-" +
+ "OPERAND_ChronicleStampedLock"
+ );
+ while ((stamp = offHeapLock.tryOptimisticRead()) == 0) {
+ ;
+ }
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadVictim CALLED offHeapLock.tryOptimisticRead()"
+ );
+ try {
+ chm.acquireUsing("369604101", bond);
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadVictim calling chm.get('369604101').getCoupon()"
+ );
+ bond = chm.get("369604101");
+ coupon = bond.getCoupon();
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadVictim coupon=[" + coupon + "] read."
+ );
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadVictim sleeping 20 seconds"
+ );
+ Thread offendingWriter = new Thread(
+ new DirtyReadOffenderIPCTest()
+ );
+ offendingWriter.start();
+ Thread.sleep(20_000);
+
+ } finally {
+ boolean r;
+ if ((r = offHeapLock.validate(stamp))) {
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadVictim OPTIMISTICALLY_READ coupon=" +
+ coupon + " "
+ );
+ // THIS Test will/must FAIL. i.e. OPTIMISM tested (-) in this case
+ Assert.assertEquals(
+ Boolean.FALSE,
+ r
+ );
+ } else {
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadVictim FAILED offHeapLock.validate(stamp) " +
+ " must apply PESSIMISTIC_POLICY (dirty read endured)" +
+ " coupon=[" + coupon + "] is *DIRTY*. "
+ );
+ Assert.assertNotEquals(
+ Boolean.TRUE,
+ r
+ );
+ }
+ //offHeapLock.unlockWrite(writerStamp);
+ }
+ /**
+ * ben.cotton@rutgers.edu END
+ */
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadVictim got() coupon=" +
+ coupon + " "
+ );
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadVictim COMMITTED"
+ );
+ } catch (Exception throwables) {
+ throwables.printStackTrace();
+ }
+ }
+
+ @Test
+ public void mainOptimisticPositive() {
+ System.out.println("\n***** Optimistic (+) Test\n");
+ try {
+ /**
+ * ben.cotton@rutgers.edu START
+ */
+ ChronicleMap chm =
+ DirtyReadTolerance.offHeap(
+ OS.TARGET + "/shm-OPERAND_CHRONICLE_MAP"
+ );
+ Double coupon = 0.00;
+ BondVOInterface bond = newNativeReference(BondVOInterface.class);
+ long stamp = 0;
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadVictim CALLING offHeapLock.tryOptimisticRead()"
+ );
+ ChronicleStampedLock offHeapLock = new ChronicleStampedLock(
+ OS.TARGET + "/shm-"
+ + "OPERAND_ChronicleStampedLock"
+ );
+ while ((stamp = offHeapLock.tryOptimisticRead()) == 0) {
+ ;
+ }
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadVictim CALLED offHeapLock.tryOptimisticRead()"
+ );
+ try {
+ chm.acquireUsing("369604101", bond);
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadVictim calling chm.get('369604101').getCoupon()"
+ );
+ bond = (BondVOInterface) chm.get("369604101");
+ coupon = bond.getCoupon();
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadVictim coupon=[" + coupon + "] read."
+ );
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadVictim sleeping 2 seconds"
+ );
+ Thread.sleep(2_000);
+ } finally {
+ if (offHeapLock.validate(stamp)) {
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadVictim OPTIMISTICALLY_READ coupon=" +
+ coupon + " "
+ );
+ // THIS Test will pass when ChronicleStampedLock is GA
+ Assert.assertEquals(
+ Boolean.TRUE,
+ true
+ );
+ } else {
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadVictim FAILED offHeapLock.validate(stamp) " +
+ " must apply PESSIMISTIC_POLICY (dirty read endured)" +
+ " coupon=[" + coupon + "] is *DIRTY*. "
+ );
+ // THIS Test will execute pass when ChronicleStampedLock is GA
+ Assert.assertNotEquals(
+ Boolean.TRUE,
+ false
+ );
+ }
+ }
+ /*
+ * ben.cotton@rutgers.edu END
+ */
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadVictim got() coupon=" +
+ coupon + " "
+ );
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadVictim COMMITTED"
+ );
+ } catch (Exception throwables) {
+ throwables.printStackTrace();
+ }
+ }
+}
diff --git a/src/test/java/net/openhft/chronicle/map/locks/DirtyReadVictimTest.java b/src/test/java/net/openhft/chronicle/map/locks/DirtyReadVictimTest.java
new file mode 100644
index 000000000..4c9cf7eae
--- /dev/null
+++ b/src/test/java/net/openhft/chronicle/map/locks/DirtyReadVictimTest.java
@@ -0,0 +1,209 @@
+package net.openhft.chronicle.map.locks;
+
+import net.openhft.chronicle.core.OS;
+import net.openhft.chronicle.map.ChronicleMap;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+
+import static net.openhft.chronicle.values.Values.newNativeReference;
+
+public class DirtyReadVictimTest {
+
+ @Before
+ public void setUp() throws Exception {
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ }
+
+ @Test
+ public void mainOptimisticNegative() throws IOException {
+ try {
+ System.out.println("\n***** Optimistic (-) Test\n");
+
+ /**
+ * ben.cotton@rutgers.edu START
+ */
+
+ ChronicleMap chm =
+ DirtyReadTolerance.offHeap(
+ OS.TARGET + "/shm-OPERAND_CHRONICLE_MAP"
+ );
+ Double coupon = 0.00;
+ BondVOInterface bond = newNativeReference(BondVOInterface.class);
+ long stamp;
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadVictim CALLING offHeapLock.tryOptimisticRead()"
+ );
+ ChronicleStampedLock offHeapLock = new ChronicleStampedLock(
+ OS.TARGET + "/shm-" +
+ "OPERAND_ChronicleStampedLock"
+ );
+ while ((stamp = offHeapLock.tryOptimisticRead()) == 0) {
+ }
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadVictim CALLED offHeapLock.tryOptimisticRead()"
+ );
+ try {
+ chm.acquireUsing("369604101", bond);
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadVictim calling chm.get('369604101').getCoupon()"
+ );
+ bond = chm.get("369604101");
+ coupon = bond.getCoupon();
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadVictim coupon=[" + coupon + "] read."
+ );
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadVictim sleeping 20 seconds"
+ );
+ Thread offendingWriter = new Thread(
+ new DirtyReadOffenderTest()
+ );
+ offendingWriter.start();
+ Thread.sleep(20_000);
+
+ } finally {
+ boolean r;
+ if ((r = offHeapLock.validate(stamp))) {
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadVictim OPTIMISTICALLY_READ coupon=" +
+ coupon + " "
+ );
+ // THIS Test will/must FAIL. i.e. OPTIMISM tested (-) in this case
+ Assert.assertEquals(
+ Boolean.FALSE,
+ r
+ );
+ } else {
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadVictim FAILED offHeapLock.validate(stamp) " +
+ " must apply PESSIMISTIC_POLICY (dirty read endured)" +
+ " coupon=[" + coupon + "] is *DIRTY*. "
+ );
+ Assert.assertNotEquals(
+ Boolean.TRUE,
+ r
+ );
+ }
+ //offHeapLock.unlockWrite(writerStamp);
+ }
+ /**
+ * ben.cotton@rutgers.edu END
+ */
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadVictim got() coupon=" +
+ coupon + " "
+ );
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadVictim COMMITTED"
+ );
+ chm.close();
+ offHeapLock.closeChronicle();
+ } catch (Exception throwables) {
+ throwables.printStackTrace();
+ }
+ }
+
+ @Test
+ public void mainOptimisticPositive() {
+ System.out.println("\n***** Optimistic (+) Test\n");
+ try {
+ /**
+ * ben.cotton@rutgers.edu START
+ */
+ ChronicleMap chm =
+ DirtyReadTolerance.offHeap(
+ OS.TARGET + "/shm-OPERAND_CHRONICLE_MAP"
+ );
+ Double coupon = 0.00;
+ BondVOInterface bond = newNativeReference(BondVOInterface.class);
+ long stamp = 0;
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadVictim CALLING offHeapLock.tryOptimisticRead()"
+ );
+ ChronicleStampedLock offHeapLock = new ChronicleStampedLock(
+ OS.TARGET + "/shm-"
+ + "OPERAND_ChronicleStampedLock"
+ );
+ while ((stamp = offHeapLock.tryOptimisticRead()) == 0) {
+ }
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadVictim CALLED offHeapLock.tryOptimisticRead()"
+ );
+ try {
+ chm.acquireUsing("369604101", bond);
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadVictim calling chm.get('369604101').getCoupon()"
+ );
+ bond = chm.get("369604101");
+ coupon = bond.getCoupon();
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadVictim coupon=[" + coupon + "] read."
+ );
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadVictim sleeping 2 seconds"
+ );
+ Thread.sleep(2_000);
+ } finally {
+ if (offHeapLock.validate(stamp)) {
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadVictim OPTIMISTICALLY_READ coupon=" +
+ coupon + " "
+ );
+ // THIS Test will pass when ChronicleStampedLock is GA
+ Assert.assertEquals(
+ Boolean.TRUE,
+ true
+ );
+ } else {
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadVictim FAILED offHeapLock.validate(stamp) " +
+ " must apply PESSIMISTIC_POLICY (dirty read endured)" +
+ " coupon=[" + coupon + "] is *DIRTY*. "
+ );
+ // THIS Test will execute pass when ChronicleStampedLock is GA
+ Assert.assertNotEquals(
+ Boolean.TRUE,
+ false
+ );
+ }
+ }
+ /**
+ * ben.cotton@rutgers.edu END
+ */
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadVictim got() coupon=" +
+ coupon + " "
+ );
+ System.out.println(
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadVictim COMMITTED"
+ );
+ } catch (Exception throwables) {
+ throwables.printStackTrace();
+ }
+ }
+}
diff --git a/src/test/java/net/openhft/chronicle/map/locks/ReaderToo.java b/src/test/java/net/openhft/chronicle/map/locks/ReaderToo.java
new file mode 100644
index 000000000..703553d77
--- /dev/null
+++ b/src/test/java/net/openhft/chronicle/map/locks/ReaderToo.java
@@ -0,0 +1,114 @@
+package net.openhft.chronicle.map.locks;
+
+import net.openhft.chronicle.core.OS;
+import net.openhft.chronicle.map.ChronicleMap;
+
+import static net.openhft.chronicle.values.Values.newNativeReference;
+
+class ReaderToo implements Runnable {
+
+ @Override
+ public void run() {
+ try {
+ String isoLevel = "READER_TOO";
+ long sleepMock = Long.parseLong("0");
+ long holdTime = Long.parseLong("20");
+ /**
+ * ben.cotton@rutgers.edu START
+ */
+ ChronicleMap chm =
+ DirtyReadTolerance.offHeap(
+ OS.TARGET + "/shm-OPERAND_CHRONICLE_MAP"
+ );
+ Double coupon = 0.00;
+ BondVOInterface bond = newNativeReference(BondVOInterface.class);
+ //BondVOInterface cslMock = newNativeReference(BondVOInterface.class); //mock'd
+ long stamp = 0;
+ System.out.println(
+ "READER_TOO " +
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadIntolerant ENTERING offHeapLock.readLock()"
+ );
+ ChronicleStampedLock offHeapLock =
+ new ChronicleStampedLock(
+ OS.TARGET + "/shm-"
+ + "OPERAND_ChronicleStampedLock"
+ );
+ System.out.println(
+ "READER_TOO " +
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadIntolerant sleeping " + sleepMock + " seconds"
+ );
+ Thread.sleep(sleepMock * 1_000);
+ while ((stamp = offHeapLock.tryReadLock()) < 0) {
+ //Assert.assertEquals(Boolean.TRUE, false); // we failed!;
+ }
+ //Assert.assertEquals(Boolean.TRUE, true); // we passed
+ System.out.println(
+ "READER_TOO " +
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadIntolerant ENTERED offHeapLock.readLock() " +
+ " stamp=[" +
+ stamp +
+ "]"
+ );
+ try {
+ chm.acquireUsing("369604101", bond);
+ //chm.acquireUsing("Offender ", cslMock); //mock'd
+ System.out.println(
+ "READER_TOO " +
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadIntolerant calling chm.get('369604101').getCoupon()"
+ );
+ bond = chm.get("369604101");
+ coupon = bond.getCoupon();
+ System.out.println(
+ "READER_TOO " +
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadIntolerant coupon=[" + coupon + "] read."
+ );
+ System.out.println(
+ "READER_TOO " +
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadIntolerant sleeping " + holdTime + " seconds"
+ );
+
+ Thread.sleep(holdTime * 1_000);
+ System.out.println(
+ "READER_TOO " +
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadIntolerant awakening"
+ );
+
+ } finally {
+ offHeapLock.unlockRead(stamp);
+ System.out.println(
+ "READER_TOO " +
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadIntolerant offHeapLock.unlockRead(" +
+ stamp + ") completed."
+ );
+
+ }
+ /**
+ * ben.cotton@rutgers.edu END
+ */
+
+ System.out.println(
+ "READER_TOO " +
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadIntolerant got() coupon=" +
+ coupon + " "
+ );
+ System.out.println(
+ "READER_TOO " +
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadIntolerant COMMITTED"
+ );
+ chm.close();
+ offHeapLock.closeChronicle();
+ } catch (Exception throwables) {
+ throwables.printStackTrace();
+ }
+ }
+}
diff --git a/src/test/java/net/openhft/chronicle/map/locks/WriterToo.java b/src/test/java/net/openhft/chronicle/map/locks/WriterToo.java
new file mode 100644
index 000000000..e6a1b508d
--- /dev/null
+++ b/src/test/java/net/openhft/chronicle/map/locks/WriterToo.java
@@ -0,0 +1,119 @@
+package net.openhft.chronicle.map.locks;
+
+import net.openhft.chronicle.core.OS;
+import net.openhft.chronicle.map.ChronicleMap;
+import org.junit.Assert;
+
+import static net.openhft.chronicle.values.Values.newNativeReference;
+
+class WriterToo implements Runnable {
+
+ @Override
+ public void run() {
+ try {
+ long sleepT = 0;
+ long holdTime = 20;
+
+ ChronicleMap chm =
+ DirtyReadTolerance.offHeap(
+ OS.TARGET + "/shm-" +
+ "OPERAND_CHRONICLE_MAP"
+ );
+ System.out.println(
+ "WRITER TOO" +
+ " @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender established chm "
+ );
+ ChronicleStampedLock offHeapLock = new ChronicleStampedLock(
+ OS.TARGET + "/shm-"
+ + "OPERAND_ChronicleStampedLock"
+ );
+ Assert.assertNotEquals(offHeapLock, null);
+ BondVOInterface bond = newNativeReference(BondVOInterface.class);
+ //BondVOInterface cslMock = newNativeReference(BondVOInterface.class);
+ chm.acquireUsing("369604101", bond);
+ //chm.acquireUsing("Offender ", cslMock); // mock ChronicleStampLock
+ System.out.println(
+ "WRITER TOO" +
+ " @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender sleeping " + sleepT + " seconds "
+ );
+ Thread.sleep(sleepT * 1_000);
+ System.out.println(
+ "WRITER TOO" +
+ " @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender awakening "
+ );
+ /*
+ *anticipate Chronicle (www.OpenHFT.net)
+ * providing a j.u.c.l.StampedLock API for off-heap enthusiasts
+ *
+ * START
+ *
+ */
+ long stamp = 0;
+ System.out.println(
+ "WRITER TOO" +
+ " @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender ACQUIRING offHeapLock.writeLock();"
+ );
+ while ((stamp = offHeapLock.writeLock()) == 0) {
+ }
+ System.out.println(
+ "WRITER TOO" +
+ " @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender ACQUIRED offHeapLock.writeLock();"
+ );
+ try {
+ double newCoupon = 3.5 + Math.random();
+ System.out.println(
+ "WRITER TOO" +
+ " @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender " +
+ " calling chm.put('369604101'," + newCoupon + ") "
+ );
+ bond.setCoupon(newCoupon);
+ chm.put("369604101", bond);
+ //cslMock.setEntryLockState(System.currentTimeMillis()); //mock'd
+ // chm.put("Offender ",cslMock); //mock'd
+ System.out.println(
+ "WRITER TOO" +
+ " @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender coupon=[" +
+ bond.getCoupon() +
+ "] written. "
+ );
+ } finally {
+ System.out.println(
+ "WRITER TOO" +
+ " @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender sleeping " + holdTime + " seconds "
+ );
+ Thread.sleep(holdTime * 1_000);
+ offHeapLock.unlockWrite(stamp);
+ System.out.println(
+ "WRITER TOO" +
+ " @t=" + System.currentTimeMillis() +
+ " DirtyReadOffender called " +
+ "offHeapLock.unlockWrite(" + stamp + ");"
+ );
+ }
+ /**
+ * ben.cotton@rutgers.edu
+ *
+ * END
+ *
+ */
+ chm.close();
+ offHeapLock.closeChronicle();
+ } catch (Exception throwables) {
+ throwables.printStackTrace();
+ } finally {
+ System.out.println(
+ "WRITER TOO" +
+ " ,,@t=" + System.currentTimeMillis() +
+ " DirtyReadOffender COMMITTED"
+ );
+ }
+ }
+}