From 8e1b065f9c9552f9beb58ecef254228305a03d54 Mon Sep 17 00:00:00 2001 From: leventov Date: Sat, 16 May 2015 00:48:10 +0700 Subject: [PATCH] Commited event listening SPI / layers for efficient coding of logic for remote/local/replication queries at once --- pom.xml | 6 + .../net/openhft/chronicle/hash/HashEntry.java | 48 +++++ .../openhft/chronicle/hash/KeyContext.java | 23 ++- .../net/openhft/chronicle/hash/Value.java | 97 ++++++++++ .../chronicle/map/ChronicleMapBuilder.java | 35 ++++ .../openhft/chronicle/map/MapAbsentEntry.java | 30 +++ .../map/MapAbsentEntryOperations.java | 33 ++++ .../net/openhft/chronicle/map/MapEntry.java | 52 +++++ .../chronicle/map/MapEntryOperations.java | 61 ++++++ .../openhft/chronicle/map/MapKeyContext.java | 1 + .../net/openhft/chronicle/map/MapMethods.java | 179 ++++++++++++++++++ .../chronicle/map/MapMethodsSupport.java | 51 +++++ .../chronicle/map/MapQueryContext.java | 55 ++++++ .../openhft/chronicle/map/ReturnValue.java | 26 +++ 14 files changed, 687 insertions(+), 10 deletions(-) create mode 100644 src/main/java/net/openhft/chronicle/hash/HashEntry.java create mode 100644 src/main/java/net/openhft/chronicle/hash/Value.java create mode 100644 src/main/java/net/openhft/chronicle/map/MapAbsentEntry.java create mode 100644 src/main/java/net/openhft/chronicle/map/MapAbsentEntryOperations.java create mode 100644 src/main/java/net/openhft/chronicle/map/MapEntry.java create mode 100644 src/main/java/net/openhft/chronicle/map/MapEntryOperations.java create mode 100644 src/main/java/net/openhft/chronicle/map/MapMethods.java create mode 100644 src/main/java/net/openhft/chronicle/map/MapMethodsSupport.java create mode 100644 src/main/java/net/openhft/chronicle/map/MapQueryContext.java create mode 100644 src/main/java/net/openhft/chronicle/map/ReturnValue.java diff --git a/pom.xml b/pom.xml index 8bf76b713..eaeaaf1a8 100755 --- a/pom.xml +++ b/pom.xml @@ -99,6 +99,11 @@ chronicle-bytes + + net.openhft + chronicle-algorithms + + @@ -215,6 +220,7 @@ net.openhft.chronicle.hash.serialization.internal:net.openhft.xstream:net.openhft.xstream.*:net.openhft.chronicle.hash.impl.* + -Xdoclint:none diff --git a/src/main/java/net/openhft/chronicle/hash/HashEntry.java b/src/main/java/net/openhft/chronicle/hash/HashEntry.java new file mode 100644 index 000000000..bb62ee277 --- /dev/null +++ b/src/main/java/net/openhft/chronicle/hash/HashEntry.java @@ -0,0 +1,48 @@ +/* + * Copyright 2015 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.chronicle.hash; + +import net.openhft.chronicle.map.MapEntry; +import net.openhft.chronicle.map.MapEntryOperations; +import org.jetbrains.annotations.NotNull; + +/** + * Access to the present {@code ChronicleHash} entry. + * + * @param type of the key in {@code ChronicleHash} + * @see MapEntryOperations + */ +public interface HashEntry { + /** + * Returns the entry key. + */ + @NotNull Value key(); + + /** + * Removes the entry from the {@code ChronicleHash}. Returns {@code true} if the remove was + * successful, {@code false} if it failed for any reason. + * + *

This method if the default implementation for {@link MapEntryOperations#remove(MapEntry)}, + * which might be customized over the default. + * + * @throws IllegalStateException if some locking/state conditions required to perform remove + * operation are not met + */ + boolean defaultRemove(); +} diff --git a/src/main/java/net/openhft/chronicle/hash/KeyContext.java b/src/main/java/net/openhft/chronicle/hash/KeyContext.java index 2f5f97047..e15a10679 100644 --- a/src/main/java/net/openhft/chronicle/hash/KeyContext.java +++ b/src/main/java/net/openhft/chronicle/hash/KeyContext.java @@ -1,17 +1,19 @@ /* - * Copyright 2014 Higher Frequency Trading http://www.higherfrequencytrading.com + * Copyright 2015 Higher Frequency Trading * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * http://www.higherfrequencytrading.com * - * http://www.apache.org/licenses/LICENSE-2.0 + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package net.openhft.chronicle.hash; @@ -34,6 +36,7 @@ import java.util.ConcurrentModificationException; * * @param key type */ +@Deprecated public interface KeyContext extends AutoCloseable, InterProcessReadWriteUpdateLock { /** diff --git a/src/main/java/net/openhft/chronicle/hash/Value.java b/src/main/java/net/openhft/chronicle/hash/Value.java new file mode 100644 index 000000000..2653bb6b3 --- /dev/null +++ b/src/main/java/net/openhft/chronicle/hash/Value.java @@ -0,0 +1,97 @@ +/* + * Copyright 2015 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.chronicle.hash; + +import net.openhft.chronicle.bytes.*; +import net.openhft.chronicle.algo.hashing.LongHashFunction; +import org.jetbrains.annotations.Nullable; + +/** + * Dual bytes/object access to keys/values/elements. + * + *

Bytes access: {@link #access()} + {@link #handle()} + {@link #size()}. + * + *

Object access: {@link #get()}. + * + * In most cases, each particular value wraps either some object or some bytes. Object is marshalled + * to bytes lazily on demand, and bytes are lazily deserialized to object, accordingly. + * + * @param type of the accessed objects + * @param type of the handle for bytes access + */ +public interface Value { + + ReadAccess access(); + + T handle(); + + long offset(); + + long size(); + + default long hash(LongHashFunction f) { + return f.hash(handle(), access(), offset(), size()); + } + + default boolean equivalent(ReadAccess access, T2 handle, long offset) { + return Access.equivalent(access(), handle(), offset(), access, handle, offset, size()); + } + + default > boolean equivalent( + Accessor accessor, S source, long index) { + return equivalent(accessor.access(source), accessor.handle(source), + accessor.offset(source, index)); + } + + default void writeTo(WriteAccess access, T2 handle, long offset) { + Access.copy(access(), handle(), offset(), access, handle, offset, size()); + } + + default void writeTo(StreamingDataOutput output) { + long offset = output.accessPositionOffset(); + output.skip(size()); + writeTo(output.access(), output.accessHandle(), offset); + } + + default > void writeTo( + Accessor accessor, S source, long index) { + writeTo(accessor.access(source), accessor.handle(source), accessor.offset(source, index)); + } + + /** + * Returns "cached" object, generally not eligible for using outside some context, or a block, + * synchronized with locks, or lambda, etc. + * + * If the {@code Value} is object wrapper -- this method just returns this object. + */ + V get(); + + /** + * Reads the object from the value's bytes, trying to reuse the given object + * (might be {@code null}). + */ + V getUsing(@Nullable V usingInstance); + + static boolean bytesEquivalent(Value v1, Value v2) { + if (v1.size() != v2.size()) + return false; + return Access.equivalent(v1.access(), v1.handle(), v1.offset(), + v2.access(), v2.handle(), v2.offset(), v1.size()); + } +} diff --git a/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilder.java b/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilder.java index 2798b6000..c5c163ba4 100755 --- a/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilder.java +++ b/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilder.java @@ -1481,5 +1481,40 @@ public final class ChronicleMapBuilder implements return (int) Math.min(Math.max(2L, entries() >> 10), Math.min(64, maxChunksPerEntry())); } + + /** + * Inject your SPI code around basic {@code ChronicleMap}'s operations with present entries: + * removing entries and replacing the entries' value. + * + * This affects behaviour of ordinary map.put(), map.remove(), etc. calls, as well as removes + * and replacing values during iterations, remote map calls and + * internal replication operations. + */ + public ChronicleMapBuilder customizeEntryOperations( + MapEntryOperations customEntryOperations) { + throw new UnsupportedOperationException(); + } + + /** + * Inject your SPI code around basic operation of inserting a new entry into the map. + * + * This affects behaviour of ordinary map.put(), map.computeIfAbsent(), etc. calls, + * as well as similar remote map calls and internal replication operation. + */ + public ChronicleMapBuilder customizeAbsentEntryOperations( + MapAbsentEntryOperations customAbsentEntryOperations) { + throw new UnsupportedOperationException(); + } + + /** + * Inject your SPI around logic of all {@code ChronicleMap}'s operations with individual keys: + * from {@link ChronicleMap#containsKey} to {@link ChronicleMap#acquireUsing} and + * {@link ChronicleMap#merge}. + * + * This affects behaviour of ordinary map calls, as well as remote calls. + */ + public ChronicleMapBuilder customizeMapMethods(MapMethods customMapMethods) { + throw new UnsupportedOperationException(); + } } diff --git a/src/main/java/net/openhft/chronicle/map/MapAbsentEntry.java b/src/main/java/net/openhft/chronicle/map/MapAbsentEntry.java new file mode 100644 index 000000000..fc9bcbe5a --- /dev/null +++ b/src/main/java/net/openhft/chronicle/map/MapAbsentEntry.java @@ -0,0 +1,30 @@ +/* + * Copyright 2015 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.chronicle.map; + +import net.openhft.chronicle.hash.Value; + +public interface MapAbsentEntry { + + Value absentKey(); + + boolean defaultInsert(Value value); + + Value defaultValue(); +} diff --git a/src/main/java/net/openhft/chronicle/map/MapAbsentEntryOperations.java b/src/main/java/net/openhft/chronicle/map/MapAbsentEntryOperations.java new file mode 100644 index 000000000..824262b4e --- /dev/null +++ b/src/main/java/net/openhft/chronicle/map/MapAbsentEntryOperations.java @@ -0,0 +1,33 @@ +/* + * Copyright 2015 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.chronicle.map; + +import net.openhft.chronicle.hash.Value; +import org.jetbrains.annotations.NotNull; + +public interface MapAbsentEntryOperations { + + default boolean insert(@NotNull MapAbsentEntry absentEntry, Value value) { + return absentEntry.defaultInsert(value); + } + + default Value defaultValue(@NotNull MapAbsentEntry absentEntry) { + return absentEntry.defaultValue(); + } +} diff --git a/src/main/java/net/openhft/chronicle/map/MapEntry.java b/src/main/java/net/openhft/chronicle/map/MapEntry.java new file mode 100644 index 000000000..7a064b273 --- /dev/null +++ b/src/main/java/net/openhft/chronicle/map/MapEntry.java @@ -0,0 +1,52 @@ + + +/* + * Copyright 2015 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.chronicle.map; + +import net.openhft.chronicle.hash.HashEntry; +import net.openhft.chronicle.hash.Value; + +/** + * Access to the present {@link ChronicleMap} entry. + * + * @param type of the key in {@code ChronicleMap} + * @param type of the value in {@code ChronicleMap} + * + * @see MapEntryOperations + */ +public interface MapEntry extends HashEntry { + /** + * Returns the entry value. + */ + Value value(); + + /** + * Replaces the entry's value with the new one. Returns {@code true} if the replace operation + * was successful, {@code false} if it failed for any reason. + * + *

This method if the default implementation for {@link MapEntryOperations#replaceValue}, + * which might be customized over the default. + * + * @throws IllegalStateException if some locking/state conditions required to perform replace + * operation are not met + */ + boolean defaultReplaceValue(Value newValue); +} + diff --git a/src/main/java/net/openhft/chronicle/map/MapEntryOperations.java b/src/main/java/net/openhft/chronicle/map/MapEntryOperations.java new file mode 100644 index 000000000..dd61abdd6 --- /dev/null +++ b/src/main/java/net/openhft/chronicle/map/MapEntryOperations.java @@ -0,0 +1,61 @@ +/* + * Copyright 2015 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.chronicle.map; + +import net.openhft.chronicle.hash.Value; +import org.jetbrains.annotations.NotNull; + +/** + * SPI interface for customizing "low-level" modification operations on the present + * {@link ChronicleMap} entry. + * + * @param type of the key in {@code ChronicleMap} + * @param type of the value in {@code ChronicleMap} + */ +public interface MapEntryOperations { + /** + * Removes the given entry from the {@code ChronicleHash}. Returns {@code true} if the remove + * was successful, {@code false} if it failed for any reason. + * + * @implNote simply delegates to {@link MapEntry#defaultRemove()}. + * + * @param entry the entry to remove + * @throws IllegalStateException if some locking/state conditions required to perform remove + * operation are not met + */ + default boolean remove(@NotNull MapEntry entry) { + return entry.defaultRemove(); + } + + + /** + * Replaces the given entry's value with the new one. Returns {@code true} if the replace + * operation was successful, {@code false} if it failed for any reason. + * + * @implNote simply delegates to {@link MapEntry#defaultReplaceValue(Value) + * entry.defaultReplace(newValue)} + * + * @param entry the entry to replace the value in + * @throws IllegalStateException if some locking/state conditions required to perform replace + * operation are not met + */ + default boolean replaceValue(@NotNull MapEntry entry, Value newValue) { + return entry.defaultReplaceValue(newValue); + } +} diff --git a/src/main/java/net/openhft/chronicle/map/MapKeyContext.java b/src/main/java/net/openhft/chronicle/map/MapKeyContext.java index a2da94c6a..1917124e8 100644 --- a/src/main/java/net/openhft/chronicle/map/MapKeyContext.java +++ b/src/main/java/net/openhft/chronicle/map/MapKeyContext.java @@ -18,6 +18,7 @@ package net.openhft.chronicle.map; import net.openhft.chronicle.hash.KeyContext; +@Deprecated public interface MapKeyContext extends KeyContext { long valueOffset(); diff --git a/src/main/java/net/openhft/chronicle/map/MapMethods.java b/src/main/java/net/openhft/chronicle/map/MapMethods.java new file mode 100644 index 000000000..190d00896 --- /dev/null +++ b/src/main/java/net/openhft/chronicle/map/MapMethods.java @@ -0,0 +1,179 @@ +/* + * Copyright 2015 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.chronicle.map; + +import net.openhft.chronicle.hash.Value; + +import java.util.function.BiFunction; +import java.util.function.Function; + +import static net.openhft.chronicle.hash.Value.bytesEquivalent; + +/** + * SPI interface for customizing behaviour of the specific Map's methods with individual keys. + */ +public interface MapMethods { + + default boolean containsKey(MapQueryContext q) { + return q.entry() != null; + } + + /** + * Backing {@link ChronicleMap#get}, {@link ChronicleMap#getUsing} and + * {@link ChronicleMap#getOrDefault} methods. + */ + default void get(MapQueryContext q, ReturnValue returnValue) { + MapMethodsSupport.returnCurrentValueIfPresent(q, returnValue); + } + + default boolean put(MapQueryContext q, + Value value, ReturnValue returnValue) { + // We cannot read the previous value under read lock, because then we will need + // to release the read lock -> acquire write lock, the value might be updated in + // between, that will break ConcurrentMap.put() atomicity guarantee. So, we acquire + // update lock from the start: + q.updateLock().lock(); + MapEntry entry = q.entry(); + if (entry != null) { + returnValue.returnValue(entry.value()); + return q.replaceValue(entry, value); + } else { + return q.insert(q.absentEntry(), value); + } + } + + default boolean putIfAbsent(MapQueryContext q, + Value value, ReturnValue returnValue) { + if (MapMethodsSupport.tryReturnCurrentValueIfPresent(q, returnValue)) + return false; + // Key is absent + return q.insert(q.absentEntry(), value); + } + + default boolean acquireUsing(MapQueryContext q, ReturnValue returnValue) { + if (MapMethodsSupport.tryReturnCurrentValueIfPresent(q, returnValue)) + return false; + // Key is absent + Value defaultValue = q.defaultValue(q.absentEntry()); + boolean inserted = q.insert(q.absentEntry(), defaultValue); + returnValue.returnValue(defaultValue); + return inserted; + } + + default boolean remove(MapQueryContext q, ReturnValue returnValue) { + // We cannot read the previous value under read lock, because then we will need + // to release the read lock -> acquire write lock, the value might be updated in + // between, that will break ConcurrentMap.remove() atomicity guarantee. So, we acquire + // update lock from the start: + q.updateLock().lock(); + MapEntry entry = q.entry(); + if (entry != null) { + returnValue.returnValue(entry.value()); + return q.remove(entry); + } else { + return false; + } + } + + default boolean remove(MapQueryContext q, Value value) { + // remove(key, value) should find the entry & remove most of the time, + // so don't try to check key presence and value equivalence under read lock first, + // as in putIfAbsent()/acquireUsing(), start with update lock: + q.updateLock().lock(); + MapEntry entry = q.entry(); + return entry != null && + bytesEquivalent(entry.value(), value) && + q.remove(entry); + } + + default boolean replace(MapQueryContext q, + Value value, ReturnValue returnValue) { + // replace(key, value) should find the key & put the value most of the time, + // so don't try to check key presence under read lock first, + // as in putIfAbsent()/acquireUsing(), start with update lock: + q.updateLock().lock(); + MapEntry entry = q.entry(); + if (entry != null) { + returnValue.returnValue(entry.value()); + return q.replaceValue(entry, value); + } else { + return false; + } + } + + default boolean replace(MapQueryContext q, + Value oldValue, Value newValue) { + // replace(key, old, new) should find the entry & put new value most of the time, + // so don't try to check key presence and value equivalence under read lock first, + // as in putIfAbsent()/acquireUsing(), start with update lock: + q.updateLock().lock(); + MapEntry entry = q.entry(); + return entry != null && + bytesEquivalent(((MapEntry) entry).value(), oldValue) && + q.replaceValue(entry, newValue); + } + + default boolean compute(MapQueryContext q, + BiFunction remappingFunction, + Function> newValueObjectToValue, + ReturnValue returnValue) { + q.updateLock().lock(); + MapEntry entry = q.entry(); + V oldValue = entry != null ? entry.value().get() : null; + V newValue = remappingFunction.apply(q.queriedKey().get(), oldValue); + if (newValue != null) { + Value newValueValue = newValueObjectToValue.apply(newValue); + boolean updated; + if (entry != null) { + updated = q.replaceValue(entry, newValueValue); + } else { + updated = q.insert(q.absentEntry(), newValueValue); + } + returnValue.returnValue(newValueValue); + return updated; + } else if (entry != null) { + return q.remove(entry); + } else { + return false; + } + } + + default boolean merge(MapQueryContext q, Value value, + BiFunction remappingFunction, + Function> newValueObjectToValue, + ReturnValue returnValue) { + q.updateLock().lock(); + Value newValueValue; + boolean updated; + MapEntry entry = q.entry(); + if (entry != null) { + V oldValue = entry.value().get(); + V newValue = remappingFunction.apply(oldValue, value.get()); + if (newValue == null) + return q.remove(entry); + newValueValue = newValueObjectToValue.apply(newValue); + updated = q.replaceValue(entry, newValueValue); + } else { + newValueValue = value; + updated = q.insert(q.absentEntry(), newValueValue); + } + returnValue.returnValue(newValueValue); + return updated; + } +} diff --git a/src/main/java/net/openhft/chronicle/map/MapMethodsSupport.java b/src/main/java/net/openhft/chronicle/map/MapMethodsSupport.java new file mode 100644 index 000000000..07662d599 --- /dev/null +++ b/src/main/java/net/openhft/chronicle/map/MapMethodsSupport.java @@ -0,0 +1,51 @@ +/* + * Copyright 2015 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.chronicle.map; + +final class MapMethodsSupport { + + static void returnCurrentValueIfPresent( + MapQueryContext q, ReturnValue returnValue) { + MapEntry entry = q.entry(); + if (entry != null) + returnValue.returnValue(entry.value()); + } + + static boolean tryReturnCurrentValueIfPresent( + MapQueryContext q, ReturnValue returnValue) { + if (q.readLock().tryLock()) { + MapEntry entry = q.entry(); + if (entry != null) { + returnValue.returnValue(entry.value()); + return true; + } + // Key is absent + q.readLock().unlock(); + } + q.updateLock().lock(); + MapEntry entry = q.entry(); + if (entry != null) { + returnValue.returnValue(entry.value()); + return true; + } + return false; + } + + private MapMethodsSupport() {} +} diff --git a/src/main/java/net/openhft/chronicle/map/MapQueryContext.java b/src/main/java/net/openhft/chronicle/map/MapQueryContext.java new file mode 100644 index 000000000..c969e256b --- /dev/null +++ b/src/main/java/net/openhft/chronicle/map/MapQueryContext.java @@ -0,0 +1,55 @@ +/* + * Copyright 2015 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.chronicle.map; + +import net.openhft.chronicle.hash.Value; +import net.openhft.chronicle.hash.locks.InterProcessReadWriteUpdateLock; +import org.jetbrains.annotations.Nullable; + +/** + * The context of Map operations with individual keys (most: get(), put(), etc., opposed + * to bulk operations). + * + * @param + * @param + */ +public interface MapQueryContext extends InterProcessReadWriteUpdateLock, + MapEntryOperations, MapAbsentEntryOperations { + + /** + * Returns the queried key as a value. + */ + Value queriedKey(); + + /** + * Returns the entry access object, if the entry with the queried key is present + * in the map, returns {@code null} is the entry is absent. Might acquire + * {@link #readLock} before searching for the key, if the context is not locked yet. + */ + @Nullable + MapEntry entry(); + + /** + * Returns the special absent entry object, if the entry with the queried key + * is absent in the map, returns {@code null}, if the entry is present. Might + * acquire {@link #readLock} before searching for the key, if the context is not locked yet. + */ + @Nullable + MapAbsentEntry absentEntry(); +} diff --git a/src/main/java/net/openhft/chronicle/map/ReturnValue.java b/src/main/java/net/openhft/chronicle/map/ReturnValue.java new file mode 100644 index 000000000..913570cad --- /dev/null +++ b/src/main/java/net/openhft/chronicle/map/ReturnValue.java @@ -0,0 +1,26 @@ +/* + * Copyright 2015 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.chronicle.map; + +import net.openhft.chronicle.hash.Value; +import org.jetbrains.annotations.NotNull; + +public interface ReturnValue { + void returnValue(@NotNull Value value); +}