/*
* Copyright (C) 2012, 2016 higherfrequencytrading.com
* Copyright (C) 2016 Roman Leventov
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.openhft.chronicle.map.example;
import net.openhft.chronicle.map.*;
public class HashQueryContextExamples {
public static <K, V> void incorrectRemovePresentEntry(ChronicleMap<K, V> map, K key) {
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.
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(entry);
}
}
}
public static <K, V> void correctRemovePresentEntry(ChronicleMap<K, V> map, K key) {
try (ExternalMapQueryContext<K, V, ?> q = map.queryContext(key)) {
q.updateLock().lock(); // acquire the update lock before checking the entry presence.
MapEntry<K, V> entry = q.entry();
if (entry != null)
q.remove(entry);
}
}
public static <K, V, R> void possibleAcquireUsingImplementation(
MapQueryContext<K, V, R> q, ReturnValue<V> returnValue) {
// 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<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 upgrade is forbidden.
q.readLock().unlock();
}
// We are here, either if we:
// 1) Failed to acquire the read lock, this means some other thread is holding the write
// lock now, in this case waiting for the update lock acquisition is no longer, than for
// the read
// 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<K, V> entry = q.entry();
if (entry != null) {
// Entry is present, return
returnValue.returnValue(entry.value());
return;
}
// Key is absent
q.insert(q.absentEntry(), q.defaultValue(q.absentEntry()));
returnValue.returnValue(q.entry().value());
}
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;
}
}
}