diff --git a/core/src/main/java/hudson/ExtensionList.java b/core/src/main/java/hudson/ExtensionList.java index 5e3d6c7610..f936d74091 100644 --- a/core/src/main/java/hudson/ExtensionList.java +++ b/core/src/main/java/hudson/ExtensionList.java @@ -209,7 +209,7 @@ public class ExtensionList extends AbstractList { if(Hudson.getInstance().getInitLevel().compareTo(InitMilestone.PLUGINS_PREPARED)<0) return legacyInstances; // can't perform the auto discovery until all plugins are loaded, so just make the legacy instances visible - synchronized (this) { + synchronized (hudson.lookup.setIfNull(Lock.class,new Lock())) { if(extensions==null) { List> r = load(); r.addAll(legacyInstances); @@ -219,6 +219,13 @@ public class ExtensionList extends AbstractList { } } + /** + * Loading an {@link ExtensionList} can result in a nested loading of another {@link ExtensionList}. + * What that means is that we need a single lock that spans across all the {@link ExtensionList}s, + * or else we can end up in a dead lock. + */ + private static final class Lock {} + /** * Loads all the extensions. */ diff --git a/core/src/main/java/hudson/Lookup.java b/core/src/main/java/hudson/Lookup.java index 7635d9bdae..090f3eb284 100644 --- a/core/src/main/java/hudson/Lookup.java +++ b/core/src/main/java/hudson/Lookup.java @@ -24,7 +24,6 @@ package hudson; -import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** @@ -33,7 +32,7 @@ import java.util.concurrent.ConcurrentHashMap; * @author Kohsuke Kawaguchi */ public class Lookup { - private final Map data = new ConcurrentHashMap(); + private final ConcurrentHashMap data = new ConcurrentHashMap(); public T get(Class type) { return type.cast(data.get(type)); @@ -42,4 +41,17 @@ public class Lookup { public T set(Class type, T instance) { return type.cast(data.put(type,instance)); } + + /** + * Overwrites the value only if the current value is null. + * + * @return + * If the value was null, return the {@code instance} value. + * Otherwise return the current value, which is non-null. + */ + public T setIfNull(Class type, T instance) { + Object o = data.putIfAbsent(type, instance); + if (o!=null) return type.cast(o); + return instance; + } }