mirror of
https://github.com/OpenHFT/Chronicle-Map.git
synced 2021-09-13 01:26:12 +00:00
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:
+7
-1
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user