HCOLL-234 fixing CHMUseCaseTest

This commit is contained in:
Rob Austin
2014-12-09 11:27:08 +00:00
parent 13e9610a73
commit 95b21ba2f8
8 changed files with 350 additions and 207 deletions
@@ -37,14 +37,16 @@ public final class ChronicleMapBuilder<K, V> implements ChronicleMapBuilderI<K,
final ChronicleMapBuilderI<K, V> delegate;
/**
* Returns a new {@code ChronicleMapBuilder} instance which is able to {@linkplain #create() create} maps
* with the specified key and value classes.
* Returns a new {@code ChronicleMapBuilder} instance which is able to {@linkplain #create()
* create} maps with the specified key and value classes.
*
* <p>{@code ChronicleMapBuilder} analyzes provided key and value classes and automatically chooses the
* most specific internal builder.
* <p>{@code ChronicleMapBuilder} analyzes provided key and value classes and automatically
* chooses the most specific internal builder.
*
* @param keyClass class object used to infer key type and discover it's properties via reflection
* @param valueClass class object used to infer value type and discover it's properties via reflection
* @param keyClass class object used to infer key type and discover it's properties via
* reflection
* @param valueClass class object used to infer value type and discover it's properties via
* reflection
* @param <K> key type of the maps, created by the returned builder
* @param <V> value type of the maps, created by the returned builder
* @return a new builder for the given key and value classes
@@ -67,6 +69,26 @@ public final class ChronicleMapBuilder<K, V> implements ChronicleMapBuilderI<K,
return new ChronicleMapBuilder<K, V>(builder);
}
// todo - work in progress this will be changed me not today, so don't refactor this !
public static <T> T newInstance(Class<T> interfaceClass, boolean isKey) {
if (interfaceClass.isEnum())
return isKey ? DataValueClasses.newDirectReference(interfaceClass) : DataValueClasses
.newDirectInstance(interfaceClass);
else if (!offHeapReference(interfaceClass) && !interfaceClass.isInterface() &&
!builtInType(interfaceClass)) {
// if (())
// interfaceClass = DataValueClasses.directClassFor(interfaceClass);
return DataValueClasses.newInstance(interfaceClass);
}
return DataValueClasses.newDirectInstance(interfaceClass);
}
static boolean builtInType(Class clazz) {
return clazz.getClassLoader() == Class.class.getClassLoader();
}
@@ -83,9 +105,9 @@ public final class ChronicleMapBuilder<K, V> implements ChronicleMapBuilderI<K,
* {@inheritDoc} With respect to {@linkplain #entryAndValueAlignment(Alignment) alignment}.
*
* <p>Note that the actual entrySize will be aligned to 4 (default {@linkplain
* #entryAndValueAlignment(Alignment) entry alignment}). I. e. if you set entry size to 30, and entry
* alignment is set to {@link Alignment#OF_4_BYTES}, the actual entry size will be 32 (30 aligned to 4
* bytes).
* #entryAndValueAlignment(Alignment) entry alignment}). I. e. if you set entry size to 30, and
* entry alignment is set to {@link Alignment#OF_4_BYTES}, the actual entry size will be 32 (30
* aligned to 4 bytes).
*
* @see #entryAndValueAlignment(Alignment) // * @see #entries(long)
*/
@@ -96,16 +118,16 @@ public final class ChronicleMapBuilder<K, V> implements ChronicleMapBuilderI<K,
}
/**
* Configures alignment strategy of address in memory of entries and independently of address in memory of
* values within entries in ChronicleMaps, created by this builder.
* Configures alignment strategy of address in memory of entries and independently of address in
* memory of values within entries in ChronicleMaps, created by this builder.
*
* <p>Useful when values of the map are updated intensively, particularly fields with volatile access,
* because it doesn't work well if the value crosses cache lines. Also, on some (nowadays rare)
* architectures any misaligned memory access is more expensive than aligned.
* <p>Useful when values of the map are updated intensively, particularly fields with volatile
* access, because it doesn't work well if the value crosses cache lines. Also, on some
* (nowadays rare) architectures any misaligned memory access is more expensive than aligned.
*
* <p>Note that {@linkplain #entrySize(int) entry size} will be aligned according to this alignment. I. e.
* if you set {@code entrySize(20)} and {@link Alignment#OF_8_BYTES}, actual entry size will be 24 (20
* aligned to 8 bytes).
* <p>Note that {@linkplain #entrySize(int) entry size} will be aligned according to this
* alignment. I. e. if you set {@code entrySize(20)} and {@link Alignment#OF_8_BYTES}, actual
* entry size will be 24 (20 aligned to 8 bytes).
*
* <p>Default is {@link Alignment#OF_4_BYTES} for Byteable values.
*
@@ -119,11 +141,11 @@ public final class ChronicleMapBuilder<K, V> implements ChronicleMapBuilderI<K,
}
/**
* {@inheritDoc} Also, it overrides any previous {@link #prepareValueBytesOnAcquire} configuration to this
* {@code ChronicleMapBuilder}.
* {@inheritDoc} Also, it overrides any previous {@link #prepareValueBytesOnAcquire}
* configuration to this {@code ChronicleMapBuilder}.
*
* <p>By default, the default value is not specified, default {@linkplain #prepareValueBytesOnAcquire
* prepare value bytes routine} is specified instead.
* <p>By default, the default value is not specified, default {@linkplain
* #prepareValueBytesOnAcquire prepare value bytes routine} is specified instead.
*
* @see #defaultValueProvider(DefaultValueProvider)
* @see #prepareValueBytesOnAcquire(PrepareValueBytes)
@@ -135,15 +157,16 @@ public final class ChronicleMapBuilder<K, V> implements ChronicleMapBuilderI<K,
}
/**
* Configures the procedure which is called on the bytes, which later the returned value is pointing to,
* if the key is absent, on {@link ChronicleMap#acquireUsing(Object, Object) acquireUsing()} call on maps,
* created by this builder. See {@link PrepareValueBytes} for more information.
* Configures the procedure which is called on the bytes, which later the returned value is
* pointing to, if the key is absent, on {@link ChronicleMap#acquireUsing(Object, Object)
* acquireUsing()} call on maps, created by this builder. See {@link PrepareValueBytes} for more
* information.
*
* <p>The default preparation callback zeroes out the value bytes.
*
* @param prepareValueBytes what to do with the value bytes before assigning them into the {@link
* net.openhft.lang.model.Byteable} value to return from {@code acquireUsing()}
* call
* @param prepareValueBytes what to do with the value bytes before assigning them into the
* {@link net.openhft.lang.model.Byteable} value to return from {@code
* acquireUsing()} call
* @return this builder back
* @see PrepareValueBytes
* @see #defaultValue(Object)
@@ -168,16 +191,17 @@ public final class ChronicleMapBuilder<K, V> implements ChronicleMapBuilderI<K,
}
/**
* Configures the optimal number of bytes, taken by serialized form of values, put into maps, created by
* this builder. If value size is always the same, call {@link #constantValueSizeBySample(Object)} method
* instead of this one.
* Configures the optimal number of bytes, taken by serialized form of values, put into maps,
* created by this builder. If value size is always the same, call {@link
* #constantValueSizeBySample(Object)} method instead of this one.
*
* <p>If value is a boxed primitive type, i. e. if value size is known statically, it is automatically
* accounted and shouldn't be specified by user.
* <p>If value is a boxed primitive type, i. e. if value size is known statically, it is
* automatically accounted and shouldn't be specified by user.
*
* <p>If value size varies moderately, specify the size higher than average, but lower than the maximum
* possible, to minimize average memory overuse. If value size varies in a wide range, it's better to use
* {@linkplain #entrySize(int) entry size} in "chunk" mode and configure it directly.
* <p>If value size varies moderately, specify the size higher than average, but lower than the
* maximum possible, to minimize average memory overuse. If value size varies in a wide range,
* it's better to use {@linkplain #entrySize(int) entry size} in "chunk" mode and configure it
* directly.
*
* @param valueSize number of bytes, taken by serialized form of values
* @return this {@code ChronicleMapOnHeapUpdatableBuilder} back
@@ -192,15 +216,16 @@ public final class ChronicleMapBuilder<K, V> implements ChronicleMapBuilderI<K,
}
/**
* Configures the constant number of bytes, taken by serialized form of values, put into maps, created by
* this builder. This is done by providing the {@code sampleValue}, all values should take the same number
* of bytes in serialized form, as this sample object.
* Configures the constant number of bytes, taken by serialized form of values, put into maps,
* created by this builder. This is done by providing the {@code sampleValue}, all values should
* take the same number of bytes in serialized form, as this sample object.
*
* <p>If values are of boxed primitive type or {@link net.openhft.lang.model.Byteable} subclass, i. e. if
* value size is known statically, it is automatically accounted and this method shouldn't be called.
* <p>If values are of boxed primitive type or {@link net.openhft.lang.model.Byteable} subclass,
* i. e. if value size is known statically, it is automatically accounted and this method
* shouldn't be called.
*
* <p>If value size varies, method {@link #valueSize(int)} or {@link #entrySize(int)} should be called
* instead of this one.
* <p>If value size varies, method {@link #valueSize(int)} or {@link #entrySize(int)} should be
* called instead of this one.
*
* @param sampleValue the sample value
* @return this builder back
@@ -233,8 +258,8 @@ public final class ChronicleMapBuilder<K, V> implements ChronicleMapBuilderI<K,
/**
* {@inheritDoc}
*
* @throws IllegalStateException if custom value marshaller is specified or value class is not either
* {@code BytesMarshallable} or {@code Externalizable}
* @throws IllegalStateException if custom value marshaller is specified or value class is not
* either {@code BytesMarshallable} or {@code Externalizable}
*/
@Override
@@ -246,8 +271,8 @@ public final class ChronicleMapBuilder<K, V> implements ChronicleMapBuilderI<K,
/**
* {@inheritDoc}
*
* <p>By default, default value provider is not specified, {@link #defaultValue(Object) default value} is
* specified instead.
* <p>By default, default value provider is not specified, {@link #defaultValue(Object) default
* value} is specified instead.
*
* @see #defaultValue(Object)
*/
@@ -258,8 +283,8 @@ public final class ChronicleMapBuilder<K, V> implements ChronicleMapBuilderI<K,
}
/**
* Configures the {@code BytesMarshaller} used to serialize/deserialize values to/from off-heap memory in
* maps, created by this builder. See <a href="https://github.com/OpenHFT/Chronicle-Map#serialization">the
* Configures the {@code BytesMarshaller} used to serialize/deserialize values to/from off-heap
* memory in maps, created by this builder. See <a href="https://github.com/OpenHFT/Chronicle-Map#serialization">the
* section about serialization in ChronicleMap manual</a> for more information.
*
* @param valueMarshaller the marshaller used to serialize values
@@ -277,20 +302,21 @@ public final class ChronicleMapBuilder<K, V> implements ChronicleMapBuilderI<K,
}
/**
* Configures the marshallers, used to serialize/deserialize values to/from off-heap memory in maps,
* created by this builder. See <a href="https://github.com/OpenHFT/Chronicle-Map#serialization">the
* Configures the marshallers, used to serialize/deserialize values to/from off-heap memory in
* maps, created by this builder. See <a href="https://github.com/OpenHFT/Chronicle-Map#serialization">the
* section about serialization in ChronicleMap manual</a> for more information.
*
* <p>Configuring marshalling this way results to a little bit more compact in-memory layout of the map,
* comparing to a single interface configuration: {@link #valueMarshaller(BytesMarshaller)}.
* <p>Configuring marshalling this way results to a little bit more compact in-memory layout of
* the map, comparing to a single interface configuration: {@link #valueMarshaller(BytesMarshaller)}.
*
* <p>Passing {@link net.openhft.chronicle.hash.serialization.BytesInterop} instead of plain {@link
* net.openhft.chronicle.hash.serialization.BytesWriter} is, of cause, possible, but currently pointless
* for values.
* <p>Passing {@link net.openhft.chronicle.hash.serialization.BytesInterop} instead of plain
* {@link net.openhft.chronicle.hash.serialization.BytesWriter} is, of cause, possible, but
* currently pointless for values.
*
* @param valueWriter the new value object &rarr; {@link net.openhft.lang.io.Bytes} writer (interop)
* @param valueWriter the new value object &rarr; {@link net.openhft.lang.io.Bytes} writer
* (interop) strategy
* @param valueReader the new {@link net.openhft.lang.io.Bytes} &rarr; value object reader
* strategy
* @param valueReader the new {@link net.openhft.lang.io.Bytes} &rarr; value object reader strategy
* @return this builder back
* @see #valueMarshaller(BytesMarshaller)
*/
@@ -301,19 +327,21 @@ public final class ChronicleMapBuilder<K, V> implements ChronicleMapBuilderI<K,
}
/**
* Configures the marshaller used to serialize actual value sizes to off-heap memory in maps, created by
* this builder.
* Configures the marshaller used to serialize actual value sizes to off-heap memory in maps,
* created by this builder.
*
* <p>Default value size marshaller is so-called {@linkplain net.openhft.chronicle.hash.serialization.SizeMarshallers#stopBit()
* stop bit encoding marshalling}. If {@linkplain #constantValueSizeBySample(Object) constant value size}
* is configured, or defaulted if the value type is always constant and {@code ChronicleHashBuilder}
* implementation knows about it, this configuration takes no effect, because a special {@link
* net.openhft.chronicle.hash.serialization.SizeMarshaller} implementation, which doesn't actually do any
* marshalling, and just returns the known constant size on {@link net.openhft.chronicle.hash.serialization.SizeMarshaller#readSize(
*net.openhft.lang.io.Bytes)} calls, is used instead of any {@code SizeMarshaller} configured using this
* method.
* stop bit encoding marshalling}. If {@linkplain #constantValueSizeBySample(Object) constant
* value size} is configured, or defaulted if the value type is always constant and {@code
* ChronicleHashBuilder} implementation knows about it, this configuration takes no effect,
* because a special {@link net.openhft.chronicle.hash.serialization.SizeMarshaller}
* implementation, which doesn't actually do any marshalling, and just returns the known
* constant size on {@link net.openhft.chronicle.hash.serialization.SizeMarshaller#readSize(
*net.openhft.lang.io.Bytes)} calls, is used instead of any {@code SizeMarshaller} configured
* using this method.
*
* @param valueSizeMarshaller the new marshaller, used to serialize actual value sizes to off-heap memory
* @param valueSizeMarshaller the new marshaller, used to serialize actual value sizes to
* off-heap memory
* @return this builder back
*/
@Override
@@ -498,13 +526,14 @@ public final class ChronicleMapBuilder<K, V> implements ChronicleMapBuilderI<K,
}
/**
* @param bootstrapOnlyLocalEntries if set to true - when a new node joins a TCP replication grid, the new
* node will be populated with data, only for the nodes that created that
* data. Otherwise, all the nodes will publish all the data they have (
* potentially swamping the new node with duplicates ) however this does
* guarantee that all the data is replicated over to the new node, and is
* useful especially in the case that the originating node is not
* currently running.
* @param bootstrapOnlyLocalEntries if set to true - when a new node joins a TCP replication
* grid, the new node will be populated with data, only for the
* nodes that created that data. Otherwise, all the nodes will
* publish all the data they have ( potentially swamping the
* new node with duplicates ) however this does guarantee that
* all the data is replicated over to the new node, and is
* useful especially in the case that the originating node is
* not currently running.
*/
public ChronicleMapBuilder<K, V> bootstrapOnlyLocalEntries(boolean bootstrapOnlyLocalEntries) {
delegate.bootstrapOnlyLocalEntries(bootstrapOnlyLocalEntries);
@@ -512,11 +541,13 @@ public final class ChronicleMapBuilder<K, V> implements ChronicleMapBuilderI<K,
}
public ChronicleMapBuilderI<K, V> checkSerializedValues() {
throw new UnsupportedOperationException("Not implemented");
// throw new UnsupportedOperationException("Not implemented");
return this;
}
public ChronicleMapBuilder<K, V> disableOversizedEntries(boolean disableOversizedEntries) {
// throw an exception rather than use oversized entries to test the size is as expected.
throw new UnsupportedOperationException("Not implemented");
// throw new UnsupportedOperationException("Not implemented");
return this;
}
}
@@ -21,8 +21,11 @@ package net.openhft.chronicle.map;
/**
* @author Rob Austin.
*/
public interface ReadContext<K,V> extends Context<K,V> {
public interface ReadContext<K, V> extends Context<K, V> {
/**
* @returns true if the entry was present
*/
boolean present();
}
@@ -654,7 +654,7 @@ final class ReplicatedChronicleMap<K, KI, MKI extends MetaBytesInterop<K, ? supe
MKBI metaKeyInterop, KBI keyInterop, KB key, long keySize,
InstanceOrBytesToInstance<KB, K> toKey,
ReadValue<RV> readValue, RV usingValue, InstanceOrBytesToInstance<RV, V> toValue,
long hash2, boolean create) {
long hash2, boolean create, MutableLockedEntry lock) {
segmentStateNotNullImpliesCopiesNotNull(copies, segmentState);
SegmentState localSegmentState = segmentState;
try {
@@ -755,9 +755,19 @@ final class ReplicatedChronicleMap<K, KI, MKI extends MetaBytesInterop<K, ? supe
}
entry = localSegmentState.tmpBytes;
}
return createEntryOnAcquire(copies, localSegmentState,
RV result =createEntryOnAcquire(copies, localSegmentState,
metaKeyInterop, keyInterop, key, keySize, toKey,
readValue, usingValue, toValue, entry);
// notify the context that the entry was created
if (lock instanceof WriteLocked)
((WriteLocked) lock).wasPresent(true);
return result;
} finally {
if (segmentState == null && localSegmentState != null)
localSegmentState.close();
@@ -68,8 +68,8 @@ class StatelessChronicleMap<K, V> implements ChronicleMap<K, V>, Closeable, Clon
private volatile ByteBuffer outBuffer;
private volatile ByteBufferBytes outBytes;
private volatile ByteBuffer inBuffer;
private volatile ByteBufferBytes inBytes;
private ByteBuffer inBuffer;
private ByteBufferBytes inBytes;
@NotNull
private final ReaderWithSize<K> keyReaderWithSize;
@@ -35,7 +35,7 @@ import net.openhft.chronicle.hash.serialization.internal.*;
import net.openhft.lang.collection.DirectBitSet;
import net.openhft.lang.collection.SingleThreadedDirectBitSet;
import net.openhft.lang.io.*;
import net.openhft.lang.io.serialization.JDKZObjectSerializer;
import net.openhft.lang.io.serialization.JDKObjectSerializer;
import net.openhft.lang.io.serialization.impl.VanillaBytesMarshallerFactory;
import net.openhft.lang.threadlocal.Provider;
import net.openhft.lang.threadlocal.StatefulCopyable;
@@ -220,7 +220,7 @@ class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, ? super KI>,
final long createMappedStoreAndSegments(File file) throws IOException {
return createMappedStoreAndSegments(new MappedStore(file, FileChannel.MapMode.READ_WRITE,
sizeInBytes(), JDKZObjectSerializer.INSTANCE));
sizeInBytes(), JDKObjectSerializer.INSTANCE));
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
@@ -521,7 +521,7 @@ class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, ? super KI>,
DelegatingMetaBytesInterop.<Bytes, BytesInterop<Bytes>>instance(),
BytesBytesInterop.INSTANCE, key, keySizeMarshaller.readSize(key),
keyBytesToInstance, readValueToOutputBytes, null, outputValueBytesToInstance,
false);
false, null);
}
private V lookupUsing(K key, V usingValue, boolean create) {
@@ -532,7 +532,7 @@ class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, ? super KI>,
metaKeyInteropProvider.get(copies, originalMetaKeyInterop, keyInterop, key);
long keySize = metaKeyInterop.size(keyInterop, key);
return lookupUsing(copies, null, metaKeyInterop, keyInterop, key, keySize, keyIdentity(),
this, usingValue, valueIdentity(), create);
this, usingValue, valueIdentity(), create, null);
}
private <KB, KBI, MKBI extends MetaBytesInterop<KB, ? super KBI>, RV>
@@ -540,30 +540,73 @@ class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, ? super KI>,
MKBI metaKeyInterop, KBI keyInterop, KB key, long keySize,
InstanceOrBytesToInstance<KB, K> toKey,
ReadValue<RV> readValue, RV usingValue, InstanceOrBytesToInstance<RV, V> toValue,
boolean create) {
boolean create, final MutableLockedEntry lock) {
long hash = metaKeyInterop.hash(keyInterop, key);
int segmentNum = getSegment(hash);
long segmentHash = segmentHash(hash);
Segment segment = segments[segmentNum];
return segment.acquire(copies, segmentState,
metaKeyInterop, keyInterop, key, keySize, toKey,
readValue, usingValue, toValue, segmentHash, create);
readValue, usingValue, toValue, segmentHash, create, lock);
}
@Override
public <R> R mapForKey(K key, @NotNull Function<? super V, R> function) {
try (ReadContext<K, V> entry = lookupUsing(key, null,
LockType.READ_LOCK, false)) {
false, false, LockType.READ_LOCK)) {
return entry.present() ? function.apply(entry.value()) : null;
}
}
@Override
public <R> R updateForKey(K key, @NotNull Mutator<? super V, R> mutator) {
try (Context<K, V> entry = lookupUsing(key, null,
LockType.WRITE_LOCK, false)) {
return mutator.update(entry.value());
if (!(CharSequence.class.isAssignableFrom(vClass))) {
try (WriteContext<K, V> entry = lookupUsing(key, null,
false, false, LockType.WRITE_LOCK)) {
if (entry.value() == null)
return null;
if (!entry.wasPresent())
return null;
return mutator.update(entry.value());
}
}
// special case - spoke to peter about this and he wants to handle strings as a
// special case
CharSequence using;
if (vClass.equals(CharSequence.class))
using = new StringBuilder();
else
using = null;
try (WriteContext<K, V> entry = lookupUsing(key, (V) using,
false, using == null, LockType.WRITE_LOCK)) {
CharSequence value = (CharSequence) entry.value();
if (entry.value() == null || value.length() == 0)
return null;
final R result = mutator.update(entry.value());
if (value instanceof StringBuilder) {
StringBuilder using1 = (StringBuilder) using;
using1.setLength(0);
using1.append((CharSequence) result);
} else
((MutableLockedEntry) entry).value(result);
return result;
}
}
@Override
@@ -737,17 +780,17 @@ class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, ? super KI>,
@NotNull
@Override
public final WriteContext<K, V> acquireUsingLocked(@NotNull K key, @NotNull V usingValue) {
return lookupUsing(key, usingValue, LockType.WRITE_LOCK, true);
return lookupUsing(key, usingValue, true, true, LockType.WRITE_LOCK);
}
@NotNull
@Override
public final ReadContext<K, V> getUsingLocked(@NotNull K key, @NotNull V usingValue) {
return lookupUsing(key, usingValue, LockType.READ_LOCK, true);
return lookupUsing(key, usingValue, true, false, LockType.READ_LOCK);
}
private <T extends Context> T lookupUsing(
K key, V usingValue, LockType lockType, boolean mustReuseValue) {
K key, V usingValue, boolean mustReuseValue, final boolean create, final LockType lockType) {
checkKey(key);
ThreadLocalCopies copies = SegmentState.getCopies(null);
@@ -769,7 +812,7 @@ class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, ? super KI>,
V v = segment.acquireWithoutLock(copies, segmentState,
metaKeyInterop, keyInterop, key, keySize, keyIdentity(),
this, usingValue, valueIdentity(),
segmentHash, lockType == LockType.WRITE_LOCK);
segmentHash, create, lock);
checkReallyUsingValue(usingValue, mustReuseValue, v);
@@ -1592,15 +1635,15 @@ class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, ? super KI>,
<KB, KBI, MKBI extends MetaBytesInterop<KB, ? super KBI>, RV>
RV acquire(@Nullable ThreadLocalCopies copies, final @Nullable SegmentState segmentState,
MKBI metaKeyInterop, KBI keyInterop, KB key, long keySize,
InstanceOrBytesToInstance<KB, K> toKey,
ReadValue<RV> readValue, RV usingValue, InstanceOrBytesToInstance<RV, V> toValue,
long hash2, boolean create) {
MKBI metaKeyInterop, KBI keyInterop, KB key, long keySize,
InstanceOrBytesToInstance<KB, K> toKey,
ReadValue<RV> readValue, RV usingValue, InstanceOrBytesToInstance<RV, V> toValue,
long hash2, boolean create, MutableLockedEntry lock) {
readLock(null);
try {
return acquireWithoutLock(copies, segmentState,
metaKeyInterop, keyInterop, key, keySize, toKey,
readValue, usingValue, toValue, hash2, create);
readValue, usingValue, toValue, hash2, create, lock);
} finally {
if (segmentState != null)
segmentState.close();
@@ -1614,7 +1657,7 @@ class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, ? super KI>,
MKBI metaKeyInterop, KBI keyInterop, KB key, long keySize,
InstanceOrBytesToInstance<KB, K> toKey,
ReadValue<RV> readValue, RV usingValue, InstanceOrBytesToInstance<RV, V> toValue,
long hash2, boolean create) {
long hash2, boolean create, MutableLockedEntry lock) {
segmentStateNotNullImpliesCopiesNotNull(copies, segmentState);
SegmentState localSegmentState = segmentState;
try {
@@ -1648,9 +1691,16 @@ class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, ? super KI>,
}
entry = localSegmentState.tmpBytes;
}
return createEntryOnAcquire(copies, localSegmentState,
RV result = createEntryOnAcquire(copies, localSegmentState,
metaKeyInterop, keyInterop, key, keySize, toKey,
readValue, usingValue, toValue, entry);
// notify the context that the entry was created
if (lock instanceof WriteLocked)
((WriteLocked) lock).wasPresent(true);
return result;
} finally {
if (segmentState == null && localSegmentState != null)
localSegmentState.close();
@@ -2587,7 +2637,7 @@ class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, ? super KI>,
}
}
private static abstract class MutableLockedEntry<
static abstract class MutableLockedEntry<
K, KI, MKI extends MetaBytesInterop<K, ? super KI>,
V, VI, MVI extends MetaBytesInterop<V, ? super VI>> implements Context<K, V> {
final SegmentState segmentState;
@@ -2602,6 +2652,7 @@ class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, ? super KI>,
private K key;
private V value;
boolean present;
MutableLockedEntry(SegmentState segmentState) {
this.segmentState = segmentState;
@@ -2620,8 +2671,10 @@ class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, ? super KI>,
}
void value(V value) {
this.present = value != null;
this.value = value;
}
}
private static class ReadLocked<K, KI, MKI extends MetaBytesInterop<K, ? super KI>,
@@ -2629,40 +2682,46 @@ class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, ? super KI>,
extends MutableLockedEntry<K, KI, MKI, V, VI, MVI>
implements ReadContext<K, V> {
private boolean present;
ReadLocked(SegmentState segmentState) {
super(segmentState);
}
void value(V value) {
this.present = value != null;
super.value(value);
}
/**
* @return if the value is not null
*/
@Override
public boolean present() {
return present;
}
@Override
public void close() {
segmentState.close();
segment.readUnlock();
}
/**
* @return if the value is not null
*/
public boolean present() {
return present;
}
}
private static abstract class WriteLocked<K, KI, MKI extends MetaBytesInterop<K, ? super KI>,
static abstract class WriteLocked<K, KI, MKI extends MetaBytesInterop<K, ? super KI>,
V, VI, MVI extends MetaBytesInterop<V, ? super VI>>
extends MutableLockedEntry<K, KI, MKI, V, VI, MVI>
implements WriteContext<K, V> {
private boolean wasPresent;
WriteLocked(SegmentState segmentState) {
super(segmentState);
}
/**
* @return if the entry was created
*/
public boolean wasPresent() {
return wasPresent;
}
public void wasPresent(boolean wasPresent) {
this.wasPresent = wasPresent;
}
}
private static class NativeWriteLocked<K, KI, MKI extends MetaBytesInterop<K, ? super KI>,
@@ -2673,6 +2732,7 @@ class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, ? super KI>,
super(segmentState);
}
@Override
public void close() {
segmentState.close();
@@ -2715,6 +2775,7 @@ class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, ? super KI>,
segment.writeUnlock();
}
@Override
public void dontPutOnClose() {
putOnClose = false;
@@ -30,9 +30,14 @@ public interface WriteContext<K, V> extends Context<K, V> {
void dontPutOnClose();
/**
* this is simular by more efficient than calling map.remove("key") as the entry is already available to
* the WriteContext
* this is similar by more efficient than calling map.remove("key") as the entry is already
* available to the WriteContext
*/
void removeEntry();
/**
* @returns true if the entry was previously present
*/
boolean wasPresent();
}