From e4d7a50eeec380fa8d3dbc9880fd763274ca4fdc Mon Sep 17 00:00:00 2001 From: Peter Lawrey Date: Fri, 18 Sep 2020 17:03:09 +0100 Subject: [PATCH] Move classes in algos which need map into maps, closes https://github.com/OpenHFT/Chronicle-Algorithms/issues/10 --- .../map/locks/ChronicleStampedLock.java | 518 ++++++++++++++++++ .../ChronicleStampedLockVOInterface.java | 36 ++ .../chronicle/map/locks/BondVOInterface.java | 93 ++++ .../map/locks/DirtyReadIntolerant.java | 99 ++++ ...DirtyReadIntolerant_ReaderReader_Test.java | 120 ++++ .../map/locks/DirtyReadOffender.java | 112 ++++ .../map/locks/DirtyReadOffenderIPCTest.java | 118 ++++ .../map/locks/DirtyReadOffenderTest.java | 109 ++++ .../DirtyReadOffender_ReaderWriterTest.java | 143 +++++ .../DirtyReadOffender_WriterReaderTest.java | 143 +++++ .../DirtyReadOffender_WriterWriterTest.java | 143 +++++ .../map/locks/DirtyReadTolerance.java | 33 ++ .../chronicle/map/locks/DirtyReadVictim.java | 122 +++++ .../map/locks/DirtyReadVictimIPCTest.java | 249 +++++++++ .../map/locks/DirtyReadVictimTest.java | 209 +++++++ .../chronicle/map/locks/ReaderToo.java | 114 ++++ .../chronicle/map/locks/WriterToo.java | 119 ++++ 17 files changed, 2480 insertions(+) create mode 100644 src/main/java/net/openhft/chronicle/map/locks/ChronicleStampedLock.java create mode 100644 src/main/java/net/openhft/chronicle/map/locks/ChronicleStampedLockVOInterface.java create mode 100644 src/test/java/net/openhft/chronicle/map/locks/BondVOInterface.java create mode 100644 src/test/java/net/openhft/chronicle/map/locks/DirtyReadIntolerant.java create mode 100644 src/test/java/net/openhft/chronicle/map/locks/DirtyReadIntolerant_ReaderReader_Test.java create mode 100644 src/test/java/net/openhft/chronicle/map/locks/DirtyReadOffender.java create mode 100644 src/test/java/net/openhft/chronicle/map/locks/DirtyReadOffenderIPCTest.java create mode 100644 src/test/java/net/openhft/chronicle/map/locks/DirtyReadOffenderTest.java create mode 100644 src/test/java/net/openhft/chronicle/map/locks/DirtyReadOffender_ReaderWriterTest.java create mode 100644 src/test/java/net/openhft/chronicle/map/locks/DirtyReadOffender_WriterReaderTest.java create mode 100644 src/test/java/net/openhft/chronicle/map/locks/DirtyReadOffender_WriterWriterTest.java create mode 100644 src/test/java/net/openhft/chronicle/map/locks/DirtyReadTolerance.java create mode 100644 src/test/java/net/openhft/chronicle/map/locks/DirtyReadVictim.java create mode 100644 src/test/java/net/openhft/chronicle/map/locks/DirtyReadVictimIPCTest.java create mode 100644 src/test/java/net/openhft/chronicle/map/locks/DirtyReadVictimTest.java create mode 100644 src/test/java/net/openhft/chronicle/map/locks/ReaderToo.java create mode 100644 src/test/java/net/openhft/chronicle/map/locks/WriterToo.java 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" + ); + } + } +}