Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[JENKINS-65821] Fix race condition by using ConcurrentHashMap #160

Merged
merged 2 commits into from
Jun 23, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
* Provides overall insight into the structure of a flow graph... but with limited visibility so we can change implementation.
* Designed to work entirely on the basis of the {@link FlowNode#id} rather than the {@link FlowNode}s themselves.
* Designed to work entirely on the basis of the {@link FlowNode#getId()} rather than the {@link FlowNode}s themselves.
*/
@SuppressFBWarnings(value = "ES_COMPARING_STRINGS_WITH_EQ", justification = "Can can use instance identity when comparing to a final constant")
@Restricted(NoExternalUse.class)
Expand All @@ -25,10 +25,10 @@ public final class StandardGraphLookupView implements GraphLookupView, GraphList
static final String INCOMPLETE = "";

/** Map the blockStartNode to its endNode, to accellerate a range of operations */
HashMap<String, String> blockStartToEnd = new HashMap<>();
ConcurrentHashMap<String, String> blockStartToEnd = new ConcurrentHashMap<>();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be fine since the value type is just String that already exists. Just a reminder that in general you need to be careful about having one thread add an entry to a map (or whatever) that another thread might pull off without the use of any synchronization barriers: if the object were recently constructed, its constructor might not have completed by the time the other thread uses it. One of those weird theoretical problems that probably does not happen often in reality but is hard to reason about.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if the object were recently constructed, its constructor might not have completed by the time the other thread uses it.

Wow, good to know. Thanks!


/** Map a node to its nearest enclosing block */
HashMap<String, String> nearestEnclosingBlock = new HashMap<>();
ConcurrentHashMap<String, String> nearestEnclosingBlock = new ConcurrentHashMap<>();

public void clearCache() {
blockStartToEnd.clear();
Expand All @@ -39,10 +39,11 @@ public void clearCache() {
@Override
public void onNewHead(@Nonnull FlowNode newHead) {
if (newHead instanceof BlockEndNode) {
blockStartToEnd.put(((BlockEndNode)newHead).getStartNode().getId(), newHead.getId());
String overallEnclosing = nearestEnclosingBlock.get(((BlockEndNode) newHead).getStartNode().getId());
if (overallEnclosing != null) {
nearestEnclosingBlock.put(newHead.getId(), overallEnclosing);
String startNodeId = ((BlockEndNode)newHead).getStartNode().getId();
blockStartToEnd.put(startNodeId, newHead.getId());
String enclosingId = nearestEnclosingBlock.get(startNodeId);
if (enclosingId != null) {
nearestEnclosingBlock.put(newHead.getId(), enclosingId);
}
} else {
if (newHead instanceof BlockStartNode) {
Expand Down Expand Up @@ -167,11 +168,9 @@ public BlockEndNode getEndNode(@Nonnull final BlockStartNode startNode) {
throw new RuntimeException(ioe);
}
} else {
BlockEndNode node = bruteForceScanForEnd(startNode);
if (node != null) {
blockStartToEnd.put(startNode.getId(), node.getId());
}
return node;
// returns the end node or null
// if this returns end node, it also adds start and end to blockStartToEnd
return bruteForceScanForEnd(startNode);
}
}

Expand All @@ -191,12 +190,8 @@ public BlockStartNode findEnclosingBlockStart(@Nonnull FlowNode node) {
}
}

BlockStartNode enclosing = bruteForceScanForEnclosingBlock(node);
if (enclosing != null) {
nearestEnclosingBlock.put(node.getId(), enclosing.getId());
return enclosing;
}
return null;
// when scan completes, enclosing is in the cache if it exists
return bruteForceScanForEnclosingBlock(node);
}

@Override
Expand Down