mirror of
https://github.com/OpenHFT/Chronicle-Map.git
synced 2021-09-13 01:26:12 +00:00
HCOLL-318 added many documentation and examples; Empowerted ReplicableEntry interface, and changed Map/SetRemoteOperations contracts to allow implementing more agile reconciliation strategies
This commit is contained in:
@@ -2,7 +2,11 @@ package net.openhft.chronicle.hash;
|
||||
|
||||
import net.openhft.chronicle.hash.locks.InterProcessLock;
|
||||
import net.openhft.chronicle.hash.locks.InterProcessReadWriteUpdateLock;
|
||||
import net.openhft.chronicle.map.MapEntryOperations;
|
||||
import net.openhft.chronicle.map.MapMethods;
|
||||
import net.openhft.chronicle.map.MapQueryContext;
|
||||
import net.openhft.chronicle.set.SetEntryOperations;
|
||||
import net.openhft.chronicle.set.SetQueryContext;
|
||||
|
||||
/**
|
||||
* Context of {@link ChronicleHash} operations with <i>individual keys</i>.
|
||||
@@ -21,10 +25,11 @@ import net.openhft.chronicle.map.MapMethods;
|
||||
* try (ExternalMapQueryContext<K, V, ?> q = map.queryContext(key)) {
|
||||
* // q.entry(), checks if the entry is present in the map, and acquires
|
||||
* // the read lock for that.
|
||||
* if (q.entry() != null) {
|
||||
* MapEntry<K, V> entry = q.entry();
|
||||
* if (entry != null) {
|
||||
* // Tries to acquire the write lock to perform modification,
|
||||
* // but this is an illegal upgrade: read -> write, throws IllegalMonitorStateException
|
||||
* q.remove(q.entry());
|
||||
* q.remove(entry);
|
||||
* }
|
||||
* }}</pre>
|
||||
* So, to workaround this, you should acquire the {@linkplain #updateLock() update lock},
|
||||
@@ -33,9 +38,9 @@ import net.openhft.chronicle.map.MapMethods;
|
||||
* // CORRECT
|
||||
* try (ExternalMapQueryContext<K, V, ?> q = map.queryContext(key)) {
|
||||
* q.updateLock().lock(); // acquire the update lock before checking the entry presence.
|
||||
* if (q.entry() != null) {
|
||||
* q.remove(q.entry());
|
||||
* }
|
||||
* MapEntry<K, V> entry = q.entry();
|
||||
* if (entry != null)
|
||||
* q.remove(entry);
|
||||
* }}</pre>
|
||||
* </li>
|
||||
* <li>You want to try to acquire some lock heuristically, in order to improve total {@code
|
||||
@@ -46,14 +51,14 @@ import net.openhft.chronicle.map.MapMethods;
|
||||
* // For acquireUsing(), it is assumed to be very probable, that the entry is already
|
||||
* // present in the map, so we will perform the whole acquireUsing() without exclusive locking
|
||||
* if (q.readLock().tryLock()) {
|
||||
* MapEntry<?, V> entry = q.entry();
|
||||
* MapEntry<K, V> entry = q.entry();
|
||||
* if (entry != null) {
|
||||
* // Entry is present, return
|
||||
* returnValue.returnValue(entry.value());
|
||||
* return;
|
||||
* }
|
||||
* // Key is absent
|
||||
* // Need to unlock, to lock to update lock later. Direct updrage is forbidden.
|
||||
* // Need to unlock, to lock to update lock later. Direct upgrade is forbidden.
|
||||
* q.readLock().unlock();
|
||||
* }
|
||||
* // We are here, either if we:
|
||||
@@ -63,7 +68,7 @@ import net.openhft.chronicle.map.MapMethods;
|
||||
* // 2) Seen the entry is absent under the read lock. This means we need to insert
|
||||
* // the default value into the map. that requires update-level access as well
|
||||
* q.updateLock().lock();
|
||||
* MapEntry<?, V> entry = q.entry();
|
||||
* MapEntry<K, V> entry = q.entry();
|
||||
* if (entry != null) {
|
||||
* // Entry is present, return
|
||||
* returnValue.returnValue(entry.value());
|
||||
@@ -74,7 +79,7 @@ import net.openhft.chronicle.map.MapMethods;
|
||||
* returnValue.returnValue(q.entry().value());
|
||||
* }}</pre></li>
|
||||
* <li>If the default {@link InterProcessLock#lock()} policy of trying to acquire the
|
||||
* lock for some time, and then throw {@code RuntimeException}, or need custom timeout:
|
||||
* lock for some time, and then throw {@code RuntimeException}, or need a custom timeout:
|
||||
* <pre>{@code
|
||||
* try (ExternalHashQueryContext<K> q = hash.queryContext(key)) {
|
||||
* if (q.writeLock().tryLock(5, TimeUnit.SECONDS)) {
|
||||
@@ -84,9 +89,53 @@ import net.openhft.chronicle.map.MapMethods;
|
||||
* }
|
||||
* }}</pre></li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>{@code HashQueryContext} defines the common pattern for working with {@code ChronicleHash}
|
||||
* contexts: it has a pair of methods, {@link #entry()} and {@link #absentEntry()}, at any moment
|
||||
* one of them returns an (absent) entry context object, another - {@code null}, depending on the
|
||||
* presence of the {@linkplain #queriedKey() queried key} in the {@code ChronicleHash}. Thus,
|
||||
* block of code that uses {@code HashQueryContext} usually has an if-else statement,
|
||||
* with "then" branch for dealing with the present entry, {@code else} branch for dealing with
|
||||
* the absent entry, or vise-versa. For example: <pre>{@code
|
||||
* interface Point {
|
||||
* double getX();
|
||||
* void setX(double x);
|
||||
* double addX(double xAdd);
|
||||
*
|
||||
* double getY();
|
||||
* void setY(double y);
|
||||
* double addY(double yAdd);
|
||||
* }
|
||||
*
|
||||
* <K> Point movePoint(ChronicleMap<K, Point> map, K key, double xMove, double yMove,
|
||||
* Point using) {
|
||||
* // Moves existing point by [xMove, yMove], if absent - assumes the default point is [0, 0].
|
||||
* // Returns the resulting point
|
||||
* try (ExternalMapQueryContext<K, Point, ?> q = map.queryContext(key)) {
|
||||
* Point offHeapPoint;
|
||||
* q.updateLock().lock();
|
||||
* MapEntry<K, Point> entry = q.entry();
|
||||
* if (entry != null) {
|
||||
* // Key is present
|
||||
* offHeapPoint = entry.value().getUsing(using);
|
||||
* } else {
|
||||
* // Key is absent
|
||||
* q.insert(q.absentEntry(), q.defaultValue(q.absentEntry()));
|
||||
* offHeapPoint = q.entry().value().getUsing(using);
|
||||
* }
|
||||
* offHeapPoint.addX(xMove);
|
||||
* offHeapPoint.addY(yMove);
|
||||
* return offHeapPoint;
|
||||
* }
|
||||
* }}</pre>
|
||||
*
|
||||
* <p>{@code HashQueryContext} is the base interface defining the structure, but it has no methods
|
||||
* to anything "interesting" with {@code entry()} or {@code absentEntry()}. Use {@link
|
||||
* MapQueryContext} or {@link SetQueryContext} interfaces, which provide access to {@link
|
||||
* MapEntryOperations} and {@link SetEntryOperations} respectively.
|
||||
*
|
||||
* @param <K> the hash key type
|
||||
* @see ChronicleHash#queryContext(Object)
|
||||
* @see ChronicleHash#queryContext(Object)
|
||||
*/
|
||||
public interface HashQueryContext<K> extends HashContext<K>, InterProcessReadWriteUpdateLock {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user