prevent AWT auto shutdown, don't create app in EDT

GitOrigin-RevId: 861d20e85bdee38f4ef371984ae360c5ff8310d8
This commit is contained in:
Vladimir Krivosheev
2021-04-12 11:11:03 +00:00
committed by intellij-monorepo-bot
parent 6486e5b086
commit b15e34043d
6 changed files with 122 additions and 67 deletions
@@ -3,6 +3,7 @@
@file:ApiStatus.Internal
package com.intellij.idea
import com.intellij.BundleBase
import com.intellij.diagnostic.*
import com.intellij.diagnostic.StartUpMeasurer.Activities
import com.intellij.icons.AllIcons
@@ -35,9 +36,9 @@ import com.intellij.util.io.createDirectories
import com.intellij.util.io.storage.HeavyProcessLatch
import com.intellij.util.lang.ZipFilePool
import com.intellij.util.ui.AsyncProcessIcon
import com.intellij.util.ui.EDT
import net.miginfocom.layout.PlatformDefaults
import org.jetbrains.annotations.ApiStatus
import java.awt.EventQueue
import java.io.IOException
import java.nio.charset.StandardCharsets
import java.nio.file.Files
@@ -52,61 +53,61 @@ private val SAFE_JAVA_ENV_PARAMETERS = arrayOf(JetBrainsProtocolHandler.REQUIRED
private val LOG = Logger.getInstance("#com.intellij.idea.ApplicationLoader")
fun initApplication(rawArgs: List<String>, prepareUiFuture: CompletionStage<*>) {
val args = processProgramArguments(rawArgs)
val initAppActivity = StartupUtil.startupStart.endAndStart(Activities.INIT_APP)
val loadAndInitPluginFutureActivity = initAppActivity.startChild("plugin descriptor init waiting")
val loadAndInitPluginFuture = PluginManagerCore.initPlugins(StartupUtil::class.java.classLoader)
loadAndInitPluginFuture.thenRun(loadAndInitPluginFutureActivity::end)
// use main thread, avoid thread switching
(prepareUiFuture as CompletableFuture<*>).join()
prepareUiFuture.thenComposeAsync({
val isInternal = java.lang.Boolean.getBoolean(ApplicationManagerEx.IS_INTERNAL_PROPERTY)
val app = ApplicationImpl(isInternal, false, Main.isHeadless(), Main.isCommandLine(), EDT.getEventDispatchThread()
?: throw IllegalStateException("Init UI first"))
(UIManager.getLookAndFeel() as? DarculaLaf)?.appCreated(app)
val args = processProgramArguments(rawArgs)
EventQueue.invokeLater {
runActivity("create app") {
val isInternal = java.lang.Boolean.getBoolean(ApplicationManagerEx.IS_INTERNAL_PROPERTY)
ApplicationImpl(isInternal, false, Main.isHeadless(), Main.isCommandLine()) { app ->
(UIManager.getLookAndFeel() as? DarculaLaf)?.appCreated(app)
if (isInternal) {
BundleBase.assertOnMissedKeys(true)
}
loadAndInitPluginFuture
.thenAcceptAsync({ plugins ->
runActivity("app component registration") {
app.registerComponents(plugins, app, null)
}
loadAndInitPluginFuture
.thenAccept { plugins ->
runActivity("app component registration") {
app.registerComponents(plugins, app, null)
}
if (args.isEmpty()) {
startApp(app, IdeStarter(), initAppActivity, plugins, args)
}
else {
// `ApplicationStarter` is an extension, so to find a starter extensions must be registered first
findCustomAppStarterAndStart(plugins, args, app, initAppActivity)
}
if (args.isEmpty()) {
startApp(app, IdeStarter(), initAppActivity, plugins, args)
}
else {
// `ApplicationStarter` is an extension, so to find a starter extensions must be registered first
findCustomAppStarterAndStart(plugins, args, app, initAppActivity)
}
if (!Main.isHeadless()) {
ForkJoinPool.commonPool().execute {
runActivity("icons preloading") {
if (isInternal) {
IconLoader.setStrictGlobally(true)
}
if (!Main.isHeadless()) {
ForkJoinPool.commonPool().execute {
runActivity("icons preloading") {
if (isInternal) {
IconLoader.setStrictGlobally(true)
}
AsyncProcessIcon("")
AnimatedIcon.Blinking(AllIcons.Ide.FatalError)
AnimatedIcon.FS()
}
AsyncProcessIcon("")
AnimatedIcon.Blinking(AllIcons.Ide.FatalError)
AnimatedIcon.FS()
}
runActivity("migLayout") {
// IDEA-170295
PlatformDefaults.setLogicalPixelBase(PlatformDefaults.BASE_FONT_SIZE)
}
}
runActivity("migLayout") {
// IDEA-170295
PlatformDefaults.setLogicalPixelBase(PlatformDefaults.BASE_FONT_SIZE)
}
}, { if (EventQueue.isDispatchThread()) ForkJoinPool.commonPool().execute(it) else it.run() })
.exceptionally {
StartupAbortedException.processException(it)
null
}
}
}
}, Executor { if (EDT.isCurrentThreadEdt()) ForkJoinPool.commonPool().execute(it) else it.run() } )
.exceptionally {
StartupAbortedException.processException(it)
null
}
}
}
private fun startApp(app: ApplicationImpl,
@@ -27,6 +27,7 @@ import com.intellij.openapi.application.ApplicationInfo;
import com.intellij.openapi.application.ApplicationNamesInfo;
import com.intellij.openapi.application.ConfigImportHelper;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.application.impl.AWTExceptionHandler;
import com.intellij.openapi.application.impl.ApplicationInfoImpl;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.ShutDownTracker;
@@ -40,6 +41,7 @@ import com.intellij.ui.IconManager;
import com.intellij.ui.mac.MacOSApplicationProvider;
import com.intellij.ui.scale.JBUIScale;
import com.intellij.util.EnvironmentUtil;
import com.intellij.util.concurrency.AppScheduledExecutorService;
import com.intellij.util.lang.ZipFilePool;
import com.intellij.util.ui.StartupUiUtil;
import com.intellij.util.ui.accessibility.ScreenReader;
@@ -477,9 +479,20 @@ public final class StartupUtil {
catch (UnsupportedLookAndFeelException e) {
throw new RuntimeException(e);
}
activity.end();
StartUpMeasurer.setCurrentState(LoadingState.LAF_INITIALIZED);
activity = activity.endAndStart("awt thread busy notification");
// instantiate AppDelayQueue which starts "Periodic task thread" which we'll mark busy to prevent this EDT to die
// that thread was chosen because we know for sure it's running
Thread thread = AppScheduledExecutorService.getPeriodicTasksThread();
// needed for EDT not to exit suddenly
AWTAutoShutdown.getInstance().notifyThreadBusy(thread);
ShutDownTracker.getInstance().registerShutdownTask(() -> {
// allow for EDT to exit
AWTAutoShutdown.getInstance().notifyThreadFree(thread);
});
activity.end();
}, it -> EventQueue.invokeLater(it)/* don't use here method reference (EventQueue class must be loaded on demand) */);
if (isUsingSeparateWriteThread()) {
@@ -954,6 +967,10 @@ public final class StartupUtil {
}
}
}
// do not crash AWT on exceptions
AWTExceptionHandler.register();
activity.end();
}
@@ -111,21 +111,12 @@ public class ApplicationImpl extends ComponentManagerImpl implements Application
}
public ApplicationImpl(boolean isInternal, boolean isUnitTestMode, boolean isHeadless, boolean isCommandLine) {
this(isInternal, isUnitTestMode, isHeadless, isCommandLine, null);
}
public ApplicationImpl(boolean isInternal, boolean isUnitTestMode, boolean isHeadless, boolean isCommandLine, Consumer<ApplicationImpl> initTask) {
super(null);
registerServiceInstance(TransactionGuard.class, myTransactionGuard, ComponentManagerImpl.getFakeCorePluginDescriptor());
registerServiceInstance(ApplicationInfo.class, ApplicationInfoImpl.getShadowInstance(), ComponentManagerImpl.getFakeCorePluginDescriptor());
registerServiceInstance(Application.class, this, ComponentManagerImpl.getFakeCorePluginDescriptor());
if (initTask != null) {
// myLock is a final field, we cannot do init in EDT and set it later
initTask.accept(this);
}
if (isUnitTestMode || isInternal) {
BundleBase.assertOnMissedKeys(true);
}
@@ -147,6 +138,7 @@ public class ApplicationImpl extends ComponentManagerImpl implements Application
if (!isUnitTestMode && !isHeadless) {
Disposable uiRootDisposable = Disposer.newDisposable();
//noinspection deprecation
Disposer.register(this, uiRootDisposable, "ui");
}
@@ -155,7 +147,7 @@ public class ApplicationImpl extends ComponentManagerImpl implements Application
EdtInvocationManager.invokeAndWaitIfNeeded(() -> {
// instantiate AppDelayQueue which starts "Periodic task thread" which we'll mark busy to prevent this EDT to die
// that thread was chosen because we know for sure it's running
Thread thread = ((AppScheduledExecutorService)AppExecutorUtil.getAppScheduledExecutorService()).getPeriodicTasksThread();
Thread thread = AppScheduledExecutorService.getPeriodicTasksThread();
AWTAutoShutdown.getInstance().notifyThreadBusy(thread); // needed for EDT not to exit suddenly
Disposer.register(this, () -> {
AWTAutoShutdown.getInstance().notifyThreadFree(thread); // allow for EDT to exit - needed for Upsource
@@ -172,6 +164,47 @@ public class ApplicationImpl extends ComponentManagerImpl implements Application
NoSwingUnderWriteAction.watchForEvents(this);
}
// this constructor must be called only by ApplicationLoader
public ApplicationImpl(boolean isInternal,
boolean isUnitTestMode,
boolean isHeadless,
boolean isCommandLine,
@NotNull Thread edtThread) {
super(null);
registerServiceInstance(TransactionGuard.class, myTransactionGuard, ComponentManagerImpl.getFakeCorePluginDescriptor());
registerServiceInstance(ApplicationInfo.class, ApplicationInfoImpl.getShadowInstance(), ComponentManagerImpl.getFakeCorePluginDescriptor());
registerServiceInstance(Application.class, this, ComponentManagerImpl.getFakeCorePluginDescriptor());
Disposer.setDebugMode(isInternal || isUnitTestMode || Disposer.isDebugDisposerOn());
myIsInternal = isInternal;
myTestModeFlag = isUnitTestMode;
myHeadlessMode = isHeadless;
myCommandLineMode = isCommandLine;
mySaveAllowed = !(isUnitTestMode || isHeadless);
if (!isUnitTestMode && !isHeadless) {
Disposable uiRootDisposable = Disposer.newDisposable();
//noinspection deprecation
Disposer.register(this, uiRootDisposable, "ui");
}
Activity activity = StartUpMeasurer.startActivity("AppDelayQueue instantiation", ActivityCategory.DEFAULT);
myLock = new ReadMostlyRWLock(edtThread);
// Acquire IW lock on EDT indefinitely in legacy mode
if (!USE_SEPARATE_WRITE_THREAD || isUnitTestMode) {
EventQueue.invokeLater(() -> acquireWriteIntentLock(getClass()));
}
activity.end();
NoSwingUnderWriteAction.watchForEvents(this);
// reset back to null only when all components already disposed
ApplicationManager.setApplication(this, myLastDisposable);
}
/**
* Executes a {@code runnable} in an "impatient" mode.
* In this mode any attempt to call {@link #runReadAction(Runnable)}
@@ -1,4 +1,4 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.openapi.application.impl;
import com.intellij.ide.IdeEventQueue;
@@ -15,20 +15,20 @@ import java.util.concurrent.atomic.AtomicBoolean;
final class NoSwingUnderWriteAction {
private static final Logger LOG = Logger.getInstance(NoSwingUnderWriteAction.class);
static void watchForEvents(Application application) {
static void watchForEvents(@NotNull Application app) {
AtomicBoolean reported = new AtomicBoolean();
IdeEventQueue.getInstance().addPostprocessor(e -> {
if (application.isWriteAccessAllowed() && reported.compareAndSet(false, true)) {
if (app.isWriteAccessAllowed() && reported.compareAndSet(false, true)) {
LOG.error("AWT events are not allowed inside write action: " + e);
}
return true;
}, application);
}, app);
application.addApplicationListener(new ApplicationListener() {
app.addApplicationListener(new ApplicationListener() {
@Override
public void afterWriteActionFinished(@NotNull Object action) {
reported.set(false);
}
}, application);
}, app);
}
}
@@ -1,4 +1,4 @@
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.util.concurrency;
import com.intellij.openapi.diagnostic.Logger;
@@ -27,16 +27,15 @@ public final class AppScheduledExecutorService extends SchedulingWrapper {
private final LowMemoryWatcherManager myLowMemoryWatcherManager;
private final MyThreadFactory myCountingThreadFactory;
private static class Holder {
private static final class Holder {
private static final AppScheduledExecutorService INSTANCE = new AppScheduledExecutorService("Global instance", 1, TimeUnit.MINUTES);
}
@NotNull
static ScheduledExecutorService getInstance() {
static @NotNull ScheduledExecutorService getInstance() {
return Holder.INSTANCE;
}
private static class MyThreadFactory extends CountingThreadFactory {
private static final class MyThreadFactory extends CountingThreadFactory {
private BiConsumer<? super Thread, ? super Runnable> newThreadListener;
private final ThreadFactory myThreadFactory = Executors.privilegedThreadFactory();
@@ -195,9 +194,8 @@ public final class AppScheduledExecutorService extends SchedulingWrapper {
}
}
@NotNull
public Thread getPeriodicTasksThread() {
return delayQueue.getThread();
public static @NotNull Thread getPeriodicTasksThread() {
return Holder.INSTANCE.delayQueue.getThread();
}
@TestOnly
@@ -1,9 +1,10 @@
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.util.ui;
import com.intellij.openapi.diagnostic.Logger;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
@@ -33,6 +34,11 @@ public final class EDT {
return thread == myEventDispatchThread;
}
@ApiStatus.Internal
public static @Nullable Thread getEventDispatchThread() {
return myEventDispatchThread;
}
/**
* Checks whether the current thread is EDT.
*