Skip to content

Commit 54ca5ee

Browse files
author
Jörg Kubitz
committed
[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 Also avoid second pass if nothing changed. Functionality is tested with IJobManagerTest.testTransferToJobWaitingOnChildRule() OrderedLockTest.testComplex() DeadlockDetectionTest.testImplicitRules()
1 parent d3dd4bf commit 54ca5ee

File tree

1 file changed

+23
-13
lines changed

1 file changed

+23
-13
lines changed

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

Lines changed: 23 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,30 @@ 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 = Set.of(lock);
314+
conflicting = computeConflicting(threadIndex, conflicting);
315+
316+
// second pass tests also transitive:
317+
if (!conflicting.isEmpty()) {
318+
conflicting = computeConflicting(threadIndex, conflicting);
319+
}
320+
}
321+
322+
private Collection<ISchedulingRule> computeConflicting(int threadIndex, Collection<ISchedulingRule> candidates) {
323+
Collection<ISchedulingRule> conflicting = new HashSet<>(candidates);
324+
for (ISchedulingRule current : candidates) {
325+
for (int j = 0; j < locks.size(); j++) {
326+
ISchedulingRule possible = locks.get(j);
327+
if (!conflicting.contains(possible) && current.isConflicting(possible)) {
328+
conflicting.add(possible);
329+
graph[threadIndex][j]++;
321330
}
322331
}
323332
}
333+
return conflicting;
324334
}
325335

326336
/**

0 commit comments

Comments
 (0)