From 71599fd4ebfdaae561d997c5d0d36d1b3f3ffebe Mon Sep 17 00:00:00 2001 From: leventov Date: Sat, 3 Oct 2015 01:38:35 +0300 Subject: [PATCH] Proper zeroing-out mapped bytes (that could potentially be dirty), and only the bytes that are required to be zero by tier state invariants; refactorings and renamings --- .../{TierData.java => TierCountersArea.java} | 8 +- .../hash/impl/VanillaChronicleHash.java | 115 ++++++++++++------ .../hash/impl/stage/entry/SegmentStages.java | 21 ++-- .../chronicle/map/ReplicatedChronicleMap.java | 21 ++-- 4 files changed, 104 insertions(+), 61 deletions(-) rename src/main/java/net/openhft/chronicle/hash/impl/{TierData.java => TierCountersArea.java} (88%) diff --git a/src/main/java/net/openhft/chronicle/hash/impl/TierData.java b/src/main/java/net/openhft/chronicle/hash/impl/TierCountersArea.java similarity index 88% rename from src/main/java/net/openhft/chronicle/hash/impl/TierData.java rename to src/main/java/net/openhft/chronicle/hash/impl/TierCountersArea.java index 32dc43fce..414c4ce6a 100644 --- a/src/main/java/net/openhft/chronicle/hash/impl/TierData.java +++ b/src/main/java/net/openhft/chronicle/hash/impl/TierCountersArea.java @@ -18,7 +18,13 @@ package net.openhft.chronicle.hash.impl; import static net.openhft.lang.io.NativeBytes.UNSAFE; -public enum TierData { +/** + * The reason why this is not a data value generated class, and offsets are allocated by + * hand -- this functionality is accessed concurrently from many threads, and data value generated + * classes are stateful - so need to keep an instance in thread local that seems to be overall more + * pain than gain. + */ +public enum TierCountersArea { ; public static final long NEXT_TIER_INDEX_OFFSET = 0L; diff --git a/src/main/java/net/openhft/chronicle/hash/impl/VanillaChronicleHash.java b/src/main/java/net/openhft/chronicle/hash/impl/VanillaChronicleHash.java index b487b2c52..a18f4f5ab 100644 --- a/src/main/java/net/openhft/chronicle/hash/impl/VanillaChronicleHash.java +++ b/src/main/java/net/openhft/chronicle/hash/impl/VanillaChronicleHash.java @@ -58,6 +58,8 @@ public abstract class VanillaChronicleHash sizeInBytesWithoutTiers() means there are some tiered segments Math.max(sizeInBytesWithoutTiers(), file.length()), BytesMarshallableSerializer.create())); - // newly-extended file contents are not guaranteed to be zero + if (created) { - // TODO zero out only hash lookup tables and bit sets, not entry data areas - bytes.zeroOut(headerSize, bytes.capacity(), true); - // zero out tier data - bytes.zeroOut(headerSize, headerSize + 64, true); + zeroOutNewlyMappedChronicleMapBytes(); } } + /** + * newly-extended file contents are not guaranteed to be zero + */ + private void zeroOutNewlyMappedChronicleMapBytes() { + zeroOutGlobalMutableState(); + zeroOutFirstSegmentTiers(); + } + + private void zeroOutGlobalMutableState() { + bytes.zeroOut(headerSize, headerSize + 64, true); + } + + private void zeroOutFirstSegmentTiers() { + for (int segmentIndex = 0; segmentIndex < segments(); segmentIndex++) { + long segmentOffset = msBytesSegmentOffset(segmentIndex); + zeroOutNewlyMappedTier(bytes, segmentOffset); + } + } + + private void zeroOutNewlyMappedTier(Bytes bytes, long segmentOffset) { + // Zero out hash lookup, tier data and free list bit set. Leave entry space. + long zeroOutEnd = segmentOffset + segmentSize - segmentEntrySpaceOuterSize; + bytes.zeroOut(segmentOffset, zeroOutEnd, true); + } + private void initTierBulks(long byteStoreSize) { tierBulkOffsets = new ArrayList<>(); long tierBulkOffset = roundUpRuntimePageSize(sizeInBytesWithoutTiers()); @@ -416,10 +440,10 @@ public abstract class VanillaChronicleHash tierDataLockTryAcquireOperation = + static final LockingStrategy globalMutableStateLockingStrategy = + VanillaReadWriteUpdateWithWaitsLockingStrategy.instance(); + static final TryAcquireOperation globalMutableStateLockTryAcquireOperation = TryAcquireOperations.lock(); static final - AcquisitionStrategy tierDataLockAcquisitionStrategy = + AcquisitionStrategy + globalMutableStateLockAcquisitionStrategy = AcquisitionStrategies.spinLoopOrFail(2, TimeUnit.SECONDS); - private static final long TIER_DATA_LOCK_OFFSET = 0L; - private static final long EXTRA_ALLOCATED_TIER_BULK_COUNT_OFFSET = TIER_DATA_LOCK_OFFSET + 8L; + private static final long GLOBAL_MUTABLE_STATE_LOCK_OFFSET = 0L; + private static final long EXTRA_ALLOCATED_TIER_BULK_COUNT_OFFSET = + GLOBAL_MUTABLE_STATE_LOCK_OFFSET + 8L; private static final long FIRST_FREE_TIER_INDEX_OFFSET = EXTRA_ALLOCATED_TIER_BULK_COUNT_OFFSET + 8L; private static final long TIERS_IN_USE_OFFSET = FIRST_FREE_TIER_INDEX_OFFSET + 8L; - private long tierDataAddress() { + private long globalMutableStateAddress() { return bytes.address() + headerSize; } - private void tierDataLock() { - tierDataLockAcquisitionStrategy.acquire( - tierDataLockTryAcquireOperation, tierDataLockingStrategy, Access.nativeAccess(), - null, tierDataAddress() + TIER_DATA_LOCK_OFFSET); + private void globalMutableStateLock() { + globalMutableStateLockAcquisitionStrategy.acquire( + globalMutableStateLockTryAcquireOperation, globalMutableStateLockingStrategy, + Access.nativeAccess(), null, + globalMutableStateAddress() + GLOBAL_MUTABLE_STATE_LOCK_OFFSET); } - private void tierDataUnlock() { - tierDataLockingStrategy.unlock(Access.nativeAccess(), null, - tierDataAddress() + TIER_DATA_LOCK_OFFSET); + private void globalMutableStateUnlock() { + globalMutableStateLockingStrategy.unlock(Access.nativeAccess(), null, + globalMutableStateAddress() + GLOBAL_MUTABLE_STATE_LOCK_OFFSET); } protected long extraAllocatedTierBulkCount() { @@ -514,7 +543,7 @@ public abstract class VanillaChronicleHash 0) { tiersInUse(tiersInUse + 1); - long tierBaseAddr = tierIndexToBaseAddr(firstFreeTierIndex); - long tierDataAddr = tierBaseAddr + segmentHashLookupOuterSize; - long nextFreeTierIndex = TierData.nextTierIndex(tierDataAddr); + Bytes allocatedTierBytes = tierBytes(firstFreeTierIndex); + long allocatedTierOffset = tierBytesOffset(firstFreeTierIndex); + long tierBaseAddr = allocatedTierBytes.address() + allocatedTierOffset; + long tierCountersAreaAddr = tierBaseAddr + segmentHashLookupOuterSize; + long nextFreeTierIndex = TierCountersArea.nextTierIndex(tierCountersAreaAddr); + // after reading previously stored nextFreeTierIndex (next linked list entry), + // zero out the whole tier + zeroOutNewlyMappedTier(allocatedTierBytes, allocatedTierOffset); // now, when this tier will be a part of the map, the next tier designates // the next tier in the data structure, should be 0 - TierData.nextTierIndex(tierDataAddr, 0); - TierData.segmentIndex(tierDataAddr, forSegmentIndex); - TierData.tier(tierDataAddr, tier); + TierCountersArea.nextTierIndex(tierCountersAreaAddr, 0); + TierCountersArea.segmentIndex(tierCountersAreaAddr, forSegmentIndex); + TierCountersArea.tier(tierCountersAreaAddr, tier); firstFreeTierIndex(nextFreeTierIndex); return firstFreeTierIndex; } else { @@ -547,7 +581,7 @@ public abstract class VanillaChronicleHash> log2NumberOfTiersInBulk; if (bulkIndex >= tierBulkOffsets.size()) diff --git a/src/main/java/net/openhft/chronicle/hash/impl/stage/entry/SegmentStages.java b/src/main/java/net/openhft/chronicle/hash/impl/stage/entry/SegmentStages.java index fba18cbe0..53d727d46 100644 --- a/src/main/java/net/openhft/chronicle/hash/impl/stage/entry/SegmentStages.java +++ b/src/main/java/net/openhft/chronicle/hash/impl/stage/entry/SegmentStages.java @@ -25,7 +25,6 @@ import net.openhft.chronicle.hash.SegmentLock; import net.openhft.chronicle.hash.impl.*; import net.openhft.chronicle.hash.impl.stage.hash.Chaining; import net.openhft.chronicle.hash.impl.stage.hash.CheckOnEachPublicOperation; -import net.openhft.chronicle.hash.impl.stage.hash.LogHolder; import net.openhft.chronicle.hash.impl.stage.query.KeySearch; import net.openhft.chronicle.hash.locks.InterProcessLock; import net.openhft.chronicle.hash.serialization.internal.MetaBytesInterop; @@ -40,6 +39,7 @@ import java.util.Objects; import static net.openhft.chronicle.algo.MemoryUnit.BITS; import static net.openhft.chronicle.algo.MemoryUnit.LONGS; import static net.openhft.chronicle.hash.impl.LocalLockState.UNLOCKED; +import static net.openhft.chronicle.hash.impl.VanillaChronicleHash.TIER_COUNTERS_AREA_SIZE; @Staged public abstract class SegmentStages implements SegmentLock, LocksInterface { @@ -473,32 +473,32 @@ public abstract class SegmentStages implements SegmentLock, LocksInterface { this.segmentBaseAddr = tierBaseAddr; } - public long tierDataOffset() { + public long tierCountersAreaAddr() { return segmentBaseAddr + hh.h().segmentHashLookupOuterSize; } public long nextTierIndex() { - return TierData.nextTierIndex(tierDataOffset()); + return TierCountersArea.nextTierIndex(tierCountersAreaAddr()); } public void nextTierIndex(long nextTierIndex) { - TierData.nextTierIndex(tierDataOffset(), nextTierIndex); + TierCountersArea.nextTierIndex(tierCountersAreaAddr(), nextTierIndex); } public long nextPosToSearchFromTiered() { - return TierData.nextPosToSearchFromTiered(tierDataOffset()); + return TierCountersArea.nextPosToSearchFromTiered(tierCountersAreaAddr()); } public void nextPosToSearchFromTiered(long nextPosToSearchFrom) { - TierData.nextPosToSearchFromTiered(tierDataOffset(), nextPosToSearchFrom); + TierCountersArea.nextPosToSearchFromTiered(tierCountersAreaAddr(), nextPosToSearchFrom); } public long prevTierIndex() { - return TierData.prevTierIndex(tierDataOffset()); + return TierCountersArea.prevTierIndex(tierCountersAreaAddr()); } public void prevTierIndex(long prevTierIndex) { - TierData.prevTierIndex(tierDataOffset(), prevTierIndex); + TierCountersArea.prevTierIndex(tierCountersAreaAddr(), prevTierIndex); } public void nextTier() { @@ -540,13 +540,12 @@ public abstract class SegmentStages implements SegmentLock, LocksInterface { VanillaChronicleHash h = hh.h(); PublicMultiStoreBytes segmentBytes = this.segmentBytes; - segmentBytes.setBytesOffset(h.tierBytes(tierIndex), h.bytesOffset(tierIndex)); + segmentBytes.setBytesOffset(h.tierBytes(tierIndex), h.tierBytesOffset(tierIndex)); long segmentBaseAddr = this.segmentBaseAddr; segmentBS.set(segmentBaseAddr, h.segmentSize); - // 64 is a cache line with tier data - long freeListOffset = h.segmentHashLookupOuterSize + 64; + long freeListOffset = h.segmentHashLookupOuterSize + TIER_COUNTERS_AREA_SIZE; freeList.setOffset(segmentBaseAddr + freeListOffset); entrySpaceOffset = freeListOffset + h.segmentFreeListOuterSize + diff --git a/src/main/java/net/openhft/chronicle/map/ReplicatedChronicleMap.java b/src/main/java/net/openhft/chronicle/map/ReplicatedChronicleMap.java index a12ab6018..092261c84 100644 --- a/src/main/java/net/openhft/chronicle/map/ReplicatedChronicleMap.java +++ b/src/main/java/net/openhft/chronicle/map/ReplicatedChronicleMap.java @@ -19,7 +19,7 @@ package net.openhft.chronicle.map; import net.openhft.chronicle.algo.bitset.BitSetFrame; import net.openhft.chronicle.algo.bitset.ConcurrentFlatBitSetFrame; import net.openhft.chronicle.algo.bitset.SingleThreadedFlatBitSetFrame; -import net.openhft.chronicle.hash.impl.TierData; +import net.openhft.chronicle.hash.impl.TierCountersArea; import net.openhft.chronicle.hash.impl.VanillaChronicleHash; import net.openhft.chronicle.hash.impl.stage.hash.ChainingInterface; import net.openhft.chronicle.hash.replication.AbstractReplication; @@ -591,14 +591,14 @@ public class ReplicatedChronicleMap