mirror of
https://github.com/OpenHFT/Chronicle-Map.git
synced 2021-09-13 01:26:12 +00:00
HCOLL-155 uncompleted, checked in order that Peter can review the changes so far of - chm.acquireUsingReadLocked and chm.acquireUsingWriteLocked
This commit is contained in:
@@ -843,7 +843,7 @@ public abstract class AbstractChronicleMapBuilder<K, V,
|
||||
statelessBuilder,
|
||||
entrySize(),
|
||||
kClass,
|
||||
vClass);
|
||||
vClass, putReturnsNull,removeReturnsNull);
|
||||
}
|
||||
|
||||
private VanillaChronicleMap<K, ?, ?, V, ?, ?> newMap() throws IOException {
|
||||
|
||||
@@ -23,6 +23,7 @@ import net.openhft.chronicle.hash.serialization.BytesReader;
|
||||
import net.openhft.lang.io.Bytes;
|
||||
import net.openhft.lang.io.serialization.BytesMarshaller;
|
||||
import net.openhft.lang.model.Byteable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.util.Map;
|
||||
@@ -100,7 +101,7 @@ public interface ChronicleMap<K, V> extends ConcurrentMap<K, V>, ChronicleHash {
|
||||
* @see #acquireUsing(Object, Object)
|
||||
* @see ChronicleMapBuilder#valueMarshaller(BytesMarshaller)
|
||||
*/
|
||||
V getUsing(K key, V usingValue);
|
||||
V getUsing(K key, V usingValue);
|
||||
|
||||
/**
|
||||
* Acquire a value for a key, creating if absent.
|
||||
@@ -137,6 +138,13 @@ public interface ChronicleMap<K, V> extends ConcurrentMap<K, V>, ChronicleHash {
|
||||
* @return value to which the given key is mapping after this call, either found or created
|
||||
* @see #getUsing(Object, Object)
|
||||
*/
|
||||
V acquireUsing(K key, V usingValue);
|
||||
public V acquireUsing(K key, V usingValue) ;
|
||||
|
||||
|
||||
// todo document
|
||||
SegmentLock acquireUsingReadLocked(@NotNull K key, @NotNull V usingValue);
|
||||
|
||||
// todo document
|
||||
SegmentLock acquireUsingWriteLocked(@NotNull K key, @NotNull V usingValue);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package net.openhft.chronicle.map;
|
||||
|
||||
/**
|
||||
* @author Rob Austin.
|
||||
*/
|
||||
public interface SegmentLock extends AutoCloseable {
|
||||
|
||||
/**
|
||||
* call this to unlock the segment
|
||||
*/
|
||||
void close();
|
||||
|
||||
}
|
||||
@@ -28,9 +28,9 @@ import java.util.Map;
|
||||
*/
|
||||
interface SharedSegment<K, V> {
|
||||
|
||||
void readLock();
|
||||
AutoCloseable readLock();
|
||||
|
||||
void writeLock();
|
||||
AutoCloseable writeLock();
|
||||
|
||||
Map.Entry<K, V> getEntry(long pos);
|
||||
|
||||
|
||||
@@ -65,6 +65,8 @@ class StatelessChronicleMap<K, V> implements ChronicleMap<K, V>, Closeable {
|
||||
private int maxEntrySize;
|
||||
private final Class<K> kClass;
|
||||
private final Class<V> vClass;
|
||||
private final boolean putReturnsNull;
|
||||
private final boolean removeReturnsNull;
|
||||
|
||||
|
||||
static enum EventId {
|
||||
@@ -74,8 +76,11 @@ class StatelessChronicleMap<K, V> implements ChronicleMap<K, V>, Closeable {
|
||||
IS_EMPTY,
|
||||
CONTAINS_KEY,
|
||||
CONTAINS_VALUE,
|
||||
GET, PUT,
|
||||
GET,
|
||||
PUT,
|
||||
PUT_WITHOUT_ACC,
|
||||
REMOVE,
|
||||
REMOVE_WITHOUT_ACC,
|
||||
CLEAR,
|
||||
KEY_SET,
|
||||
VALUES,
|
||||
@@ -86,6 +91,7 @@ class StatelessChronicleMap<K, V> implements ChronicleMap<K, V>, Closeable {
|
||||
REMOVE_WITH_VALUE,
|
||||
TO_STRING,
|
||||
PUT_ALL,
|
||||
PUT_ALL_WITHOUT_ACC,
|
||||
HASH_CODE
|
||||
}
|
||||
|
||||
@@ -93,12 +99,16 @@ class StatelessChronicleMap<K, V> implements ChronicleMap<K, V>, Closeable {
|
||||
|
||||
StatelessChronicleMap(final KeyValueSerializer<K, V> keyValueSerializer,
|
||||
final StatelessBuilder builder,
|
||||
int maxEntrySize, Class<K> kClass, Class<V> vClass) throws IOException {
|
||||
int maxEntrySize, Class<K> kClass, Class<V> vClass,
|
||||
boolean putReturnsNull,
|
||||
boolean removeReturnsNull) throws IOException {
|
||||
this.keyValueSerializer = keyValueSerializer;
|
||||
this.builder = builder;
|
||||
this.maxEntrySize = maxEntrySize;
|
||||
this.kClass = kClass;
|
||||
this.vClass = vClass;
|
||||
this.putReturnsNull = putReturnsNull;
|
||||
this.removeReturnsNull = removeReturnsNull;
|
||||
attemptConnect(builder.remoteAddress());
|
||||
|
||||
buffer = allocateDirect(maxEntrySize).order(ByteOrder.nativeOrder());
|
||||
@@ -242,7 +252,7 @@ class StatelessChronicleMap<K, V> implements ChronicleMap<K, V>, Closeable {
|
||||
|
||||
|
||||
public synchronized V putIfAbsent(K key, V value) {
|
||||
final long sizeLocation = writeEvent(PUT_IF_ABSENT);
|
||||
final long sizeLocation = writeEventAnSkip(PUT_IF_ABSENT);
|
||||
final ThreadLocalCopies local = keyValueSerializer.threadLocalCopies();
|
||||
writeKey(key, local);
|
||||
writeValue(value, local);
|
||||
@@ -255,7 +265,7 @@ class StatelessChronicleMap<K, V> implements ChronicleMap<K, V>, Closeable {
|
||||
if (key == null)
|
||||
throw new NullPointerException("key can not be null");
|
||||
|
||||
final long sizeLocation = writeEvent(REMOVE_WITH_VALUE);
|
||||
final long sizeLocation = writeEventAnSkip(REMOVE_WITH_VALUE);
|
||||
final ThreadLocalCopies local = keyValueSerializer.threadLocalCopies();
|
||||
writeKey((K) key, local);
|
||||
writeValue((V) value, local);
|
||||
@@ -269,7 +279,7 @@ class StatelessChronicleMap<K, V> implements ChronicleMap<K, V>, Closeable {
|
||||
if (key == null)
|
||||
throw new NullPointerException("key can not be null");
|
||||
|
||||
final long sizeLocation = writeEvent(REPLACE_WITH_OLD_AND_NEW_VALUE);
|
||||
final long sizeLocation = writeEventAnSkip(REPLACE_WITH_OLD_AND_NEW_VALUE);
|
||||
final ThreadLocalCopies local = keyValueSerializer.threadLocalCopies();
|
||||
writeKey(key, local);
|
||||
writeValue(oldValue, local);
|
||||
@@ -284,7 +294,7 @@ class StatelessChronicleMap<K, V> implements ChronicleMap<K, V>, Closeable {
|
||||
if (key == null)
|
||||
throw new NullPointerException("key can not be null");
|
||||
|
||||
final long sizeLocation = writeEvent(REPLACE);
|
||||
final long sizeLocation = writeEventAnSkip(REPLACE);
|
||||
final ThreadLocalCopies local = keyValueSerializer.threadLocalCopies();
|
||||
writeKey(key, local);
|
||||
writeValue(value, local);
|
||||
@@ -329,31 +339,23 @@ class StatelessChronicleMap<K, V> implements ChronicleMap<K, V>, Closeable {
|
||||
}
|
||||
|
||||
|
||||
public synchronized void putAll(Map<? extends K, ? extends V> map) {
|
||||
|
||||
final long sizeLocation = writeEvent(PUT_ALL);
|
||||
writeEntries(map);
|
||||
blockingFetch(sizeLocation);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public synchronized int hashCode() {
|
||||
return blockingFetch(writeEvent(HASH_CODE)).readInt();
|
||||
return blockingFetch(writeEventAnSkip(HASH_CODE)).readInt();
|
||||
}
|
||||
|
||||
public synchronized String toString() {
|
||||
return blockingFetch(writeEvent(TO_STRING)).readObject(String.class);
|
||||
return blockingFetch(writeEventAnSkip(TO_STRING)).readObject(String.class);
|
||||
}
|
||||
|
||||
|
||||
public synchronized boolean isEmpty() {
|
||||
return blockingFetch(writeEvent(IS_EMPTY)).readBoolean();
|
||||
return blockingFetch(writeEventAnSkip(IS_EMPTY)).readBoolean();
|
||||
}
|
||||
|
||||
|
||||
public synchronized boolean containsKey(Object key) {
|
||||
final long sizeLocation = writeEvent(CONTAINS_KEY);
|
||||
final long sizeLocation = writeEventAnSkip(CONTAINS_KEY);
|
||||
writeKey((K) key);
|
||||
|
||||
// get the data back from the server
|
||||
@@ -363,7 +365,7 @@ class StatelessChronicleMap<K, V> implements ChronicleMap<K, V>, Closeable {
|
||||
|
||||
|
||||
public synchronized boolean containsValue(Object value) {
|
||||
final long sizeLocation = writeEvent(CONTAINS_VALUE);
|
||||
final long sizeLocation = writeEventAnSkip(CONTAINS_VALUE);
|
||||
writeValue((V) value);
|
||||
|
||||
// get the data back from the server
|
||||
@@ -372,12 +374,12 @@ class StatelessChronicleMap<K, V> implements ChronicleMap<K, V>, Closeable {
|
||||
|
||||
|
||||
public synchronized long longSize() {
|
||||
return blockingFetch(writeEvent(LONG_SIZE)).readLong();
|
||||
return blockingFetch(writeEventAnSkip(LONG_SIZE)).readLong();
|
||||
}
|
||||
|
||||
|
||||
public synchronized V get(Object key) {
|
||||
final long sizeLocation = writeEvent(GET);
|
||||
final long sizeLocation = writeEventAnSkip(GET);
|
||||
final ThreadLocalCopies local = keyValueSerializer.threadLocalCopies();
|
||||
writeKey((K) key, local);
|
||||
|
||||
@@ -387,35 +389,102 @@ class StatelessChronicleMap<K, V> implements ChronicleMap<K, V>, Closeable {
|
||||
}
|
||||
|
||||
public synchronized V getUsing(K key, V usingValue) {
|
||||
throw new UnsupportedOperationException("getUsing is not supported for stateless clients");
|
||||
throw new UnsupportedOperationException("getUsing() is not supported for stateless " +
|
||||
"clients");
|
||||
}
|
||||
|
||||
|
||||
public synchronized V acquireUsing(K key, V usingValue) {
|
||||
throw new UnsupportedOperationException("getUsing is not supported for stateless clients");
|
||||
throw new UnsupportedOperationException("acquireUsing() is not supported for stateless " +
|
||||
"clients");
|
||||
}
|
||||
|
||||
public synchronized V put(K key, V value) {
|
||||
|
||||
final long sizeLocation = writeEvent(PUT);
|
||||
final ThreadLocalCopies local = keyValueSerializer.threadLocalCopies();
|
||||
writeKey(key, local);
|
||||
writeValue(value, local);
|
||||
|
||||
// get the data back from the server
|
||||
return readKey(sizeLocation, local);
|
||||
@Override
|
||||
public SegmentLock acquireUsingReadLocked(K key, V usingValue) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SegmentLock acquireUsingWriteLocked(K key, V usingValue) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
||||
public synchronized V remove(Object key) {
|
||||
|
||||
if (key == null)
|
||||
throw new NullPointerException("key can not be null");
|
||||
|
||||
final long sizeLocation = writeEvent(REMOVE);
|
||||
final long sizeLocation = (removeReturnsNull) ? writeEvent(REMOVE_WITHOUT_ACC) : writeEventAnSkip(REMOVE);
|
||||
|
||||
|
||||
writeKey((K) key);
|
||||
|
||||
// get the data back from the server
|
||||
return readKey(sizeLocation);
|
||||
if (removeReturnsNull) {
|
||||
sendWithoutAcc(bytes, sizeLocation);
|
||||
return null;
|
||||
} else
|
||||
// get the data back from the server
|
||||
return readKey(sizeLocation);
|
||||
}
|
||||
|
||||
|
||||
public synchronized V put(K key, V value) {
|
||||
|
||||
final ThreadLocalCopies local = keyValueSerializer.threadLocalCopies();
|
||||
|
||||
final long sizeLocation = (putReturnsNull) ? writeEvent(PUT_WITHOUT_ACC) : writeEventAnSkip(PUT);
|
||||
|
||||
writeKey(key, local);
|
||||
writeValue(value, local);
|
||||
|
||||
|
||||
if (putReturnsNull) {
|
||||
sendWithoutAcc(bytes, sizeLocation);
|
||||
return null;
|
||||
} else
|
||||
// get the data back from the server
|
||||
return readKey(sizeLocation, local);
|
||||
|
||||
}
|
||||
|
||||
|
||||
public synchronized void putAll(Map<? extends K, ? extends V> map) {
|
||||
|
||||
final long sizeLocation = (putReturnsNull) ? writeEvent(PUT_ALL_WITHOUT_ACC) : writeEventAnSkip(PUT_ALL);
|
||||
|
||||
writeEntries(map);
|
||||
|
||||
if (putReturnsNull)
|
||||
sendWithoutAcc(bytes, sizeLocation);
|
||||
else
|
||||
blockingFetch(sizeLocation);
|
||||
|
||||
}
|
||||
|
||||
|
||||
private void sendWithoutAcc(final Bytes bytes, long sizeLocation) {
|
||||
writeSizeAt(sizeLocation);
|
||||
long timeoutTime = builder.timeoutMs() + System.currentTimeMillis();
|
||||
try {
|
||||
for (; ; ) {
|
||||
|
||||
if (clientChannel == null)
|
||||
clientChannel = lazyConnect(builder.timeoutMs(), builder.remoteAddress());
|
||||
|
||||
try {
|
||||
|
||||
send(bytes, timeoutTime);
|
||||
return;
|
||||
} catch (java.nio.channels.ClosedChannelException | ClosedConnectionException e) {
|
||||
checkTimeout(timeoutTime);
|
||||
clientChannel = lazyConnect(builder.timeoutMs(), builder.remoteAddress());
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
close();
|
||||
throw new IORuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -534,13 +603,13 @@ class StatelessChronicleMap<K, V> implements ChronicleMap<K, V>, Closeable {
|
||||
public synchronized void clear() {
|
||||
|
||||
// get the data back from the server
|
||||
blockingFetch(writeEvent(CLEAR));
|
||||
blockingFetch(writeEventAnSkip(CLEAR));
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
public synchronized Collection<V> values() {
|
||||
final long sizeLocation = writeEvent(VALUES);
|
||||
final long sizeLocation = writeEventAnSkip(VALUES);
|
||||
|
||||
final long startTime = System.currentTimeMillis();
|
||||
final long transactionId = nextUniqueTransaction(startTime);
|
||||
@@ -575,7 +644,7 @@ class StatelessChronicleMap<K, V> implements ChronicleMap<K, V>, Closeable {
|
||||
|
||||
@NotNull
|
||||
public synchronized Set<Map.Entry<K, V>> entrySet() {
|
||||
final long sizeLocation = writeEvent(ENTRY_SET);
|
||||
final long sizeLocation = writeEventAnSkip(ENTRY_SET);
|
||||
|
||||
final long startTime = System.currentTimeMillis();
|
||||
final long transactionId = nextUniqueTransaction(startTime);
|
||||
@@ -623,7 +692,7 @@ class StatelessChronicleMap<K, V> implements ChronicleMap<K, V>, Closeable {
|
||||
|
||||
@NotNull
|
||||
public synchronized Set<K> keySet() {
|
||||
final long sizeLocation = writeEvent(KEY_SET);
|
||||
final long sizeLocation = writeEventAnSkip(KEY_SET);
|
||||
|
||||
final long startTime = System.currentTimeMillis();
|
||||
final long transactionId = nextUniqueTransaction(startTime);
|
||||
@@ -656,9 +725,7 @@ class StatelessChronicleMap<K, V> implements ChronicleMap<K, V>, Closeable {
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private long writeEvent(EventId event) {
|
||||
|
||||
buffer.clear();
|
||||
bytes.clear();
|
||||
|
||||
@@ -666,9 +733,28 @@ class StatelessChronicleMap<K, V> implements ChronicleMap<K, V>, Closeable {
|
||||
|
||||
bytes.write((byte) event.ordinal());
|
||||
long sizeLocation = markSizeLocation();
|
||||
|
||||
|
||||
return sizeLocation;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* skips for the transactionid
|
||||
*
|
||||
* @param event
|
||||
* @return
|
||||
*/
|
||||
private long writeEventAnSkip(EventId event) {
|
||||
|
||||
long sizeLocation = writeEvent(event);
|
||||
|
||||
// skips for the transaction id
|
||||
bytes.skip(SIZE_OF_TRANSACTIONID);
|
||||
return sizeLocation;
|
||||
}
|
||||
|
||||
|
||||
class Entry implements Map.Entry<K, V> {
|
||||
|
||||
final K key;
|
||||
@@ -828,7 +914,7 @@ class StatelessChronicleMap<K, V> implements ChronicleMap<K, V>, Closeable {
|
||||
}
|
||||
|
||||
|
||||
private ByteBufferBytes receive(int requiredNumberOfBytes, long timeoutTime) throws IOException {
|
||||
private Bytes receive(int requiredNumberOfBytes, long timeoutTime) throws IOException {
|
||||
|
||||
while (bytes.buffer().position() < requiredNumberOfBytes) {
|
||||
clientChannel.read(bytes.buffer());
|
||||
@@ -839,7 +925,7 @@ class StatelessChronicleMap<K, V> implements ChronicleMap<K, V>, Closeable {
|
||||
return bytes;
|
||||
}
|
||||
|
||||
private void send(final ByteBufferBytes out, long timeoutTime) throws IOException {
|
||||
private void send(final Bytes out, long timeoutTime) throws IOException {
|
||||
|
||||
buffer.limit((int) out.position());
|
||||
buffer.position(0);
|
||||
@@ -869,10 +955,16 @@ class StatelessChronicleMap<K, V> implements ChronicleMap<K, V>, Closeable {
|
||||
|
||||
}
|
||||
|
||||
|
||||
private void writeSizeAt(long locationOfSize) {
|
||||
final long size = bytes.position() - locationOfSize;
|
||||
bytes.writeInt(locationOfSize, (int) size - SIZE_OF_SIZE);
|
||||
}
|
||||
|
||||
|
||||
private long markSizeLocation() {
|
||||
final long position = bytes.position();
|
||||
bytes.skip(SIZE_OF_SIZE);
|
||||
bytes.skip(SIZE_OF_TRANSACTIONID);
|
||||
return position;
|
||||
}
|
||||
|
||||
|
||||
@@ -1261,6 +1261,16 @@ class StatelessServerConnector<K, V> {
|
||||
case ENTRY_SET:
|
||||
return entrySet(reader, writer);
|
||||
|
||||
case PUT_WITHOUT_ACC:
|
||||
return put(reader);
|
||||
|
||||
|
||||
case PUT_ALL_WITHOUT_ACC:
|
||||
return putAll(reader);
|
||||
|
||||
case REMOVE_WITHOUT_ACC:
|
||||
return remove(reader);
|
||||
|
||||
}
|
||||
|
||||
final long sizeLocation = reflectTransactionId(reader, writer);
|
||||
@@ -1484,6 +1494,20 @@ class StatelessServerConnector<K, V> {
|
||||
|
||||
}
|
||||
|
||||
|
||||
private Work put(Bytes reader) {
|
||||
if (MAP_SUPPORTS_BYTES) {
|
||||
map.put(reader);
|
||||
} else {
|
||||
final ThreadLocalCopies local = keyValueSerializer.threadLocalCopies();
|
||||
final K k = readKey(reader, local);
|
||||
final V v = readValue(reader, local);
|
||||
map.put(k, v);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Work put(Bytes reader, Bytes writer, final long sizeLocation) {
|
||||
if (MAP_SUPPORTS_BYTES) {
|
||||
map.put(reader, writer);
|
||||
@@ -1502,6 +1526,19 @@ class StatelessServerConnector<K, V> {
|
||||
return null;
|
||||
}
|
||||
|
||||
private Work remove(Bytes reader) {
|
||||
if (MAP_SUPPORTS_BYTES) {
|
||||
map.removeWithBytes(reader);
|
||||
} else {
|
||||
final ThreadLocalCopies local = keyValueSerializer.threadLocalCopies();
|
||||
final K key = readKey(reader, local);
|
||||
map.remove(key);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private Work remove(Bytes reader, Bytes writer, final long sizeLocation) {
|
||||
if (MAP_SUPPORTS_BYTES) {
|
||||
map.remove(reader, writer);
|
||||
@@ -1540,6 +1577,18 @@ class StatelessServerConnector<K, V> {
|
||||
return null;
|
||||
}
|
||||
|
||||
private Work putAll(Bytes reader) {
|
||||
|
||||
if (MAP_SUPPORTS_BYTES) {
|
||||
map.putAll(reader);
|
||||
} else {
|
||||
final ThreadLocalCopies local = keyValueSerializer.threadLocalCopies();
|
||||
map.putAll(readEntries(reader, local));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private Work clear(Bytes reader, Bytes writer, final long sizeLocation) {
|
||||
|
||||
@@ -1788,7 +1837,9 @@ class StatelessServerConnector<K, V> {
|
||||
final HashMap<K, V> result = new HashMap<K, V>();
|
||||
|
||||
for (long i = 0; i < numberOfEntries; i++) {
|
||||
result.put(readKey(reader, local), readValue(reader, local));
|
||||
K key = readKey(reader, local);
|
||||
V value = readValue(reader, local);
|
||||
result.put(key, value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,8 @@ import net.openhft.lang.io.serialization.impl.VanillaBytesMarshallerFactory;
|
||||
import net.openhft.lang.threadlocal.Provider;
|
||||
import net.openhft.lang.threadlocal.ThreadLocalCopies;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -48,6 +50,10 @@ import static net.openhft.lang.MemoryUnit.*;
|
||||
class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, KI>,
|
||||
V, VW, MVW extends MetaBytesWriter<V, VW>> extends AbstractMap<K, V>
|
||||
implements ChronicleMap<K, V>, Serializable {
|
||||
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(VanillaChronicleMap.class);
|
||||
|
||||
/**
|
||||
* Because DirectBitSet implementations couldn't find more than 64 continuous clear or set
|
||||
* bits.
|
||||
@@ -325,11 +331,12 @@ class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, KI>,
|
||||
}
|
||||
|
||||
@Override
|
||||
public V putIfAbsent(K key, V value) {
|
||||
public V putIfAbsent( K key, V value) {
|
||||
return put0(key, value, false);
|
||||
}
|
||||
|
||||
private V put0(K key, V value, boolean replaceIfPresent) {
|
||||
private V put0(@net.openhft.lang.model.constraints.NotNull K key, V value,
|
||||
boolean replaceIfPresent) {
|
||||
checkKey(key);
|
||||
checkValue(value);
|
||||
ThreadLocalCopies copies = keyInteropProvider.getCopies(null);
|
||||
@@ -354,11 +361,51 @@ class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, KI>,
|
||||
return lookupUsing(key, usingValue, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SegmentLock acquireUsingReadLocked(K key, V usingValue) {
|
||||
return this.acquireUsing(key, usingValue, LockType.READ_LOCK);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SegmentLock acquireUsingWriteLocked(K key, V usingValue) {
|
||||
return this.acquireUsing(key, usingValue, LockType.WRITE_LOCK);
|
||||
|
||||
//todo call put() on close of a on-heap value
|
||||
|
||||
//todo set dirty when using replication on
|
||||
}
|
||||
|
||||
|
||||
// todo remove
|
||||
@Override
|
||||
public V acquireUsing(K key, V usingValue) {
|
||||
return lookupUsing(key, usingValue, true);
|
||||
}
|
||||
|
||||
|
||||
private enum LockType {READ_LOCK, WRITE_LOCK}
|
||||
|
||||
SegmentLock acquireUsing(K key, V value, LockType lockTypeType) {
|
||||
checkKey(key);
|
||||
ThreadLocalCopies copies = keyInteropProvider.getCopies(null);
|
||||
KI keyInterop = keyInteropProvider.get(copies, originalKeyInterop);
|
||||
copies = metaKeyInteropProvider.getCopies(copies);
|
||||
MKI metaKeyInterop =
|
||||
metaKeyInteropProvider.get(copies, originalMetaKeyInterop, keyInterop, key);
|
||||
long hash = metaKeyInterop.hash(keyInterop, key);
|
||||
int segmentNum = getSegment(hash);
|
||||
long segmentHash = segmentHash(hash);
|
||||
|
||||
Segment segment = segments[segmentNum];
|
||||
SegmentLock lock = (lockTypeType == LockType.WRITE_LOCK) ? segment.writeLock() : segment.readLock();
|
||||
|
||||
|
||||
segment.acquireWithoutLock(copies, metaKeyInterop, keyInterop, key, value,
|
||||
segmentHash, false);
|
||||
return lock;
|
||||
|
||||
}
|
||||
|
||||
V lookupUsing(K key, V value, boolean create) {
|
||||
checkKey(key);
|
||||
ThreadLocalCopies copies = keyInteropProvider.getCopies(null);
|
||||
@@ -369,8 +416,13 @@ class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, KI>,
|
||||
long hash = metaKeyInterop.hash(keyInterop, key);
|
||||
int segmentNum = getSegment(hash);
|
||||
long segmentHash = segmentHash(hash);
|
||||
return segments[segmentNum].acquire(copies, metaKeyInterop, keyInterop, key, value,
|
||||
segmentHash, create);
|
||||
Segment segment = segments[segmentNum];
|
||||
|
||||
try (SegmentLock lock = create ? segment.writeLock() : segment.readLock()) {
|
||||
return segment.acquireWithoutLock(copies, metaKeyInterop, keyInterop, key, value,
|
||||
segmentHash, create);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -400,6 +452,7 @@ class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, KI>,
|
||||
return (entrySet != null) ? entrySet : (entrySet = new EntrySet());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @throws NullPointerException if the specified key is null
|
||||
*/
|
||||
@@ -418,6 +471,7 @@ class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, KI>,
|
||||
return removeIfValueIs(key, (V) value) != null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* removes ( if there exists ) an entry from the map, if the {@param key} and {@param
|
||||
* expectedValue} match that of a maps.entry. If the {@param expectedValue} equals null then (
|
||||
@@ -451,6 +505,7 @@ class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, KI>,
|
||||
return oldValue.equals(replaceIfValueIs(key, oldValue, newValue));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @throws NullPointerException if the specified key or value is null
|
||||
*/
|
||||
@@ -569,6 +624,10 @@ class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, KI>,
|
||||
throw new UnsupportedOperationException("todo");
|
||||
}
|
||||
|
||||
public void putAll(Bytes reader) {
|
||||
throw new UnsupportedOperationException("todo");
|
||||
}
|
||||
|
||||
long[] segmentSizes() {
|
||||
long[] sizes = new long[segments.length];
|
||||
for (int i = 0; i < segments.length; i++)
|
||||
@@ -576,6 +635,14 @@ class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, KI>,
|
||||
return sizes;
|
||||
}
|
||||
|
||||
public void put(Bytes reader) {
|
||||
throw new UnsupportedOperationException("todo");
|
||||
}
|
||||
|
||||
public void removeWithBytes(Bytes reader) {
|
||||
throw new UnsupportedOperationException("todo");
|
||||
}
|
||||
|
||||
// these methods should be package local, not public or private.
|
||||
class Segment implements SharedSegment {
|
||||
/*
|
||||
@@ -597,6 +664,19 @@ class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, KI>,
|
||||
private MultiMap hashLookup;
|
||||
private long nextPosToSearchFrom = 0L;
|
||||
|
||||
final SegmentLock readUnlock = new SegmentLock() {
|
||||
@Override
|
||||
public void close() {
|
||||
Segment.this.readUnlock();
|
||||
}
|
||||
};
|
||||
|
||||
final SegmentLock writeUnlock = new SegmentLock() {
|
||||
@Override
|
||||
public void close() {
|
||||
Segment.this.writeUnlock();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param index the index of this segment held by the map
|
||||
@@ -679,10 +759,10 @@ class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, KI>,
|
||||
}
|
||||
|
||||
|
||||
public void readLock() throws IllegalStateException {
|
||||
public SegmentLock readLock() throws IllegalStateException {
|
||||
while (true) {
|
||||
final boolean success = segmentHeader.tryRWReadLock(LOCK_OFFSET, lockTimeOutNS);
|
||||
if (success) return;
|
||||
if (success) return readUnlock;
|
||||
if (currentThread().isInterrupted()) {
|
||||
throw new IllegalStateException(new InterruptedException("Unable to obtain lock, interrupted"));
|
||||
} else {
|
||||
@@ -692,10 +772,10 @@ class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, KI>,
|
||||
}
|
||||
}
|
||||
|
||||
public void writeLock() throws IllegalStateException {
|
||||
public SegmentLock writeLock() throws IllegalStateException {
|
||||
while (true) {
|
||||
final boolean success = segmentHeader.tryRWWriteLock(LOCK_OFFSET, lockTimeOutNS);
|
||||
if (success) return;
|
||||
if (success) return writeUnlock;
|
||||
if (currentThread().isInterrupted()) {
|
||||
throw new IllegalStateException(new InterruptedException("Unable to obtain lock, interrupted"));
|
||||
} else {
|
||||
@@ -762,51 +842,44 @@ class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, KI>,
|
||||
return (int) (sizeInBytes / entrySize) + 1;
|
||||
}
|
||||
|
||||
V acquire(ThreadLocalCopies copies, MKI metaKeyInterop, KI keyInterop, K key, V usingValue,
|
||||
long hash2, boolean create) {
|
||||
V acquireWithoutLock(ThreadLocalCopies copies, MKI metaKeyInterop, KI keyInterop, K key, V usingValue,
|
||||
long hash2, boolean create) {
|
||||
V v = acquire0(copies, metaKeyInterop, keyInterop, key, usingValue, hash2, false);
|
||||
if (create && v == null)
|
||||
v = acquire0(copies, metaKeyInterop, keyInterop, key, usingValue, hash2, true);
|
||||
v = acquire0(copies, metaKeyInterop, keyInterop, key, usingValue, hash2, true);
|
||||
return v;
|
||||
}
|
||||
V acquire0(ThreadLocalCopies copies, MKI metaKeyInterop, KI keyInterop, K key, V usingValue,
|
||||
long hash2, boolean create) {
|
||||
if (create)
|
||||
writeLock();
|
||||
else
|
||||
readLock();
|
||||
try {
|
||||
long keySize = metaKeyInterop.size(keyInterop, key);
|
||||
MultiStoreBytes entry = acquireTmpBytes();
|
||||
long offset = searchKey(keyInterop, metaKeyInterop, key, keySize, hash2, entry,
|
||||
hashLookup);
|
||||
if (offset >= 0L) {
|
||||
return onKeyPresentOnAcquire(copies, key, usingValue, offset, entry);
|
||||
} else {
|
||||
if (!create)
|
||||
return null;
|
||||
if (defaultValueProvider != null) {
|
||||
V defaultValue = defaultValueProvider.get(key);
|
||||
copies = valueWriterProvider.getCopies(copies);
|
||||
VW valueWriter = valueWriterProvider.get(copies, originalValueWriter);
|
||||
copies = metaValueWriterProvider.getCopies(copies);
|
||||
MetaBytesWriter<V, VW> metaValueWriter = metaValueWriterProvider.get(
|
||||
copies, originalMetaValueWriter, valueWriter, defaultValue);
|
||||
|
||||
offset = putEntry(metaKeyInterop, keyInterop, key, keySize,
|
||||
metaValueWriter, valueWriter, defaultValue);
|
||||
} else {
|
||||
offset = putEntry(metaKeyInterop, keyInterop, key, keySize,
|
||||
prepareValueBytesAsWriter, null, key);
|
||||
}
|
||||
return onKeyAbsentOnAcquire(copies, key, keySize, usingValue, offset);
|
||||
V acquire0(ThreadLocalCopies copies, MKI metaKeyInterop, KI keyInterop, K key, V usingValue,
|
||||
long hash2, boolean create) {
|
||||
|
||||
|
||||
long keySize = metaKeyInterop.size(keyInterop, key);
|
||||
MultiStoreBytes entry = acquireTmpBytes();
|
||||
long offset = searchKey(keyInterop, metaKeyInterop, key, keySize, hash2, entry,
|
||||
hashLookup);
|
||||
if (offset >= 0L) {
|
||||
return onKeyPresentOnAcquire(copies, key, usingValue, offset, entry);
|
||||
} else {
|
||||
if (!create)
|
||||
return null;
|
||||
if (defaultValueProvider != null) {
|
||||
V defaultValue = defaultValueProvider.get(key);
|
||||
copies = valueWriterProvider.getCopies(copies);
|
||||
VW valueWriter = valueWriterProvider.get(copies, originalValueWriter);
|
||||
copies = metaValueWriterProvider.getCopies(copies);
|
||||
MetaBytesWriter<V, VW> metaValueWriter = metaValueWriterProvider.get(
|
||||
copies, originalMetaValueWriter, valueWriter, defaultValue);
|
||||
|
||||
offset = putEntry(metaKeyInterop, keyInterop, key, keySize,
|
||||
metaValueWriter, valueWriter, defaultValue);
|
||||
} else {
|
||||
offset = putEntry(metaKeyInterop, keyInterop, key, keySize,
|
||||
prepareValueBytesAsWriter, null, key);
|
||||
}
|
||||
} finally {
|
||||
if (create)
|
||||
writeUnlock();
|
||||
else
|
||||
readUnlock();
|
||||
return onKeyAbsentOnAcquire(copies, key, keySize, usingValue, offset);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
long searchKey(KI keyInterop, MKI metaKeyInterop, K key, long keySize, long hash2,
|
||||
@@ -1007,12 +1080,12 @@ class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, KI>,
|
||||
/**
|
||||
* Removes a key (or key-value pair) from the Segment.
|
||||
*
|
||||
* <p>The entry will only be removed if {@code expectedValue} equals to {@code null} or the value previously
|
||||
* corresponding to the specified key.
|
||||
* <p>The entry will only be removed if {@code expectedValue} equals to {@code null} or the
|
||||
* value previously corresponding to the specified key.
|
||||
*
|
||||
* @param hash2 a hash code related to the {@code keyBytes}
|
||||
* @return the value of the entry that was removed if the entry corresponding to the {@code keyBytes} exists and
|
||||
* {@link #removeReturnsNull} is {@code false}, {@code null} otherwise
|
||||
* @return the value of the entry that was removed if the entry corresponding to the {@code
|
||||
* keyBytes} exists and {@link #removeReturnsNull} is {@code false}, {@code null} otherwise
|
||||
*/
|
||||
V remove(ThreadLocalCopies copies, MKI metaKeyInterop, KI keyInterop, K key,
|
||||
V expectedValue, long hash2) {
|
||||
@@ -1068,8 +1141,9 @@ class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, KI>,
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the specified value for the key with the given value. {@code newValue} is set only if the existing
|
||||
* value corresponding to the specified key is equal to {@code expectedValue} or {@code expectedValue == null}.
|
||||
* Replaces the specified value for the key with the given value. {@code newValue} is set
|
||||
* only if the existing value corresponding to the specified key is equal to {@code
|
||||
* expectedValue} or {@code expectedValue == null}.
|
||||
*
|
||||
* @param hash2 a hash code related to the {@code keyBytes}
|
||||
* @return the replaced value or {@code null} if the value was not replaced
|
||||
@@ -1145,15 +1219,17 @@ class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, KI>,
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces value in existing entry. May cause entry relocation, because there may be not enough space for new
|
||||
* value in location already allocated for this entry.
|
||||
* Replaces value in existing entry. May cause entry relocation, because there may be not
|
||||
* enough space for new value in location already allocated for this entry.
|
||||
*
|
||||
* @param pos index of the first block occupied by the entry
|
||||
* @param offset relative offset of the entry in Segment bytes (before, i. e. including metaData)
|
||||
* @param offset relative offset of the entry in Segment bytes (before, i. e.
|
||||
* including metaData)
|
||||
* @param entry relative pointer in Segment bytes
|
||||
* @param valueSizePos relative position of value size in entry
|
||||
* @param entryEndAddr absolute address of the entry end
|
||||
* @return relative offset of the entry in Segment bytes after putting value (that may cause entry relocation)
|
||||
* @return relative offset of the entry in Segment bytes after putting value (that may cause
|
||||
* entry relocation)
|
||||
*/
|
||||
long putValue(long pos, long offset, NativeBytes entry, long valueSizePos,
|
||||
long entryEndAddr, ThreadLocalCopies copies, V value, Bytes valueBytes,
|
||||
|
||||
Reference in New Issue
Block a user