mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-10-01 09:03:31 +07:00
IDEA-245031 mark plugin classloaders as being unloaded to ensure that new extension instances will be not created during unload
GitOrigin-RevId: a453671f96a6c90b4fe6a68952f711d38accf500
This commit is contained in:
committed by
intellij-monorepo-bot
parent
04e6bed3be
commit
b7d1be4eb3
@@ -52,6 +52,8 @@ public final class PluginClassLoader extends UrlClassLoader implements PluginAwa
|
||||
|
||||
private final int instanceId;
|
||||
|
||||
private volatile int state = ACTIVE;
|
||||
|
||||
public PluginClassLoader(@NotNull List<URL> urls,
|
||||
@NotNull ClassLoader @NotNull [] parents,
|
||||
@NotNull PluginDescriptor pluginDescriptor,
|
||||
@@ -90,6 +92,16 @@ public final class PluginClassLoader extends UrlClassLoader implements PluginAwa
|
||||
}
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public int getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public void setState(int state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInstanceId() {
|
||||
return instanceId;
|
||||
|
||||
+7
@@ -3,12 +3,16 @@ package com.intellij.ide.plugins.cl;
|
||||
|
||||
import com.intellij.openapi.extensions.PluginDescriptor;
|
||||
import com.intellij.openapi.extensions.PluginId;
|
||||
import org.intellij.lang.annotations.MagicConstant;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.Collection;
|
||||
|
||||
public interface PluginAwareClassLoader {
|
||||
int ACTIVE = 1;
|
||||
int UNLOAD_IN_PROGRESS = 2;
|
||||
|
||||
@NotNull PluginDescriptor getPluginDescriptor();
|
||||
|
||||
@NotNull PluginId getPluginId();
|
||||
@@ -22,4 +26,7 @@ public interface PluginAwareClassLoader {
|
||||
long getLoadedClassCount();
|
||||
|
||||
@NotNull Collection<URL> getUrls();
|
||||
|
||||
@MagicConstant(intValues = {ACTIVE, UNLOAD_IN_PROGRESS})
|
||||
int getState();
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package com.intellij.openapi.extensions.impl;
|
||||
|
||||
import com.intellij.diagnostic.ActivityCategory;
|
||||
import com.intellij.diagnostic.StartUpMeasurer;
|
||||
import com.intellij.ide.plugins.cl.PluginAwareClassLoader;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.components.ComponentManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
@@ -465,6 +466,9 @@ public abstract class ExtensionPointImpl<@NotNull T> implements ExtensionPoint<T
|
||||
// (EP->adapter in one thread, adapter->EP in the other thread)
|
||||
private synchronized @Nullable T processAdapter(@NotNull ExtensionComponentAdapter adapter) {
|
||||
try {
|
||||
if (!checkThatClassloaderIsActive(adapter)) {
|
||||
return null;
|
||||
}
|
||||
return adapter.createInstance(componentManager);
|
||||
}
|
||||
catch (ExtensionNotApplicableException ignore) {
|
||||
@@ -488,6 +492,10 @@ public abstract class ExtensionPointImpl<@NotNull T> implements ExtensionPoint<T
|
||||
@NotNull Class<T> extensionClassForCheck,
|
||||
@NotNull List<? extends ExtensionComponentAdapter> adapters) {
|
||||
try {
|
||||
if (!checkThatClassloaderIsActive(adapter)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
boolean isNotifyThatAdded = listeners != null && listeners.length != 0 && !adapter.isInstanceCreated() && !isDynamic;
|
||||
// do not call CHECK_CANCELED here in loop because it is called by createInstance()
|
||||
T extension = adapter.createInstance(componentManager);
|
||||
@@ -526,6 +534,16 @@ public abstract class ExtensionPointImpl<@NotNull T> implements ExtensionPoint<T
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean checkThatClassloaderIsActive(@NotNull ExtensionComponentAdapter adapter) {
|
||||
ClassLoader classLoader = adapter.getPluginDescriptor().getPluginClassLoader();
|
||||
if (classLoader instanceof PluginAwareClassLoader &&
|
||||
((PluginAwareClassLoader)classLoader).getState() != PluginAwareClassLoader.ACTIVE) {
|
||||
LOG.warn(adapter + " not loaded because classloader is being unloaded");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// used in upsource
|
||||
// remove extensions for which implementation class is not available
|
||||
@SuppressWarnings("unused")
|
||||
|
||||
@@ -15,6 +15,7 @@ import com.intellij.ide.IdeEventQueue
|
||||
import com.intellij.ide.SaveAndSyncHandler
|
||||
import com.intellij.ide.actions.RevealFileAction
|
||||
import com.intellij.ide.impl.ProjectUtil
|
||||
import com.intellij.ide.plugins.cl.PluginAwareClassLoader
|
||||
import com.intellij.ide.plugins.cl.PluginClassLoader
|
||||
import com.intellij.ide.ui.TopHitCache
|
||||
import com.intellij.ide.ui.UIThemeProvider
|
||||
@@ -473,10 +474,14 @@ object DynamicPlugins {
|
||||
|
||||
app.messageBus.syncPublisher(DynamicPluginListener.TOPIC).beforePluginUnload(pluginDescriptor, options.isUpdate)
|
||||
IdeEventQueue.getInstance().flushQueue()
|
||||
// must be after flushQueue (e.g. https://youtrack.jetbrains.com/issue/IDEA-252010)
|
||||
app.runWriteAction {
|
||||
// must be after flushQueue (e.g. https://youtrack.jetbrains.com/issue/IDEA-252010)
|
||||
val forbidGettingServicesToken = app.forbidGettingServices("Plugin $pluginId being unloaded.")
|
||||
try {
|
||||
// https://youtrack.jetbrains.com/issue/IDEA-245031
|
||||
// mark plugin classloaders as being unloaded to ensure that new extension instances will be not created during unload
|
||||
setClassLoaderState(pluginDescriptor, PluginClassLoader.UNLOAD_IN_PROGRESS)
|
||||
|
||||
processLoadedOptionalDependenciesOnPlugin(pluginId) { mainDescriptor, subDescriptor ->
|
||||
// must be before unloadPluginDescriptorNotRecursively as this method nullize classloader
|
||||
val classLoader = (subDescriptor ?: mainDescriptor).pluginClassLoader as? PluginClassLoader
|
||||
@@ -587,6 +592,11 @@ object DynamicPlugins {
|
||||
FUCounterUsageLogger.getInstance().logEvent("plugins.dynamic", eventId, fuData)
|
||||
}
|
||||
}
|
||||
|
||||
if (!classLoaderUnloaded) {
|
||||
setClassLoaderState(pluginDescriptor, PluginAwareClassLoader.ACTIVE)
|
||||
}
|
||||
|
||||
return classLoaderUnloaded
|
||||
}
|
||||
|
||||
@@ -1107,4 +1117,11 @@ private fun unloadClassLoader(pluginDescriptor: IdeaPluginDescriptorImpl, timeou
|
||||
val watcher = GCWatcher.tracking(pluginDescriptor.classLoader)
|
||||
ClassLoaderConfigurator.setPluginClassLoaderForMainAndSubPlugins(pluginDescriptor, null)
|
||||
return watcher.tryCollect(timeoutMs)
|
||||
}
|
||||
|
||||
private fun setClassLoaderState(pluginDescriptor: IdeaPluginDescriptorImpl, state: Int) {
|
||||
(pluginDescriptor.classLoader as? PluginClassLoader)?.state = state
|
||||
for (dependency in (pluginDescriptor.pluginDependencies ?: return)) {
|
||||
dependency.subDescriptor?.let { setClassLoaderState(it, state) }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user