提交 d5167025 编写于 作者: J Jesse Glick

[FIXED JENKINS-22767] Make sure only one thread actually loads a given build.

上级 8f426002
......@@ -353,7 +353,19 @@ public abstract class AbstractLazyLoadRunMap<R> extends AbstractMap<Integer,R> i
if (v!=null) return v; // already in memory
// otherwise fall through to load
}
return load(n, null);
synchronized (this) {
if (index.byNumber.containsKey(n)) { // JENKINS-22767: recheck inside lock
BuildReference<R> ref = index.byNumber.get(n);
if (ref == null) {
return null;
}
R v = unwrap(ref);
if (v != null) {
return v;
}
}
return load(n, null);
}
}
protected final synchronized void proposeNewNumber(int number) throws IllegalStateException {
......@@ -443,7 +455,8 @@ public abstract class AbstractLazyLoadRunMap<R> extends AbstractMap<Integer,R> i
*
* @return null if the data failed to load.
*/
protected R load(int n, Index editInPlace) {
private R load(int n, Index editInPlace) {
assert Thread.holdsLock(this);
assert dir != null;
R v = load(new File(dir, String.valueOf(n)), editInPlace);
if (v==null && editInPlace!=null) {
......@@ -460,7 +473,8 @@ public abstract class AbstractLazyLoadRunMap<R> extends AbstractMap<Integer,R> i
* If non-null, update this data structure.
* Otherwise do a copy-on-write of {@link #index}
*/
protected synchronized R load(File dataDir, Index editInPlace) {
private R load(File dataDir, Index editInPlace) {
assert Thread.holdsLock(this);
try {
R r = retrieve(dataDir);
if (r==null) return null;
......
......@@ -23,6 +23,7 @@
*/
package jenkins.model.lazy;
import java.io.File;
import static org.junit.Assert.*;
import jenkins.model.lazy.AbstractLazyLoadRunMap.Direction;
......@@ -31,13 +32,19 @@ import org.junit.Rule;
import org.junit.Test;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.SortedMap;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import jenkins.util.Timer;
import org.junit.BeforeClass;
import org.jvnet.hudson.test.Issue;
......@@ -72,6 +79,29 @@ public class AbstractLazyLoadRunMapTest {
}
};
private final Map<Integer,Semaphore> slowBuilderStartSemaphores = new HashMap<>();
private final Map<Integer,Semaphore> slowBuilderEndSemaphores = new HashMap<>();
private final Map<Integer,AtomicInteger> slowBuilderLoadCount = new HashMap<>();
@Rule
public FakeMapBuilder slowBuilder = new FakeMapBuilder() {
@Override
public FakeMap make() {
return new FakeMap(getDir()) {
@Override
protected Build retrieve(File dir) throws IOException {
Build b = super.retrieve(dir);
slowBuilderStartSemaphores.get(b.n).release();
try {
slowBuilderEndSemaphores.get(b.n).acquire();
} catch (InterruptedException x) {
throw new IOException(x);
}
slowBuilderLoadCount.get(b.n).incrementAndGet();
return b;
}
};
}
};
@BeforeClass
public static void setUpClass() {
......@@ -358,4 +388,36 @@ public class AbstractLazyLoadRunMapTest {
assertTrue(a.entrySet().contains(e));
}
}
@Issue("JENKINS-22767")
@Test
public void slowRetrieve() throws Exception {
for (int i = 1; i <= 3; i++) {
slowBuilder.add(i);
slowBuilderStartSemaphores.put(i, new Semaphore(0));
slowBuilderEndSemaphores.put(i, new Semaphore(0));
slowBuilderLoadCount.put(i, new AtomicInteger());
}
final FakeMap m = slowBuilder.make();
Future<Build> firstLoad = Timer.get().submit(new Callable<Build>() {
@Override
public Build call() throws Exception {
return m.getByNumber(2);
}
});
Future<Build> secondLoad = Timer.get().submit(new Callable<Build>() {
@Override
public Build call() throws Exception {
return m.getByNumber(2);
}
});
slowBuilderStartSemaphores.get(2).acquire(1);
// now one of them is inside retrieve(…); the other is waiting for the lock
slowBuilderEndSemaphores.get(2).release(2); // allow both to proceed
Build first = firstLoad.get();
Build second = secondLoad.get();
assertEquals(1, slowBuilderLoadCount.get(2).get());
assertSame(second, first);
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册