diff --git a/README.md b/README.md index cf8e5f0e0..e537f1310 100644 --- a/README.md +++ b/README.md @@ -1922,131 +1922,131 @@ ChronicleMap> map1 = ChronicleMapBuilder Using nested contexts: ```java - public static boolean addEdge( - ChronicleMap> graph, int source, int target) { - if (source == target) - throw new IllegalArgumentException("loops are forbidden"); - ExternalMapQueryContext, ?> sourceC = graph.queryContext(source); - ExternalMapQueryContext, ?> targetC = graph.queryContext(target); - // order for consistent lock acquisition => avoid dead lock - if (sourceC.segmentIndex() <= targetC.segmentIndex()) { - return innerAddEdge(source, sourceC, target, targetC); - } else { - return innerAddEdge(target, targetC, source, sourceC); - } +public static boolean addEdge( + ChronicleMap> graph, int source, int target) { + if (source == target) + throw new IllegalArgumentException("loops are forbidden"); + ExternalMapQueryContext, ?> sourceC = graph.queryContext(source); + ExternalMapQueryContext, ?> targetC = graph.queryContext(target); + // order for consistent lock acquisition => avoid dead lock + if (sourceC.segmentIndex() <= targetC.segmentIndex()) { + return innerAddEdge(source, sourceC, target, targetC); + } else { + return innerAddEdge(target, targetC, source, sourceC); } +} - private static boolean innerAddEdge( - int source, ExternalMapQueryContext, ?> sourceContext, - int target, ExternalMapQueryContext, ?> targetContext) { - try (ExternalMapQueryContext, ?> sc = sourceContext) { - try (ExternalMapQueryContext, ?> tc = targetContext) { - sc.updateLock().lock(); - tc.updateLock().lock(); - MapEntry> sEntry = sc.entry(); - if (sEntry != null) { - MapEntry> tEntry = tc.entry(); - if (tEntry != null) { - return addEdgeBothPresent(sc, sEntry, source, tc, tEntry, target); - } else { - addEdgePresentAbsent(sc, sEntry, source, tc, target); - return true; - } +private static boolean innerAddEdge( + int source, ExternalMapQueryContext, ?> sourceContext, + int target, ExternalMapQueryContext, ?> targetContext) { + try (ExternalMapQueryContext, ?> sc = sourceContext) { + try (ExternalMapQueryContext, ?> tc = targetContext) { + sc.updateLock().lock(); + tc.updateLock().lock(); + MapEntry> sEntry = sc.entry(); + if (sEntry != null) { + MapEntry> tEntry = tc.entry(); + if (tEntry != null) { + return addEdgeBothPresent(sc, sEntry, source, tc, tEntry, target); } else { - MapEntry> tEntry = tc.entry(); - if (tEntry != null) { - addEdgePresentAbsent(tc, tEntry, target, sc, source); - } else { - addEdgeBothAbsent(sc, source, tc, target); - } + addEdgePresentAbsent(sc, sEntry, source, tc, target); return true; } - } - } - } - - private static boolean addEdgeBothPresent( - MapQueryContext, ?> sc, - @NotNull MapEntry> sEntry, int source, - MapQueryContext, ?> tc, - @NotNull MapEntry> tEntry, int target) { - Set sNeighbours = sEntry.value().get(); - if (sNeighbours.add(target)) { - Set tNeighbours = tEntry.value().get(); - boolean added = tNeighbours.add(source); - assert added; - sEntry.doReplaceValue(sc.wrapValueAsData(sNeighbours)); - tEntry.doReplaceValue(tc.wrapValueAsData(tNeighbours)); - return true; - } else { - return false; - } - } - - private static void addEdgePresentAbsent( - MapQueryContext, ?> sc, - @NotNull MapEntry> sEntry, int source, - MapQueryContext, ?> tc, int target) { - Set sNeighbours = sEntry.value().get(); - boolean added = sNeighbours.add(target); - assert added; - sEntry.doReplaceValue(sc.wrapValueAsData(sNeighbours)); - - addEdgeOneSide(tc, source); - } - - private static void addEdgeBothAbsent(MapQueryContext, ?> sc, int source, - MapQueryContext, ?> tc, int target) { - addEdgeOneSide(sc, target); - addEdgeOneSide(tc, source); - } - - private static void addEdgeOneSide(MapQueryContext, ?> tc, int source) { - Set tNeighbours = new HashSet<>(); - tNeighbours.add(source); - MapAbsentEntry> tAbsentEntry = tc.absentEntry(); - assert tAbsentEntry != null; - tAbsentEntry.doInsert(tc.wrapValueAsData(tNeighbours)); - } - - public static boolean removeEdge( - ChronicleMap> graph, int source, int target) { - ExternalMapQueryContext, ?> sourceC = graph.queryContext(source); - ExternalMapQueryContext, ?> targetC = graph.queryContext(target); - // order for consistent lock acquisition => avoid dead lock - 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, ?> sourceContext, - int target, ExternalMapQueryContext, ?> targetContext) { - try (ExternalMapQueryContext, ?> sc = sourceContext) { - try (ExternalMapQueryContext, ?> tc = targetContext) { - sc.updateLock().lock(); - MapEntry> sEntry = sc.entry(); - if (sEntry == null) - return false; - Set sNeighbours = sEntry.value().get(); - if (!sNeighbours.remove(target)) - return false; - - tc.updateLock().lock(); + } else { MapEntry> tEntry = tc.entry(); - if (tEntry == null) - throw new IllegalStateException("target node should be present in the graph"); - Set tNeighbours = tEntry.value().get(); - if (!tNeighbours.remove(source)) - throw new IllegalStateException("the target node have an edge to the source"); - sEntry.doReplaceValue(sc.wrapValueAsData(sNeighbours)); - tEntry.doReplaceValue(tc.wrapValueAsData(tNeighbours)); + if (tEntry != null) { + addEdgePresentAbsent(tc, tEntry, target, sc, source); + } else { + addEdgeBothAbsent(sc, source, tc, target); + } return true; } } } +} + +private static boolean addEdgeBothPresent( + MapQueryContext, ?> sc, + @NotNull MapEntry> sEntry, int source, + MapQueryContext, ?> tc, + @NotNull MapEntry> tEntry, int target) { + Set sNeighbours = sEntry.value().get(); + if (sNeighbours.add(target)) { + Set tNeighbours = tEntry.value().get(); + boolean added = tNeighbours.add(source); + assert added; + sEntry.doReplaceValue(sc.wrapValueAsData(sNeighbours)); + tEntry.doReplaceValue(tc.wrapValueAsData(tNeighbours)); + return true; + } else { + return false; + } +} + +private static void addEdgePresentAbsent( + MapQueryContext, ?> sc, + @NotNull MapEntry> sEntry, int source, + MapQueryContext, ?> tc, int target) { + Set sNeighbours = sEntry.value().get(); + boolean added = sNeighbours.add(target); + assert added; + sEntry.doReplaceValue(sc.wrapValueAsData(sNeighbours)); + + addEdgeOneSide(tc, source); +} + +private static void addEdgeBothAbsent(MapQueryContext, ?> sc, int source, + MapQueryContext, ?> tc, int target) { + addEdgeOneSide(sc, target); + addEdgeOneSide(tc, source); +} + +private static void addEdgeOneSide(MapQueryContext, ?> tc, int source) { + Set tNeighbours = new HashSet<>(); + tNeighbours.add(source); + MapAbsentEntry> tAbsentEntry = tc.absentEntry(); + assert tAbsentEntry != null; + tAbsentEntry.doInsert(tc.wrapValueAsData(tNeighbours)); +} + +public static boolean removeEdge( + ChronicleMap> graph, int source, int target) { + ExternalMapQueryContext, ?> sourceC = graph.queryContext(source); + ExternalMapQueryContext, ?> targetC = graph.queryContext(target); + // order for consistent lock acquisition => avoid dead lock + 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, ?> sourceContext, + int target, ExternalMapQueryContext, ?> targetContext) { + try (ExternalMapQueryContext, ?> sc = sourceContext) { + try (ExternalMapQueryContext, ?> tc = targetContext) { + sc.updateLock().lock(); + MapEntry> sEntry = sc.entry(); + if (sEntry == null) + return false; + Set sNeighbours = sEntry.value().get(); + if (!sNeighbours.remove(target)) + return false; + + tc.updateLock().lock(); + MapEntry> tEntry = tc.entry(); + if (tEntry == null) + throw new IllegalStateException("target node should be present in the graph"); + Set tNeighbours = tEntry.value().get(); + if (!tNeighbours.remove(source)) + throw new IllegalStateException("the target node have an edge to the source"); + sEntry.doReplaceValue(sc.wrapValueAsData(sNeighbours)); + tEntry.doReplaceValue(tc.wrapValueAsData(tNeighbours)); + return true; + } + } +} ``` Usage: diff --git a/src/main/java/net/openhft/chronicle/hash/SegmentLock.java b/src/main/java/net/openhft/chronicle/hash/SegmentLock.java index d8b408232..96f9eb6ae 100644 --- a/src/main/java/net/openhft/chronicle/hash/SegmentLock.java +++ b/src/main/java/net/openhft/chronicle/hash/SegmentLock.java @@ -29,7 +29,7 @@ import net.openhft.chronicle.hash.locks.InterProcessReadWriteUpdateLock; * @see ChronicleHashBuilder#actualSegments(int) */ public interface SegmentLock extends InterProcessReadWriteUpdateLock { - + /** * Returns the index of the accessed segment. */