Skip to content

Commit 31bb835

Browse files
Jörg Kubitzjukzi
authored andcommitted
[performance] Avoid O(n^2) in DeadlockDetector.lockAcquired()
With n conflicting rules. "conflicting.contains(possible)" has been observed as severe hotspot in a workspace with n= ~1000 projects, especially because all n Jobs did the same. Also avoid second pass if nothing changed. Functionality is tested with IJobManagerTest.testTransferToJobWaitingOnChildRule() OrderedLockTest.testComplex() DeadlockDetectionTest.testImplicitRules()
1 parent 7fa8bbc commit 31bb835

File tree

1 file changed

+22
-13
lines changed

1 file changed

+22
-13
lines changed

runtime/bundles/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/DeadlockDetector.java

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
import java.io.PrintWriter;
1717
import java.io.StringWriter;
1818
import java.util.ArrayList;
19+
import java.util.Collection;
20+
import java.util.HashSet;
21+
import java.util.Set;
1922
import org.eclipse.core.internal.runtime.RuntimeLog;
2023
import org.eclipse.core.runtime.Assert;
2124
import org.eclipse.core.runtime.IStatus;
@@ -304,23 +307,29 @@ void lockAcquired(Thread owner, ISchedulingRule lock) {
304307
* or conflict with a lock the given lock will acquire implicitly
305308
* (locks are acquired implicitly when a conflicting lock is acquired)
306309
*/
307-
ArrayList<ISchedulingRule> conflicting = new ArrayList<>(1);
308-
//only need two passes through all the locks to pick up all conflicting rules
309-
int NUM_PASSES = 2;
310-
conflicting.add(lock);
311310
graph[threadIndex][lockIndex]++;
312-
for (int i = 0; i < NUM_PASSES; i++) {
313-
for (int k = 0; k < conflicting.size(); k++) {
314-
ISchedulingRule current = conflicting.get(k);
315-
for (int j = 0; j < locks.size(); j++) {
316-
ISchedulingRule possible = locks.get(j);
317-
if (!conflicting.contains(possible) && current.isConflicting(possible)) {
318-
conflicting.add(possible);
319-
graph[threadIndex][j]++;
320-
}
311+
312+
// first pass tests against lock:
313+
Collection<ISchedulingRule> conflicting = computeConflicting(threadIndex, Set.of(lock));
314+
315+
// second pass tests also transitive:
316+
if (conflicting.size() > 1) {
317+
computeConflicting(threadIndex, conflicting);
318+
}
319+
}
320+
321+
private Collection<ISchedulingRule> computeConflicting(int threadIndex, Collection<ISchedulingRule> candidates) {
322+
Collection<ISchedulingRule> conflicting = new HashSet<>(candidates);
323+
for (ISchedulingRule current : candidates) {
324+
for (int j = 0; j < locks.size(); j++) {
325+
ISchedulingRule possible = locks.get(j);
326+
if (!conflicting.contains(possible) && current.isConflicting(possible)) {
327+
conflicting.add(possible);
328+
graph[threadIndex][j]++;
321329
}
322330
}
323331
}
332+
return conflicting;
324333
}
325334

326335
/**

0 commit comments

Comments
 (0)