mirror of
https://github.com/OpenHFT/Chronicle-Map.git
synced 2021-09-13 01:26:12 +00:00
Lock ordering made right
This commit is contained in:
@@ -1926,18 +1926,22 @@ Using nested contexts:
|
||||
ChronicleMap<Integer, Set<Integer>> graph, int source, int target) {
|
||||
if (source == target)
|
||||
throw new IllegalArgumentException("loops are forbidden");
|
||||
|
||||
ExternalMapQueryContext<Integer, Set<Integer>, ?> sourceC = graph.queryContext(source);
|
||||
ExternalMapQueryContext<Integer, Set<Integer>, ?> targetC = graph.queryContext(target);
|
||||
// order for consistent lock acquisition => avoid dead lock
|
||||
if (target < source) {
|
||||
int t = source;
|
||||
source = target;
|
||||
target = t;
|
||||
if (sourceC.segmentIndex() <= targetC.segmentIndex()) {
|
||||
return innerAddEdge(source, sourceC, target, targetC);
|
||||
} else {
|
||||
return innerAddEdge(target, targetC, source, sourceC);
|
||||
}
|
||||
}
|
||||
|
||||
try (ExternalMapQueryContext<Integer, Set<Integer>, ?> sc = graph.queryContext(source)) {
|
||||
sc.updateLock().lock();
|
||||
try (ExternalMapQueryContext<Integer, Set<Integer>, ?> tc =
|
||||
graph.queryContext(target)) {
|
||||
private static boolean innerAddEdge(
|
||||
int source, ExternalMapQueryContext<Integer, Set<Integer>, ?> sourceContext,
|
||||
int target, ExternalMapQueryContext<Integer, Set<Integer>, ?> targetContext) {
|
||||
try (ExternalMapQueryContext<Integer, Set<Integer>, ?> sc = sourceContext) {
|
||||
try (ExternalMapQueryContext<Integer, Set<Integer>, ?> tc = targetContext) {
|
||||
sc.updateLock().lock();
|
||||
tc.updateLock().lock();
|
||||
MapEntry<Integer, Set<Integer>> sEntry = sc.entry();
|
||||
if (sEntry != null) {
|
||||
@@ -2007,23 +2011,29 @@ Using nested contexts:
|
||||
|
||||
public static boolean removeEdge(
|
||||
ChronicleMap<Integer, Set<Integer>> graph, int source, int target) {
|
||||
ExternalMapQueryContext<Integer, Set<Integer>, ?> sourceC = graph.queryContext(source);
|
||||
ExternalMapQueryContext<Integer, Set<Integer>, ?> targetC = graph.queryContext(target);
|
||||
// order for consistent lock acquisition => avoid dead lock
|
||||
if (target < source) {
|
||||
int t = source;
|
||||
source = target;
|
||||
target = t;
|
||||
if (sourceC.segmentIndex() <= targetC.segmentIndex()) {
|
||||
return innerRemoveEdge(source, sourceC, target, targetC);
|
||||
} else {
|
||||
return innerRemoveEdge(target, targetC, source, sourceC);
|
||||
}
|
||||
|
||||
try (ExternalMapQueryContext<Integer, Set<Integer>, ?> sc = graph.queryContext(source)) {
|
||||
sc.updateLock().lock();
|
||||
MapEntry<Integer, Set<Integer>> sEntry = sc.entry();
|
||||
if (sEntry == null)
|
||||
return false;
|
||||
Set<Integer> sNeighbours = sEntry.value().get();
|
||||
if (!sNeighbours.remove(target))
|
||||
return false;
|
||||
try (ExternalMapQueryContext<Integer, Set<Integer>, ?> tc =
|
||||
graph.queryContext(target)) {
|
||||
}
|
||||
|
||||
private static boolean innerRemoveEdge(
|
||||
int source, ExternalMapQueryContext<Integer, Set<Integer>, ?> sourceContext,
|
||||
int target, ExternalMapQueryContext<Integer, Set<Integer>, ?> targetContext) {
|
||||
try (ExternalMapQueryContext<Integer, Set<Integer>, ?> sc = sourceContext) {
|
||||
try (ExternalMapQueryContext<Integer, Set<Integer>, ?> tc = targetContext) {
|
||||
sc.updateLock().lock();
|
||||
MapEntry<Integer, Set<Integer>> sEntry = sc.entry();
|
||||
if (sEntry == null)
|
||||
return false;
|
||||
Set<Integer> sNeighbours = sEntry.value().get();
|
||||
if (!sNeighbours.remove(target))
|
||||
return false;
|
||||
|
||||
tc.updateLock().lock();
|
||||
MapEntry<Integer, Set<Integer>> tEntry = tc.entry();
|
||||
if (tEntry == null)
|
||||
|
||||
@@ -7,6 +7,7 @@ import net.openhft.chronicle.map.MapMethods;
|
||||
import net.openhft.chronicle.map.MapQueryContext;
|
||||
import net.openhft.chronicle.set.SetEntryOperations;
|
||||
import net.openhft.chronicle.set.SetQueryContext;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Context of {@link ChronicleHash} operations with <i>individual keys</i>.
|
||||
@@ -137,7 +138,17 @@ import net.openhft.chronicle.set.SetQueryContext;
|
||||
* @param <K> the hash key type
|
||||
* @see ChronicleHash#queryContext(Object)
|
||||
*/
|
||||
public interface HashQueryContext<K> extends HashContext<K>, InterProcessReadWriteUpdateLock {
|
||||
public interface HashQueryContext<K> extends HashContext<K>, SegmentLock {
|
||||
|
||||
/**
|
||||
* Returns the index of the accessed segment, where the queried key is located (or to which
|
||||
* the key is going to be put).
|
||||
*
|
||||
* <p>This index might also be used as the {@code InterProcessReadWriteUpdateLock} identifier,
|
||||
* because {@code ChronicleHashes} has per-segment locks.
|
||||
*/
|
||||
@Override
|
||||
int segmentIndex();
|
||||
|
||||
/**
|
||||
* Returns the queried key as a {@code Data}.
|
||||
@@ -160,5 +171,6 @@ public interface HashQueryContext<K> extends HashContext<K>, InterProcessReadWri
|
||||
* @implNote Might acquire {@link #readLock} before searching for the key, if the context
|
||||
* is not locked yet.
|
||||
*/
|
||||
@Nullable
|
||||
HashAbsentEntry<K> absentEntry();
|
||||
}
|
||||
|
||||
+17
-7
@@ -1,5 +1,7 @@
|
||||
/*
|
||||
* Copyright 2014 Higher Frequency Trading http://www.higherfrequencytrading.com
|
||||
* 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.
|
||||
@@ -17,11 +19,19 @@
|
||||
package net.openhft.chronicle.hash;
|
||||
|
||||
import net.openhft.chronicle.hash.locks.InterProcessReadWriteUpdateLock;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public interface Segment {
|
||||
long index();
|
||||
|
||||
@NotNull
|
||||
InterProcessReadWriteUpdateLock lock();
|
||||
/**
|
||||
* {@link InterProcessReadWriteUpdateLock} of a segment in {@code ChronicleHash}.
|
||||
*
|
||||
* <p>In Chronicle-Map off-heap design, locks (and concurrency) are per-segment.
|
||||
*
|
||||
* @see ChronicleHashBuilder#minSegments(int)
|
||||
* @see ChronicleHashBuilder#actualSegments(int)
|
||||
*/
|
||||
public interface SegmentLock extends InterProcessReadWriteUpdateLock {
|
||||
|
||||
/**
|
||||
* Returns the index of the accessed segment.
|
||||
*/
|
||||
int segmentIndex();
|
||||
}
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
package net.openhft.chronicle.hash.impl.stage.entry;
|
||||
|
||||
import net.openhft.chronicle.hash.SegmentLock;
|
||||
import net.openhft.chronicle.hash.impl.BigSegmentHeader;
|
||||
import net.openhft.chronicle.hash.impl.LocalLockState;
|
||||
import net.openhft.chronicle.hash.impl.SegmentHeader;
|
||||
@@ -38,7 +39,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import static net.openhft.chronicle.hash.impl.LocalLockState.UNLOCKED;
|
||||
|
||||
@Staged
|
||||
public abstract class SegmentStages implements InterProcessReadWriteUpdateLock {
|
||||
public abstract class SegmentStages implements SegmentLock {
|
||||
|
||||
@StageRef Chaining chaining;
|
||||
@StageRef public VanillaChronicleHashHolder<?, ?, ?> hh;
|
||||
@@ -48,7 +49,6 @@ public abstract class SegmentStages implements InterProcessReadWriteUpdateLock {
|
||||
|
||||
public void initTheSegmentIndex(int segmentIndex) {
|
||||
this.segmentIndex = segmentIndex;
|
||||
|
||||
}
|
||||
|
||||
// "SegHeader" because stage-generator doesn't support stage names - one is a prefix of another
|
||||
|
||||
@@ -27,8 +27,6 @@ import org.jetbrains.annotations.Nullable;
|
||||
* (like during {@code get()}, {@code put()}, etc., opposed to <i>bulk</i> operations).
|
||||
* This is the main context type of {@link MapMethods} and {@link MapRemoteOperations}.
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param <K> the map key type
|
||||
* @param <V> the map value type
|
||||
* @param <R> the return type of {@link MapEntryOperations} specialized for the queried map
|
||||
|
||||
@@ -37,7 +37,7 @@ public class NestedContextsTest {
|
||||
public void nestedContextsTest() throws ExecutionException, InterruptedException {
|
||||
ChronicleMap<Integer, Set<Integer>> graph = ChronicleMapBuilder
|
||||
.of(Integer.class, (Class<Set<Integer>>) (Class) Set.class)
|
||||
.actualSegments(1)
|
||||
.actualSegments(2)
|
||||
.create();
|
||||
|
||||
addEdge(graph, 1, 2);
|
||||
@@ -81,17 +81,22 @@ public class NestedContextsTest {
|
||||
ChronicleMap<Integer, Set<Integer>> graph, int source, int target) {
|
||||
if (source == target)
|
||||
throw new IllegalArgumentException("loops are forbidden");
|
||||
ExternalMapQueryContext<Integer, Set<Integer>, ?> sourceC = graph.queryContext(source);
|
||||
ExternalMapQueryContext<Integer, Set<Integer>, ?> targetC = graph.queryContext(target);
|
||||
// order for consistent lock acquisition => avoid dead lock
|
||||
if (target < source) {
|
||||
int t = source;
|
||||
source = target;
|
||||
target = t;
|
||||
if (sourceC.segmentIndex() <= targetC.segmentIndex()) {
|
||||
return innerAddEdge(source, sourceC, target, targetC);
|
||||
} else {
|
||||
return innerAddEdge(target, targetC, source, sourceC);
|
||||
}
|
||||
}
|
||||
|
||||
try (ExternalMapQueryContext<Integer, Set<Integer>, ?> sc = graph.queryContext(source)) {
|
||||
sc.updateLock().lock();
|
||||
try (ExternalMapQueryContext<Integer, Set<Integer>, ?> tc =
|
||||
graph.queryContext(target)) {
|
||||
private static boolean innerAddEdge(
|
||||
int source, ExternalMapQueryContext<Integer, Set<Integer>, ?> sourceContext,
|
||||
int target, ExternalMapQueryContext<Integer, Set<Integer>, ?> targetContext) {
|
||||
try (ExternalMapQueryContext<Integer, Set<Integer>, ?> sc = sourceContext) {
|
||||
try (ExternalMapQueryContext<Integer, Set<Integer>, ?> tc = targetContext) {
|
||||
sc.updateLock().lock();
|
||||
tc.updateLock().lock();
|
||||
MapEntry<Integer, Set<Integer>> sEntry = sc.entry();
|
||||
if (sEntry != null) {
|
||||
@@ -161,23 +166,29 @@ public class NestedContextsTest {
|
||||
|
||||
public static boolean removeEdge(
|
||||
ChronicleMap<Integer, Set<Integer>> graph, int source, int target) {
|
||||
ExternalMapQueryContext<Integer, Set<Integer>, ?> sourceC = graph.queryContext(source);
|
||||
ExternalMapQueryContext<Integer, Set<Integer>, ?> targetC = graph.queryContext(target);
|
||||
// order for consistent lock acquisition => avoid dead lock
|
||||
if (target < source) {
|
||||
int t = source;
|
||||
source = target;
|
||||
target = t;
|
||||
if (sourceC.segmentIndex() <= targetC.segmentIndex()) {
|
||||
return innerRemoveEdge(source, sourceC, target, targetC);
|
||||
} else {
|
||||
return innerRemoveEdge(target, targetC, source, sourceC);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean innerRemoveEdge(
|
||||
int source, ExternalMapQueryContext<Integer, Set<Integer>, ?> sourceContext,
|
||||
int target, ExternalMapQueryContext<Integer, Set<Integer>, ?> targetContext) {
|
||||
try (ExternalMapQueryContext<Integer, Set<Integer>, ?> sc = sourceContext) {
|
||||
try (ExternalMapQueryContext<Integer, Set<Integer>, ?> tc = targetContext) {
|
||||
sc.updateLock().lock();
|
||||
MapEntry<Integer, Set<Integer>> sEntry = sc.entry();
|
||||
if (sEntry == null)
|
||||
return false;
|
||||
Set<Integer> sNeighbours = sEntry.value().get();
|
||||
if (!sNeighbours.remove(target))
|
||||
return false;
|
||||
|
||||
try (ExternalMapQueryContext<Integer, Set<Integer>, ?> sc = graph.queryContext(source)) {
|
||||
sc.updateLock().lock();
|
||||
MapEntry<Integer, Set<Integer>> sEntry = sc.entry();
|
||||
if (sEntry == null)
|
||||
return false;
|
||||
Set<Integer> sNeighbours = sEntry.value().get();
|
||||
if (!sNeighbours.remove(target))
|
||||
return false;
|
||||
try (ExternalMapQueryContext<Integer, Set<Integer>, ?> tc =
|
||||
graph.queryContext(target)) {
|
||||
tc.updateLock().lock();
|
||||
MapEntry<Integer, Set<Integer>> tEntry = tc.entry();
|
||||
if (tEntry == null)
|
||||
|
||||
Reference in New Issue
Block a user