2 * Copyright 2000-2014 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.openapi.application.impl;
18 import com.intellij.BundleBase;
19 import com.intellij.CommonBundle;
20 import com.intellij.diagnostic.ThreadDumper;
21 import com.intellij.ide.*;
22 import com.intellij.ide.plugins.IdeaPluginDescriptor;
23 import com.intellij.ide.plugins.PluginManagerCore;
24 import com.intellij.idea.IdeaApplication;
25 import com.intellij.idea.Main;
26 import com.intellij.idea.StartupUtil;
27 import com.intellij.openapi.Disposable;
28 import com.intellij.openapi.application.*;
29 import com.intellij.openapi.application.ex.ApplicationEx;
30 import com.intellij.openapi.command.CommandProcessor;
31 import com.intellij.openapi.components.StateStorageException;
32 import com.intellij.openapi.components.impl.ApplicationPathMacroManager;
33 import com.intellij.openapi.components.impl.PlatformComponentManagerImpl;
34 import com.intellij.openapi.components.impl.stores.IApplicationStore;
35 import com.intellij.openapi.components.impl.stores.IComponentStore;
36 import com.intellij.openapi.components.impl.stores.StoreUtil;
37 import com.intellij.openapi.components.impl.stores.StoresFactory;
38 import com.intellij.openapi.diagnostic.Logger;
39 import com.intellij.openapi.extensions.ExtensionPointName;
40 import com.intellij.openapi.extensions.Extensions;
41 import com.intellij.openapi.fileEditor.FileDocumentManager;
42 import com.intellij.openapi.progress.EmptyProgressIndicator;
43 import com.intellij.openapi.progress.ProcessCanceledException;
44 import com.intellij.openapi.progress.ProgressIndicator;
45 import com.intellij.openapi.progress.ProgressManager;
46 import com.intellij.openapi.progress.util.ProgressWindow;
47 import com.intellij.openapi.project.Project;
48 import com.intellij.openapi.project.ProjectManager;
49 import com.intellij.openapi.project.ex.ProjectEx;
50 import com.intellij.openapi.project.ex.ProjectManagerEx;
51 import com.intellij.openapi.project.impl.ProjectManagerImpl;
52 import com.intellij.openapi.ui.DialogWrapper;
53 import com.intellij.openapi.ui.MessageDialogBuilder;
54 import com.intellij.openapi.ui.Messages;
55 import com.intellij.openapi.util.*;
56 import com.intellij.openapi.util.io.FileUtil;
57 import com.intellij.openapi.util.text.StringUtil;
58 import com.intellij.openapi.vfs.CharsetToolkit;
59 import com.intellij.openapi.wm.IdeFrame;
60 import com.intellij.openapi.wm.WindowManager;
61 import com.intellij.psi.PsiLock;
62 import com.intellij.ui.Splash;
63 import com.intellij.util.*;
64 import com.intellij.util.containers.Stack;
65 import com.intellij.util.io.storage.HeavyProcessLatch;
66 import com.intellij.util.ui.UIUtil;
67 import org.jetbrains.annotations.NonNls;
68 import org.jetbrains.annotations.NotNull;
69 import org.jetbrains.annotations.Nullable;
70 import org.jetbrains.annotations.TestOnly;
71 import org.jetbrains.ide.PooledThreadExecutor;
72 import org.picocontainer.MutablePicoContainer;
77 import java.io.IOException;
78 import java.lang.reflect.Method;
79 import java.util.List;
80 import java.util.concurrent.Callable;
81 import java.util.concurrent.ExecutorService;
82 import java.util.concurrent.Future;
83 import java.util.concurrent.TimeUnit;
84 import java.util.concurrent.atomic.AtomicBoolean;
85 import java.util.concurrent.locks.ReentrantReadWriteLock;
87 public class ApplicationImpl extends PlatformComponentManagerImpl implements ApplicationEx {
88 private static final Logger LOG = Logger.getInstance("#com.intellij.application.impl.ApplicationImpl");
89 private final ModalityState MODALITY_STATE_NONE = ModalityState.NON_MODAL;
91 // about writer preference: the way the j.u.c.l.ReentrantReadWriteLock.NonfairSync is implemented, the
92 // writer thread will be always at the queue head and therefore, j.u.c.l.ReentrantReadWriteLock.NonfairSync.readerShouldBlock()
93 // will return true if the write action is pending, exactly as we need
94 private final ReentrantReadWriteLock myLock = new ReentrantReadWriteLock(false);
96 private final ModalityInvokator myInvokator = new ModalityInvokatorImpl();
98 private final EventDispatcher<ApplicationListener> myDispatcher = EventDispatcher.create(ApplicationListener.class);
100 private IApplicationStore myComponentStore;
102 private final boolean myTestModeFlag;
103 private final boolean myHeadlessMode;
104 private final boolean myCommandLineMode;
106 private final boolean myIsInternal;
107 private final String myName;
109 private final Stack<Class> myWriteActionsStack = new Stack<Class>(); // accessed from EDT only, no need to sync
111 private int myInEditorPaintCounter; // EDT only
112 private final long myStartTime;
114 private final Splash mySplash;
115 private boolean myDoNotSave;
116 private volatile boolean myDisposeInProgress;
118 private final Disposable myLastDisposable = Disposer.newDisposable(); // will be disposed last
120 private final AtomicBoolean mySaveSettingsIsInProgress = new AtomicBoolean(false);
122 private final ExecutorService ourThreadExecutorsService = PooledThreadExecutor.INSTANCE;
123 private boolean myIsFiringLoadingEvent = false;
124 private boolean myLoaded = false;
125 @NonNls private static final String WAS_EVER_SHOWN = "was.ever.shown";
127 private Boolean myActive;
129 private static final int IS_EDT_FLAG = 1<<30; // we don't mess with sign bit since we want to do arithmetic
130 private static final int IS_READ_LOCK_ACQUIRED_FLAG = 1<<29;
131 private static class Status {
132 // higher three bits are for IS_* flags
133 // lower bits are for edtSafe counter
137 private static final ThreadLocal<Status> status = new ThreadLocal<Status>(){
139 protected Status initialValue() {
140 Status status = new Status();
141 status.flags = BitUtil.set(status.flags, IS_EDT_FLAG, EventQueue.isDispatchThread());
145 private static Status getStatus() {
148 private static void setReadLockAcquired(Status status, boolean acquired) {
149 status.flags = BitUtil.set(status.flags, IS_READ_LOCK_ACQUIRED_FLAG, acquired);
152 private static final ModalityState ANY = new ModalityState() {
154 public boolean dominates(@NotNull ModalityState anotherState) {
160 public String toString() {
166 protected void bootstrapPicoContainer(@NotNull String name) {
167 super.bootstrapPicoContainer(name);
168 getPicoContainer().registerComponentImplementation(IComponentStore.class, StoresFactory.getApplicationStoreClass());
169 getPicoContainer().registerComponentImplementation(ApplicationPathMacroManager.class);
173 public synchronized IApplicationStore getStateStore() {
174 if (myComponentStore == null) {
175 myComponentStore = (IApplicationStore)getPicoContainer().getComponentInstance(IComponentStore.class);
177 return myComponentStore;
181 public void initializeComponent(@NotNull Object component, boolean service) {
182 getStateStore().initComponent(component, service);
185 public ApplicationImpl(boolean isInternal,
186 boolean isUnitTestMode,
188 boolean isCommandLine,
189 @NotNull String appName,
190 @Nullable Splash splash) {
193 ApplicationManager.setApplication(this, myLastDisposable); // reset back to null only when all components already disposed
195 getPicoContainer().registerComponentInstance(Application.class, this);
197 BundleBase.assertKeyIsFound = IconLoader.STRICT = isUnitTestMode || isInternal;
199 AWTExceptionHandler.register(); // do not crash AWT on exceptions
201 String debugDisposer = System.getProperty("idea.disposer.debug");
202 Disposer.setDebugMode((isInternal || isUnitTestMode || "on".equals(debugDisposer)) && !"off".equals(debugDisposer));
204 myStartTime = System.currentTimeMillis();
208 myIsInternal = isInternal;
209 myTestModeFlag = isUnitTestMode;
210 myHeadlessMode = isHeadless;
211 myCommandLineMode = isCommandLine;
213 myDoNotSave = isUnitTestMode || isHeadless;
215 loadApplicationComponents();
217 if (myTestModeFlag) {
218 registerShutdownHook();
221 if (!isUnitTestMode && !isHeadless) {
222 Disposer.register(this, Disposer.newDisposable(), "ui");
224 StartupUtil.addExternalInstanceListener(new Consumer<List<String>>() {
226 public void consume(final List<String> args) {
227 invokeLater(new Runnable() {
230 LOG.info("ApplicationImpl.externalInstanceListener invocation");
231 String currentDirectory = args.isEmpty() ? null : args.get(0);
232 List<String> realArgs = args.isEmpty() ? args : args.subList(1, args.size());
233 final Project project = CommandLineProcessor.processExternalCommandLine(realArgs, currentDirectory);
235 if (project != null) {
236 frame = (JFrame)WindowManager.getInstance().getIdeFrame(project);
239 frame = WindowManager.getInstance().findVisibleFrame();
241 if (frame != null) frame.requestFocus();
247 WindowsCommandLineProcessor.LISTENER = new WindowsCommandLineListener() {
249 public void processWindowsLauncherCommandLine(final String currentDirectory, final String commandLine) {
250 LOG.info("Received external Windows command line: current directory " + currentDirectory + ", command line " + commandLine);
251 invokeLater(new Runnable() {
254 final List<String> args = StringUtil.splitHonorQuotes(commandLine, ' ');
255 args.remove(0); // process name
256 CommandLineProcessor.processExternalCommandLine(args, currentDirectory);
262 if (isUnitTestMode && IdeaApplication.getInstance() == null) {
263 String[] args = {"inspect", "", "", ""};
264 Main.setFlags(args); // set both isHeadless and isCommandLine to true
265 System.setProperty(IdeaApplication.IDEA_IS_UNIT_TEST, Boolean.TRUE.toString());
266 assert Main.isHeadless();
267 assert Main.isCommandLine();
268 new IdeaApplication(args);
272 private void registerShutdownHook() {
273 ShutDownTracker.getInstance().registerShutdownTask(new Runnable() {
276 if (isDisposed() || myDisposeInProgress) {
279 ShutDownTracker.invokeAndWait(isUnitTestMode(), true, new Runnable() {
282 if (ApplicationManager.getApplication() != ApplicationImpl.this) return;
284 myDisposeInProgress = true;
288 if (!disposeSelf(true)) {
289 myDisposeInProgress = false;
298 private boolean disposeSelf(final boolean checkCanCloseProject) {
299 final ProjectManagerImpl manager = (ProjectManagerImpl)ProjectManagerEx.getInstanceEx();
300 if (manager != null) {
301 final boolean[] canClose = {true};
302 for (final Project project : manager.getOpenProjects()) {
304 CommandProcessor.getInstance().executeCommand(project, new Runnable() {
307 if (!manager.closeProject(project, true, true, checkCanCloseProject)) {
311 }, ApplicationBundle.message("command.exit"), null);
313 catch (Throwable e) {
321 runWriteAction(new Runnable() {
324 Disposer.dispose(ApplicationImpl.this);
328 Disposer.assertIsEmpty();
334 public String getName() {
339 public boolean holdsReadLock() {
340 return holdsReadLock(getStatus());
343 private static boolean holdsReadLock(Status status) {
344 return BitUtil.isSet(status.flags, IS_READ_LOCK_ACQUIRED_FLAG);
347 private void loadApplicationComponents() {
348 PluginManagerCore.initPlugins(mySplash);
349 IdeaPluginDescriptor[] plugins = PluginManagerCore.getPlugins();
350 for (IdeaPluginDescriptor plugin : plugins) {
351 if (!PluginManagerCore.shouldSkipPlugin(plugin)) {
352 loadComponentsConfiguration(plugin.getAppComponents(), plugin, false);
358 protected synchronized Object createComponent(@NotNull Class componentInterface) {
359 Object component = super.createComponent(componentInterface);
360 if (mySplash != null) {
361 mySplash.showProgress("", 0.65f + getPercentageOfComponentsLoaded() * 0.35f);
368 protected MutablePicoContainer createPicoContainer() {
369 return Extensions.getRootArea().getPicoContainer();
373 public boolean isInternal() {
378 public boolean isEAP() {
379 return ApplicationInfoImpl.getShadowInstance().isEAP();
383 public boolean isUnitTestMode() {
384 return myTestModeFlag;
388 public boolean isHeadlessEnvironment() {
389 return myHeadlessMode;
393 public boolean isCommandLine() {
394 return myCommandLineMode;
399 public Future<?> executeOnPooledThread(@NotNull final Runnable action) {
400 return ourThreadExecutorsService.submit(new Runnable() {
403 assert !isReadAccessAllowed(): describe(Thread.currentThread());
407 catch (ProcessCanceledException e) {
410 catch (Throwable t) {
414 //ReflectionUtil.resetThreadLocals();
415 Thread.interrupted(); // reset interrupted status
416 assert !isReadAccessAllowed(): describe(Thread.currentThread());
424 public <T> Future<T> executeOnPooledThread(@NotNull final Callable<T> action) {
425 return ourThreadExecutorsService.submit(new Callable<T>() {
428 assert !isReadAccessAllowed(): describe(Thread.currentThread());
430 return action.call();
432 catch (ProcessCanceledException e) {
435 catch (Throwable t) {
439 //ReflectionUtil.resetThreadLocals();
440 Thread.interrupted(); // reset interrupted status
441 assert !isReadAccessAllowed(): describe(Thread.currentThread());
449 public boolean isDispatchThread() {
450 return isDispatchThread(getStatus());
453 private static boolean isDispatchThread(Status status) {
454 return BitUtil.isSet(status.flags, IS_EDT_FLAG);
459 public ModalityInvokator getInvokator() {
465 public void invokeLater(@NotNull final Runnable runnable) {
466 myInvokator.invokeLater(runnable);
470 public void invokeLater(@NotNull final Runnable runnable, @NotNull final Condition expired) {
471 myInvokator.invokeLater(runnable, expired);
475 public void invokeLater(@NotNull final Runnable runnable, @NotNull final ModalityState state) {
476 myInvokator.invokeLater(runnable, state);
480 public void invokeLater(@NotNull final Runnable runnable, @NotNull final ModalityState state, @NotNull final Condition expired) {
481 myInvokator.invokeLater(runnable, state, expired);
485 public void load(@Nullable String optionsPath) throws IOException {
486 load(PathManager.getConfigPath(), optionsPath == null ? PathManager.getOptionsPath() : optionsPath);
489 public void load(@NotNull String configPath, @NotNull String optionsPath) throws IOException {
490 IApplicationStore store = getStateStore();
491 store.setOptionsPath(optionsPath);
492 store.setConfigPath(configPath);
494 myIsFiringLoadingEvent = true;
496 fireBeforeApplicationLoaded();
499 myIsFiringLoadingEvent = false;
502 AccessToken token = HeavyProcessLatch.INSTANCE.processStarted("Loading application components");
506 catch (StateStorageException e) {
507 throw new IOException(e.getMessage());
517 private static void createLocatorFile() {
518 File locatorFile = new File(PathManager.getSystemPath() + "/" + ApplicationEx.LOCATOR_FILE_NAME);
520 byte[] data = PathManager.getHomePath().getBytes(CharsetToolkit.UTF8_CHARSET);
521 FileUtil.writeToFile(locatorFile, data);
523 catch (IOException e) {
524 LOG.warn("can't store a location in '" + locatorFile + "'", e);
529 public boolean isLoaded() {
534 protected <T> T getComponentFromContainer(@NotNull final Class<T> interfaceClass) {
535 if (myIsFiringLoadingEvent) {
538 return super.getComponentFromContainer(interfaceClass);
541 private void fireBeforeApplicationLoaded() {
542 for (ApplicationLoadListener listener : ApplicationLoadListener.EP_NAME.getExtensions()) {
544 listener.beforeApplicationLoaded(this);
546 catch (Exception e) {
553 public void dispose() {
554 fireApplicationExiting();
556 ShutDownTracker.getInstance().ensureStopperThreadsFinished();
560 ourThreadExecutorsService.shutdownNow();
561 myComponentStore = null;
563 Disposer.dispose(myLastDisposable); // dispose it last
567 public boolean runProcessWithProgressSynchronously(@NotNull final Runnable process,
568 @NotNull String progressTitle,
569 boolean canBeCanceled,
571 return runProcessWithProgressSynchronously(process, progressTitle, canBeCanceled, project, null);
575 public boolean runProcessWithProgressSynchronously(@NotNull final Runnable process,
576 @NotNull final String progressTitle,
577 final boolean canBeCanceled,
578 @Nullable final Project project,
579 final JComponent parentComponent) {
580 return runProcessWithProgressSynchronously(process, progressTitle, canBeCanceled, project, parentComponent, null);
584 public boolean runProcessWithProgressSynchronously(@NotNull final Runnable process,
585 @NotNull final String progressTitle,
586 final boolean canBeCanceled,
587 @Nullable final Project project,
588 final JComponent parentComponent,
589 final String cancelText) {
590 assertIsDispatchThread();
591 boolean writeAccessAllowed = isInsideWriteActionEDTOnly();
592 if (writeAccessAllowed // Disallow running process in separate thread from under write action.
593 // The thread will deadlock trying to get read action otherwise.
594 || isHeadlessEnvironment() && !isUnitTestMode()
596 LOG.debug("Starting process with progress from within write action makes no sense");
598 ProgressManager.getInstance().runProcess(process, new EmptyProgressIndicator());
600 catch (ProcessCanceledException e) {
607 final ProgressWindow progress = new ProgressWindow(canBeCanceled, false, project, parentComponent, cancelText);
608 // in case of abrupt application exit when 'ProgressManager.getInstance().runProcess(process, progress)' below
609 // does not have a chance to run, and as a result the progress won't be disposed
610 Disposer.register(this, progress);
612 progress.setTitle(progressTitle);
614 final AtomicBoolean threadStarted = new AtomicBoolean();
615 //noinspection SSBasedInspection
616 SwingUtilities.invokeLater(new Runnable() {
619 executeOnPooledThread(new Runnable() {
623 ProgressManager.getInstance().runProcess(process, progress);
625 catch (ProcessCanceledException e) {
629 catch (RuntimeException e) {
635 threadStarted.set(true);
639 progress.startBlocking();
641 LOG.assertTrue(threadStarted.get());
642 LOG.assertTrue(!progress.isRunning());
644 return !progress.isCanceled();
648 public void invokeAndWait(@NotNull Runnable runnable, @NotNull ModalityState modalityState) {
649 Status status = getStatus();
650 if (isDispatchThread(status)) {
655 if (holdsReadLock()) {
656 LOG.error("Calling invokeAndWait from read-action leads to possible deadlock.");
659 LaterInvocator.invokeAndWait(runnable, modalityState);
664 public ModalityState getCurrentModalityState() {
665 Object[] entities = LaterInvocator.getCurrentModalEntities();
666 return entities.length > 0 ? new ModalityStateEx(entities) : getNoneModalityState();
671 public ModalityState getModalityStateForComponent(@NotNull Component c) {
672 Window window = UIUtil.getWindow(c);
673 if (window == null) return getNoneModalityState(); //?
674 return LaterInvocator.modalityStateForWindow(window);
679 public ModalityState getAnyModalityState() {
685 public ModalityState getDefaultModalityState() {
686 if (isDispatchThread()) {
687 return getCurrentModalityState();
689 ProgressIndicator progress = ProgressManager.getInstance().getProgressIndicator();
690 return progress == null ? getNoneModalityState() : progress.getModalityState();
695 public ModalityState getNoneModalityState() {
696 return MODALITY_STATE_NONE;
700 public long getStartTime() {
705 public long getIdleTime() {
706 assertIsDispatchThread();
707 return IdeEventQueue.getInstance().getIdleTime();
716 public void exit(boolean force, final boolean exitConfirmed) {
717 exit(false, exitConfirmed, true, false);
721 public void restart() {
726 public void restart(final boolean exitConfirmed) {
727 exit(false, exitConfirmed, true, true);
731 * There are two ways we can get an exit notification.
732 * 1. From user input i.e. ExitAction
733 * 2. From the native system.
734 * We should not process any quit notifications if we are handling another one
736 * Note: there are possible scenarios when we get a quit notification at a moment when another
737 * quit message is shown. In that case, showing multiple messages sounds contra-intuitive as well
739 private static volatile boolean exiting = false;
741 public void exit(final boolean force, final boolean exitConfirmed, final boolean allowListenersToCancel, final boolean restart) {
742 if (!force && exiting) {
748 if (!force && !exitConfirmed && getDefaultModalityState() != ModalityState.NON_MODAL) {
752 Runnable runnable = new Runnable() {
755 if (!force && !confirmExitIfNeeded(exitConfirmed)) {
760 getMessageBus().syncPublisher(AppLifecycleListener.TOPIC).appClosing();
761 myDisposeInProgress = true;
762 doExit(allowListenersToCancel, restart);
763 myDisposeInProgress = false;
767 if (isDispatchThread()) {
771 invokeLater(runnable, ModalityState.NON_MODAL);
779 private boolean doExit(boolean allowListenersToCancel, boolean restart) {
782 if (allowListenersToCancel && !canExit()) {
786 final boolean success = disposeSelf(allowListenersToCancel);
787 if (!success || isUnitTestMode()) {
792 if (restart && Restarter.isSupported()) {
794 exitCode = Restarter.scheduleRestart();
796 catch (IOException e) {
797 LOG.warn("Cannot restart", e);
800 System.exit(exitCode);
804 private static boolean confirmExitIfNeeded(boolean exitConfirmed) {
805 final boolean hasUnsafeBgTasks = ProgressManager.getInstance().hasUnsafeProgressIndicator();
806 if (exitConfirmed && !hasUnsafeBgTasks) {
810 DialogWrapper.DoNotAskOption option = new DialogWrapper.DoNotAskOption() {
812 public boolean isToBeShown() {
813 return GeneralSettings.getInstance().isConfirmExit() && ProjectManager.getInstance().getOpenProjects().length > 0;
817 public void setToBeShown(boolean value, int exitCode) {
818 GeneralSettings.getInstance().setConfirmExit(value);
822 public boolean canBeHidden() {
823 return !hasUnsafeBgTasks;
827 public boolean shouldSaveOptionsOnCancel() {
833 public String getDoNotShowMessage() {
834 return "Do not ask me again";
838 if (hasUnsafeBgTasks || option.isToBeShown()) {
839 String message = ApplicationBundle
840 .message(hasUnsafeBgTasks ? "exit.confirm.prompt.tasks" : "exit.confirm.prompt",
841 ApplicationNamesInfo.getInstance().getFullProductName());
843 if (MessageDialogBuilder.yesNo(ApplicationBundle.message("exit.confirm.title"), message).yesText(ApplicationBundle.message("command.exit")).noText(CommonBundle.message("button.cancel"))
844 .doNotAsk(option).show() != Messages.YES) {
851 private boolean canExit() {
852 for (ApplicationListener applicationListener : myDispatcher.getListeners()) {
853 if (!applicationListener.canExitApplication()) {
858 ProjectManagerEx projectManager = (ProjectManagerEx)ProjectManager.getInstance();
859 Project[] projects = projectManager.getOpenProjects();
860 for (Project project : projects) {
861 if (!projectManager.canClose(project)) {
870 public void runReadAction(@NotNull final Runnable action) {
871 Status status = getStatus();
872 if (isReadAccessAllowed(status)) {
887 public <T> T runReadAction(@NotNull final Computable<T> computation) {
888 Status status = getStatus();
889 if (isReadAccessAllowed(status)) {
890 return computation.compute();
894 return computation.compute();
902 public <T, E extends Throwable> T runReadAction(@NotNull ThrowableComputable<T, E> computation) throws E {
903 Status status = getStatus();
904 if (isReadAccessAllowed(status)) {
905 return computation.compute();
909 return computation.compute();
916 private void startRead(Status status) {
919 myLock.readLock().lockInterruptibly();
920 setReadLockAcquired(status, true);
922 catch (InterruptedException e) {
923 throw new RuntimeInterruptedException(e);
927 private void endRead(Status status) {
928 setReadLockAcquired(status, false);
929 myLock.readLock().unlock();
933 public void runWriteAction(@NotNull final Runnable action) {
934 Class<? extends Runnable> clazz = action.getClass();
945 public <T> T runWriteAction(@NotNull final Computable<T> computation) {
946 Class<? extends Computable> clazz = computation.getClass();
949 return computation.compute();
957 public <T, E extends Throwable> T runWriteAction(@NotNull ThrowableComputable<T, E> computation) throws E {
958 Class<? extends ThrowableComputable> clazz = computation.getClass();
961 return computation.compute();
969 public boolean hasWriteAction(@Nullable Class<?> actionClass) {
970 assertIsDispatchThread();
972 for (int i = myWriteActionsStack.size() - 1; i >= 0; i--) {
973 Class action = myWriteActionsStack.get(i);
974 if (actionClass == action || action != null && actionClass != null && ReflectionUtil.isAssignable(actionClass, action)) return true;
980 public void assertReadAccessAllowed() {
981 if (!isReadAccessAllowed()) {
983 "Read access is allowed from event dispatch thread or inside read-action only" +
984 " (see com.intellij.openapi.application.Application.runReadAction())",
985 "Current thread: " + describe(Thread.currentThread()), "; dispatch thread: " + EventQueue.isDispatchThread() +"; isDispatchThread(): "+isDispatchThread(),
986 "SystemEventQueueThread: " + describe(getEventQueueThread()));
991 private static String describe(Thread o) {
992 if (o == null) return "null";
993 return o + " " + System.identityHashCode(o);
996 private static Thread getEventQueueThread() {
997 EventQueue eventQueue = Toolkit.getDefaultToolkit().getSystemEventQueue();
999 Method method = ReflectionUtil.getDeclaredMethod(EventQueue.class, "getDispatchThread");
1000 return (Thread)method.invoke(eventQueue);
1002 catch (Exception e) {
1003 throw new RuntimeException(e);
1008 public boolean isReadAccessAllowed() {
1009 return isReadAccessAllowed(getStatus());
1012 private static boolean isReadAccessAllowed(Status status) {
1013 return (status.flags & (IS_EDT_FLAG | IS_READ_LOCK_ACQUIRED_FLAG)) != 0;
1017 public void assertIsDispatchThread() {
1018 assertIsDispatchThread(getStatus());
1021 private static void assertIsDispatchThread(Status status) {
1022 if (isDispatchThread(status)) return;
1023 if (ShutDownTracker.isShutdownHookRunning()) return;
1024 int safeCounter = getSafeCounter(status);
1025 if (safeCounter == 0) {
1026 assertIsDispatchThread(status, "Access is allowed from event dispatch thread only.");
1030 private static int getSafeCounter(Status status) {
1031 return status.flags & 0x1fffffff;
1034 private static void assertIsDispatchThread(Status status, @NotNull String message) {
1035 if (isDispatchThread(status)) return;
1037 "EventQueue.isDispatchThread()="+EventQueue.isDispatchThread(),
1038 "isDispatchThread()="+isDispatchThread(getStatus()),
1039 "Toolkit.getEventQueue()="+Toolkit.getDefaultToolkit().getSystemEventQueue(),
1040 "Current thread: " + describe(Thread.currentThread()),
1041 "SystemEventQueueThread: " + describe(getEventQueueThread()) +"\n"+ ThreadDumper.dumpThreadsToString()+"\n-----------");
1045 public void runEdtSafeAction(@NotNull Runnable runnable) {
1046 Status status = getStatus();
1047 LOG.assertTrue(getSafeCounter(status) < 1<<26);
1059 public void assertIsDispatchThread(@Nullable final JComponent component) {
1060 if (component == null) return;
1062 Status status = getStatus();
1063 if (isDispatchThread(status)) {
1067 if (Boolean.TRUE.equals(component.getClientProperty(WAS_EVER_SHOWN))) {
1068 assertIsDispatchThread(status);
1071 final JRootPane root = component.getRootPane();
1073 component.putClientProperty(WAS_EVER_SHOWN, Boolean.TRUE);
1074 assertIsDispatchThread(status);
1080 public void assertTimeConsuming() {
1081 if (myTestModeFlag || myHeadlessMode || ShutDownTracker.isShutdownHookRunning()) return;
1082 LOG.assertTrue(!isDispatchThread(), "This operation is time consuming and must not be called on EDT");
1086 public boolean tryRunReadAction(@NotNull Runnable action) {
1087 Status status = getStatus();
1088 //if we are inside read action, do not try to acquire read lock again since it will deadlock if there is a pending writeAction
1089 boolean mustAcquire = !isReadAccessAllowed(status);
1094 // timed version of tryLock() respects fairness unlike the no-args method
1095 if (!myLock.readLock().tryLock(0, TimeUnit.MILLISECONDS)) return false;
1096 setReadLockAcquired(status, true);
1098 catch (InterruptedException e) {
1099 throw new RuntimeInterruptedException(e);
1114 public boolean tryToApplyActivationState(boolean active, Window window) {
1115 final Component frame = UIUtil.findUltimateParent(window);
1117 if (frame instanceof IdeFrame) {
1118 final IdeFrame ideFrame = (IdeFrame)frame;
1119 if (isActive() != active) {
1121 System.setProperty("idea.active", myActive.toString());
1122 ApplicationActivationListener publisher = getMessageBus().syncPublisher(ApplicationActivationListener.TOPIC);
1124 publisher.applicationActivated(ideFrame);
1127 publisher.applicationDeactivated(ideFrame);
1137 public boolean isActive() {
1138 if (isUnitTestMode()) return true;
1140 if (myActive == null) {
1141 Window active = KeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow();
1142 return active != null;
1150 public AccessToken acquireReadActionLock() {
1151 Status status = getStatus();
1152 // if we are inside read action, do not try to acquire read lock again since it will deadlock if there is a pending writeAction
1153 if (isReadAccessAllowed(status)) return AccessToken.EMPTY_ACCESS_TOKEN;
1155 return new ReadAccessToken(status);
1158 private volatile boolean myWriteActionPending;
1161 public boolean isWriteActionPending() {
1162 return myWriteActionPending;
1165 private void startWrite(Class clazz) {
1166 boolean writeActionPending = myWriteActionPending;
1167 myWriteActionPending = true;
1170 ActivityTracker.getInstance().inc();
1171 fireBeforeWriteActionStart(clazz);
1174 if (!isWriteAccessAllowed()) {
1177 if (!myLock.writeLock().tryLock()) {
1178 final AtomicBoolean lockAcquired = new AtomicBoolean(false);
1179 myLock.writeLock().lockInterruptibly();
1180 lockAcquired.set(true);
1183 catch (InterruptedException e) {
1184 throw new RuntimeInterruptedException(e);
1188 myWriteActionPending = writeActionPending;
1191 myWriteActionsStack.push(clazz);
1192 fireWriteActionStarted(clazz);
1195 private void endWrite(Class clazz) {
1197 myWriteActionsStack.pop();
1198 fireWriteActionFinished(clazz);
1201 myLock.writeLock().unlock();
1207 public AccessToken acquireWriteActionLock(Class clazz) {
1208 assertIsDispatchThread(getStatus(), "Write access is allowed from event dispatch thread only");
1209 return new WriteAccessToken(clazz);
1212 private class WriteAccessToken extends AccessToken {
1213 private final Class clazz;
1215 public WriteAccessToken(Class clazz) {
1218 markThreadNameInStackTrace();
1223 public void finish() {
1228 unmarkThreadNameInStackTrace();
1233 private void markThreadNameInStackTrace() {
1237 final Thread thread = Thread.currentThread();
1238 thread.setName(thread.getName() + id);
1242 private void unmarkThreadNameInStackTrace() {
1246 final Thread thread = Thread.currentThread();
1247 String name = thread.getName();
1248 name = StringUtil.replace(name, id, "");
1249 thread.setName(name);
1253 private String id() {
1254 Class aClass = getClass();
1255 String name = aClass.getName();
1256 while (name == null) {
1257 aClass = aClass.getSuperclass();
1258 name = aClass.getName();
1261 name = name.substring(name.lastIndexOf('.') + 1);
1262 name = name.substring(name.lastIndexOf('$') + 1);
1263 if (!name.equals("AccessToken")) {
1264 return " [" + name+"]";
1270 private class ReadAccessToken extends AccessToken {
1271 private final Status myStatus;
1273 private ReadAccessToken(Status status) {
1280 public void finish() {
1286 private final boolean myExtraChecks = isUnitTestMode();
1288 private void assertNoPsiLock() {
1289 if (myExtraChecks) {
1290 LOG.assertTrue(!Thread.holdsLock(PsiLock.LOCK), "Thread must not hold PsiLock while performing readAction");
1295 public void assertWriteAccessAllowed() {
1296 LOG.assertTrue(isWriteAccessAllowed(),
1297 "Write access is allowed inside write-action only (see com.intellij.openapi.application.Application.runWriteAction())");
1301 public boolean isWriteAccessAllowed() {
1302 return myLock.isWriteLockedByCurrentThread();
1305 // cheaper version of isWriteAccessAllowed(). must be called from EDT
1306 private boolean isInsideWriteActionEDTOnly() {
1307 return !myWriteActionsStack.isEmpty();
1311 public boolean isWriteActionInProgress() {
1312 return myLock.isWriteLocked();
1315 public void editorPaintStart() {
1316 myInEditorPaintCounter++;
1319 public void editorPaintFinish() {
1320 myInEditorPaintCounter--;
1321 LOG.assertTrue(myInEditorPaintCounter >= 0);
1325 public void addApplicationListener(@NotNull ApplicationListener l) {
1326 myDispatcher.addListener(l);
1330 public void addApplicationListener(@NotNull ApplicationListener l, @NotNull Disposable parent) {
1331 myDispatcher.addListener(l, parent);
1335 public void removeApplicationListener(@NotNull ApplicationListener l) {
1336 myDispatcher.removeListener(l);
1339 private void fireApplicationExiting() {
1340 myDispatcher.getMulticaster().applicationExiting();
1343 private void fireBeforeWriteActionStart(Class action) {
1344 myDispatcher.getMulticaster().beforeWriteActionStart(action);
1347 private void fireWriteActionStarted(Class action) {
1348 myDispatcher.getMulticaster().writeActionStarted(action);
1351 private void fireWriteActionFinished(Class action) {
1352 myDispatcher.getMulticaster().writeActionFinished(action);
1355 // public for testing purposes
1356 public void _saveSettings() {
1357 if (mySaveSettingsIsInProgress.compareAndSet(false, true)) {
1359 StoreUtil.save(getStateStore(), null);
1362 mySaveSettingsIsInProgress.set(false);
1368 public void saveSettings() {
1369 if (myDoNotSave) return;
1374 public void saveAll() {
1375 if (myDoNotSave) return;
1377 FileDocumentManager.getInstance().saveAllDocuments();
1379 Project[] openProjects = ProjectManager.getInstance().getOpenProjects();
1380 for (Project openProject : openProjects) {
1381 ProjectEx project = (ProjectEx)openProject;
1389 public void doNotSave() {
1394 public void doNotSave(boolean value) {
1395 myDoNotSave = value;
1399 public boolean isDoNotSave() {
1405 public <T> T[] getExtensions(@NotNull final ExtensionPointName<T> extensionPointName) {
1406 return Extensions.getRootArea().getExtensionPoint(extensionPointName).getExtensions();
1410 public boolean isDisposeInProgress() {
1411 return myDisposeInProgress || ShutDownTracker.isShutdownHookRunning();
1415 public boolean isRestartCapable() {
1416 return Restarter.isSupported();
1420 protected boolean logSlowComponents() {
1421 return super.logSlowComponents() || ApplicationInfoImpl.getShadowInstance().isEAP();
1425 public void setDisposeInProgress(boolean disposeInProgress) {
1426 myDisposeInProgress = disposeInProgress;
1431 public String toString() {
1432 return "Application" +
1433 (isDisposed() ? " (Disposed)" : "") +
1434 (isUnitTestMode() ? " (Unit test)" : "") +
1435 (isInternal() ? " (Internal)" : "") +
1436 (isHeadlessEnvironment() ? " (Headless)" : "") +
1437 (isCommandLine() ? " (Command line)" : "");