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

This commit is contained in:
leventov
2015-10-03 01:38:35 +03:00
parent 05f94965be
commit 71599fd4eb
4 changed files with 104 additions and 61 deletions
@@ -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;
@@ -58,6 +58,8 @@ public abstract class VanillaChronicleHash<K, KI, MKI extends MetaBytesInterop<K
private static final long serialVersionUID = 0L;
public static final long TIER_COUNTERS_AREA_SIZE = 64;
/////////////////////////////////////////////////
// Version
public final String dataFileVersion;
@@ -220,7 +222,7 @@ public abstract class VanillaChronicleHash<K, KI, MKI extends MetaBytesInterop<K
}
private long segmentSize() {
long ss = segmentHashLookupOuterSize + 64 /* Tier cache line */ +
long ss = segmentHashLookupOuterSize + TIER_COUNTERS_AREA_SIZE +
segmentFreeListOuterSize + segmentEntrySpaceOuterSize;
if ((ss & 63L) != 0)
throw new AssertionError();
@@ -295,15 +297,37 @@ public abstract class VanillaChronicleHash<K, KI, MKI extends MetaBytesInterop<K
// file.length() > 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<K, KI, MKI extends MetaBytesInterop<K
}
public final long segmentBaseAddr(int segmentIndex) {
return ms.address() + msBytesOffset(segmentIndex);
return ms.address() + msBytesSegmentOffset(segmentIndex);
}
private long msBytesOffset(long segmentIndex) {
private long msBytesSegmentOffset(long segmentIndex) {
return segmentsOffset + segmentIndex * segmentSize;
}
@@ -456,36 +480,41 @@ public abstract class VanillaChronicleHash<K, KI, MKI extends MetaBytesInterop<K
}
/**
* Tier data lock doesn't yet need read-write levels and waits;
* VanillaReadWriteWithWaitsLockingStrategy is just the simplest locking strategy available
* Global mutable state lock doesn't yet need read-write levels and waits;
* Used the same locking strategy as in segment locks
* (VanillaReadWriteUpdateWithWaitsLockingStrategy) in order to simplify Chronicle Map
* specification (having only one kind of locks to specify and implement).
*/
static final LockingStrategy tierDataLockingStrategy =
VanillaReadWriteWithWaitsLockingStrategy.instance();
static final TryAcquireOperation<LockingStrategy> tierDataLockTryAcquireOperation =
static final LockingStrategy globalMutableStateLockingStrategy =
VanillaReadWriteUpdateWithWaitsLockingStrategy.instance();
static final TryAcquireOperation<LockingStrategy> globalMutableStateLockTryAcquireOperation =
TryAcquireOperations.lock();
static final
AcquisitionStrategy<LockingStrategy, RuntimeException> tierDataLockAcquisitionStrategy =
AcquisitionStrategy<LockingStrategy, RuntimeException>
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<K, KI, MKI extends MetaBytesInterop<K
}
public long allocateTier(int forSegmentIndex, int tier) {
tierDataLock();
globalMutableStateLock();
try {
long tiersInUse = tiersInUse();
if (tiersInUse == maxExtraTiers) {
@@ -532,14 +561,19 @@ public abstract class VanillaChronicleHash<K, KI, MKI extends MetaBytesInterop<K
long firstFreeTierIndex = firstFreeTierIndex();
if (firstFreeTierIndex > 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<K, KI, MKI extends MetaBytesInterop<K
}
}
} finally {
tierDataUnlock();
globalMutableStateUnlock();
}
}
@@ -555,18 +589,21 @@ public abstract class VanillaChronicleHash<K, KI, MKI extends MetaBytesInterop<K
long alreadyAllocatedBulks = extraAllocatedTierBulkCount();
mapTiers(alreadyAllocatedBulks);
extraAllocatedTierBulkCount(alreadyAllocatedBulks + 1);
linkFreeTiersList(alreadyAllocatedBulks);
linkNewlyAllocatedTiersIntoFreeTiersList(alreadyAllocatedBulks);
}
private void linkFreeTiersList(long alreadyAllocatedBulks) {
private void linkNewlyAllocatedTiersIntoFreeTiersList(long alreadyAllocatedBulks) {
long firstTierIndex = actualSegments + 1 +
alreadyAllocatedBulks * numberOfTiersInBulk;
firstFreeTierIndex(firstTierIndex);
Bytes bytes = tierBytes(firstTierIndex);
for (long tierIndex = firstTierIndex;
tierIndex < firstTierIndex + numberOfTiersInBulk - 1; tierIndex++) {
long tierBaseAddr = tierIndexToBaseAddr(tierIndex);
long tierDataAddr = tierBaseAddr + segmentHashLookupOuterSize;
TierData.nextTierIndex(tierDataAddr, tierIndex + 1);
long tierOffset = tierBytesOffset(tierIndex);
long tierCountersAreaOffset = tierOffset + segmentHashLookupOuterSize;
// zero out tier counters area, because newly mapped memory could be dirty
bytes.zeroOut(tierCountersAreaOffset, tierCountersAreaOffset + TIER_COUNTERS_AREA_SIZE);
TierCountersArea.nextTierIndex(bytes.address() + tierCountersAreaOffset, tierIndex + 1);
}
}
@@ -586,10 +623,10 @@ public abstract class VanillaChronicleHash<K, KI, MKI extends MetaBytesInterop<K
return tierBulkData(tierIndex).langBytes;
}
public long bytesOffset(long tierIndex) {
public long tierBytesOffset(long tierIndex) {
tierIndex -= 1;
if (tierIndex < actualSegments)
return msBytesOffset(tierIndex);
return msBytesSegmentOffset(tierIndex);
long extraTierIndex = tierIndex - actualSegments;
long bulkIndex = extraTierIndex >> log2NumberOfTiersInBulk;
if (bulkIndex >= tierBulkOffsets.size())
@@ -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 +
@@ -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<K, KI, MKI extends MetaBytesInterop<K, ? sup
* single value
*/
private long combine(long tierIndex, long pos) {
tierIndex -= 1;
if (tierIndex < actualSegments)
return (tierIndex << segmentIndexShift) | pos;
return combineExtraTier(tierIndex, pos);
long tierIndexMinusOne = tierIndex - 1;
if (tierIndexMinusOne < actualSegments)
return (tierIndexMinusOne << segmentIndexShift) | pos;
return combineExtraTier(tierIndexMinusOne, pos);
}
private long combineExtraTier(long tierIndex, long pos) {
long extraTierIndex = tierIndex - actualSegments;
private long combineExtraTier(long tierIndexMinusOne, long pos) {
long extraTierIndex = tierIndexMinusOne - actualSegments;
long tierOffsetWithinBulk = extraTierIndex & ((1L << log2NumberOfTiersInBulk) - 1);
return (tierOffsetWithinBulk << segmentIndexShift) | pos;
}
@@ -745,9 +745,10 @@ public class ReplicatedChronicleMap<K, KI, MKI extends MetaBytesInterop<K, ? sup
context.initSegmentIndex(tierIndexMinusOne);
} else {
long tierBaseAddr = tierIndexToBaseAddr(tierIndexMinusOne + 1);
long tierDataAddr = tierBaseAddr + segmentHashLookupOuterSize;
context.initSegmentIndex(TierData.segmentIndex(tierDataAddr));
int tier = TierData.tier(tierDataAddr);
long tierCountersAreaAddr = tierBaseAddr + segmentHashLookupOuterSize;
context.initSegmentIndex(
TierCountersArea.segmentIndex(tierCountersAreaAddr));
int tier = TierCountersArea.tier(tierCountersAreaAddr);
long tierIndex = actualSegments +
(iterationMainSegmentsAreaOrTierBulk << log2NumberOfTiersInBulk) +
tierIndexMinusOne + 1;