optimisation: do not start background thread if there is no contention
[idea/community.git] / platform / platform-impl / src / com / intellij / openapi / application / impl / ApplicationImpl.java
1 /*
2  * Copyright 2000-2011 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 package com.intellij.openapi.application.impl;
17
18 import com.intellij.CommonBundle;
19 import com.intellij.diagnostic.PerformanceWatcher;
20 import com.intellij.diagnostic.PluginException;
21 import com.intellij.ide.*;
22 import com.intellij.ide.plugins.IdeaPluginDescriptor;
23 import com.intellij.ide.plugins.PluginManager;
24 import com.intellij.idea.StartupUtil;
25 import com.intellij.openapi.Disposable;
26 import com.intellij.openapi.application.*;
27 import com.intellij.openapi.application.ex.ApplicationEx;
28 import com.intellij.openapi.command.CommandProcessor;
29 import com.intellij.openapi.components.RoamingType;
30 import com.intellij.openapi.components.StateStorageException;
31 import com.intellij.openapi.components.impl.ApplicationPathMacroManager;
32 import com.intellij.openapi.components.impl.ComponentManagerImpl;
33 import com.intellij.openapi.components.impl.stores.*;
34 import com.intellij.openapi.diagnostic.Logger;
35 import com.intellij.openapi.extensions.ExtensionPoint;
36 import com.intellij.openapi.extensions.ExtensionPointName;
37 import com.intellij.openapi.extensions.Extensions;
38 import com.intellij.openapi.extensions.PluginId;
39 import com.intellij.openapi.fileEditor.FileDocumentManager;
40 import com.intellij.openapi.progress.EmptyProgressIndicator;
41 import com.intellij.openapi.progress.ProcessCanceledException;
42 import com.intellij.openapi.progress.ProgressIndicator;
43 import com.intellij.openapi.progress.ProgressManager;
44 import com.intellij.openapi.progress.util.ProgressWindow;
45 import com.intellij.openapi.project.Project;
46 import com.intellij.openapi.project.ProjectManager;
47 import com.intellij.openapi.project.ex.ProjectEx;
48 import com.intellij.openapi.project.ex.ProjectManagerEx;
49 import com.intellij.openapi.project.impl.ProjectManagerImpl;
50 import com.intellij.openapi.ui.DialogWrapper;
51 import com.intellij.openapi.ui.Messages;
52 import com.intellij.openapi.util.*;
53 import com.intellij.openapi.util.registry.Registry;
54 import com.intellij.openapi.wm.IdeFrame;
55 import com.intellij.openapi.wm.WindowManager;
56 import com.intellij.openapi.wm.ex.ProgressIndicatorEx;
57 import com.intellij.openapi.wm.impl.IdeFrameImpl;
58 import com.intellij.psi.PsiLock;
59 import com.intellij.ui.Splash;
60 import com.intellij.util.Consumer;
61 import com.intellij.util.EventDispatcher;
62 import com.intellij.util.ReflectionCache;
63 import com.intellij.util.Restarter;
64 import com.intellij.util.concurrency.ReentrantWriterPreferenceReadWriteLock;
65 import com.intellij.util.containers.Stack;
66 import com.intellij.util.io.storage.HeavyProcessLatch;
67 import com.intellij.util.ui.UIUtil;
68 import org.jetbrains.annotations.NonNls;
69 import org.jetbrains.annotations.NotNull;
70 import org.jetbrains.annotations.Nullable;
71 import org.jetbrains.annotations.TestOnly;
72 import org.picocontainer.MutablePicoContainer;
73
74 import javax.swing.*;
75 import java.awt.*;
76 import java.io.IOException;
77 import java.io.InputStream;
78 import java.lang.reflect.Method;
79 import java.util.List;
80 import java.util.concurrent.*;
81 import java.util.concurrent.atomic.AtomicBoolean;
82 import java.util.concurrent.atomic.AtomicInteger;
83
84 @SuppressWarnings({"AssignmentToStaticFieldFromInstanceMethod"})
85 public class ApplicationImpl extends ComponentManagerImpl implements ApplicationEx {
86   private static final Logger LOG = Logger.getInstance("#com.intellij.application.impl.ApplicationImpl");
87   private final ModalityState MODALITY_STATE_NONE = ModalityState.NON_MODAL;
88   private final ModalityInvokator myInvokator = new ModalityInvokatorImpl();
89
90   private final EventDispatcher<ApplicationListener> myDispatcher = EventDispatcher.create(ApplicationListener.class);
91
92   private IApplicationStore myComponentStore;
93
94   private boolean myTestModeFlag;
95   private final boolean myHeadlessMode;
96   private final boolean myCommandLineMode;
97
98   private final boolean myIsInternal;
99   private final String myName;
100
101   private final ReentrantWriterPreferenceReadWriteLock myActionsLock = new ReentrantWriterPreferenceReadWriteLock();
102   private final Stack<Class> myWriteActionsStack = new Stack<Class>(); // accessed from EDT only, no need to sync
103
104   private volatile Runnable myExceptionalThreadWithReadAccessRunnable;
105
106   private int myInEditorPaintCounter = 0;
107   private long myStartTime = 0;
108   @Nullable
109   private Splash mySplash;
110   private boolean myDoNotSave;
111   private volatile boolean myDisposeInProgress = false;
112
113   private int myRestartCode = 0;
114   private volatile int myExitCode = 0;
115   private final Disposable myLastDisposable = Disposer.newDisposable(); // will be disposed last
116   
117   private boolean myHandlingInitComponentError;
118
119   private final AtomicBoolean mySaveSettingsIsInProgress = new AtomicBoolean(false);
120   @SuppressWarnings({"UseOfArchaicSystemPropertyAccessors"})
121   private static final int ourDumpThreadsOnLongWriteActionWaiting = Integer.getInteger("dump.threads.on.long.write.action.waiting", 0);
122   private final AtomicInteger myAliveThreads = new AtomicInteger(0);
123   private static final int ourReasonableThreadPoolSize = Registry.intValue("core.pooled.threads");
124
125   private final ExecutorService ourThreadExecutorsService = new ThreadPoolExecutor(
126     3,
127     Integer.MAX_VALUE,
128     5 * 60L,
129     TimeUnit.SECONDS,
130     new SynchronousQueue<Runnable>(),
131     new ThreadFactory() {
132       int i;
133       public Thread newThread(Runnable r) {
134         final int count = myAliveThreads.incrementAndGet();
135         final Thread thread = new Thread(r, "ApplicationImpl pooled thread "+i++) {
136           public void interrupt() {
137             if (LOG.isDebugEnabled()) {
138               LOG.debug("Interrupted worker, will remove from pool");
139             }
140             super.interrupt();
141           }
142
143           public void run() {
144             try {
145               super.run();
146             }
147             catch (Throwable t) {
148               if (LOG.isDebugEnabled()) {
149                 LOG.debug("Worker exits due to exception", t);
150               }
151             }
152             myAliveThreads.decrementAndGet();
153           }
154         };
155         if (ApplicationInfoImpl.getShadowInstance().isEAP() && count > ourReasonableThreadPoolSize) {
156           LOG.info("Not enough pooled threads; creating one at:", new Throwable());
157         }
158         thread.setPriority(Thread.NORM_PRIORITY - 1);
159         return thread;
160       }
161     }
162   );
163   private boolean myIsFiringLoadingEvent = false;
164   @NonNls private static final String WAS_EVER_SHOWN = "was.ever.shown";
165
166   private Boolean myActive;
167
168   private static final ThreadLocal<Integer> ourEdtSafe = new ThreadLocal<Integer>();
169   private static final ModalityState ANY = new ModalityState() {
170     @Override
171     public boolean dominates(@NotNull ModalityState anotherState) {
172       return false;
173     }
174
175     @Override
176     public String toString() {
177       return "ANY";
178     }
179   };
180
181   protected void bootstrapPicoContainer() {
182     super.bootstrapPicoContainer();
183     getPicoContainer().registerComponentImplementation(IComponentStore.class, StoresFactory.getApplicationStoreClass());
184     getPicoContainer().registerComponentImplementation(ApplicationPathMacroManager.class);
185   }
186
187   @NotNull
188   public synchronized IApplicationStore getStateStore() {
189     if (myComponentStore == null) {
190       myComponentStore = (IApplicationStore)getPicoContainer().getComponentInstance(IComponentStore.class);
191     }
192     return myComponentStore;
193   }
194
195   @Override
196   public void initializeComponent(Object component, boolean service) {
197     getStateStore().initComponent(component, service);
198   }
199
200   public ApplicationImpl(boolean isInternal,
201                          boolean isUnitTestMode,
202                          boolean isHeadless,
203                          boolean isCommandLine,
204                          @NotNull String appName,
205                          Splash splash) {
206     super(null);
207
208     ApplicationManager.setApplication(this, myLastDisposable); // reset back to null only when all components already disposed
209
210     getPicoContainer().registerComponentInstance(Application.class, this);
211
212     CommonBundle.assertKeyIsFound = isUnitTestMode;
213     AWTExceptionHandler.register(); // do not crash AWT on exceptions
214     if ((isInternal || isUnitTestMode) && !Comparing.equal("off", System.getProperty("idea.disposer.debug"))) {
215       Disposer.setDebugMode(true);
216     }
217     myStartTime = System.currentTimeMillis();
218     mySplash = splash;
219     myName = appName;
220
221     myIsInternal = isInternal;
222     myTestModeFlag = isUnitTestMode;
223     myHeadlessMode = isHeadless;
224     myCommandLineMode = isCommandLine;
225
226     myDoNotSave = myTestModeFlag || myHeadlessMode;
227
228     loadApplicationComponents();
229
230     if (myTestModeFlag) {
231       registerShutdownHook();
232     }
233
234     if (!isUnitTestMode && !isHeadless) {
235       Disposer.register(this, Disposer.newDisposable(), "ui");
236
237       StartupUtil.addExternalInstanceListener(new Consumer<List<String>>() {
238         @Override
239         public void consume(final List<String> args) {
240           invokeLater(new Runnable() {
241             @Override
242             public void run() {
243               final Project project = CommandLineProcessor.processExternalCommandLine(args);
244               final IdeFrame frame;
245               if (project != null) {
246                 frame = WindowManager.getInstance().getIdeFrame(project);
247               }
248               else {
249                 frame = WindowManager.getInstance().getAllFrames() [0];
250               }
251               ((IdeFrameImpl)frame).requestFocus();
252             }
253           });
254         }
255       });
256     }
257
258     myRestartCode = Restarter.getRestartCode();
259
260     registerFont("/fonts/Inconsolata.ttf");
261   }
262
263   private void registerFont(String name) {
264     if (isHeadlessEnvironment()) return;
265
266     InputStream is = null;
267     try {
268       is = getClass().getResourceAsStream(name);
269       final Font font = Font.createFont(Font.TRUETYPE_FONT, is);
270       GraphicsEnvironment.getLocalGraphicsEnvironment().registerFont(font);
271     }
272     catch (Exception e) {
273       LOG.info(e);
274     } finally {
275       if (is != null) {
276         try {
277           is.close();
278         }
279         catch (IOException e) {
280           LOG.error(e);
281         }
282       }
283     }
284   }
285
286   private void registerShutdownHook() {
287     ShutDownTracker.getInstance(); // Necessary to avoid creating an instance while already shutting down.
288
289     ShutDownTracker.getInstance().registerShutdownTask(new Runnable() {
290       public void run() {
291         if (isDisposed() || isDisposeInProgress()) {
292           return;
293         }
294         ShutDownTracker.invokeAndWait(isUnitTestMode(), true, new Runnable() {
295           public void run() {
296             if (ApplicationManager.getApplication() != ApplicationImpl.this) return;
297             try {
298               myDisposeInProgress = true;
299               saveAll();
300             }
301             finally {
302               disposeSelf(true);
303             }
304           }
305         });
306       }
307     });
308   }
309
310   private boolean disposeSelf(final boolean checkCanCloseProject) {
311     final CommandProcessor commandProcessor = CommandProcessor.getInstance();
312     final Ref<Boolean> canClose = new Ref<Boolean>(Boolean.TRUE);
313     for (final Project project : ProjectManagerEx.getInstanceEx().getOpenProjects()) {
314       try {
315         commandProcessor.executeCommand(project, new Runnable() {
316           public void run() {
317             canClose.set(((ProjectManagerImpl)ProjectManagerEx.getInstanceEx()).closeProject(project, true, true, checkCanCloseProject));
318           }
319         }, ApplicationBundle.message("command.exit"), null);
320       }
321       catch (Throwable e) {
322         LOG.error(e);
323       }
324       if (!canClose.get()) {
325         myDisposeInProgress = false;
326         return false;
327       }
328     }
329     Disposer.dispose(this);
330
331     Disposer.assertIsEmpty();
332     return true;
333   }
334
335   @NotNull
336   public String getName() {
337     return myName;
338   }
339
340   public boolean holdsReadLock() {
341     return myActionsLock.isReadLockAcquired();
342   }
343
344   @Override
345   protected void handleInitComponentError(final Throwable ex, final boolean fatal, final String componentClassName) {
346     if (myHandlingInitComponentError) {
347       return;
348     }
349     myHandlingInitComponentError = true;
350     try {
351       if (PluginManager.isPluginClass(componentClassName)) {
352         LOG.error(ex);
353         PluginId pluginId = PluginManager.getPluginByClassName(componentClassName);
354         @NonNls final String errorMessage =
355           "Plugin " + pluginId.getIdString() + " failed to initialize and will be disabled:\n" + ex.getMessage() +
356           "\nPlease restart " + ApplicationNamesInfo.getInstance().getFullProductName() + ".";
357         PluginManager.disablePlugin(pluginId.getIdString());
358         if (!myHeadlessMode) {
359           JOptionPane.showMessageDialog(null, errorMessage);
360         }
361         else {
362           //noinspection UseOfSystemOutOrSystemErr
363           System.out.println(errorMessage);
364           System.exit(1);
365         }
366         return;  // do not call super
367       }
368       if (fatal) {
369         LOG.error(ex);
370         @NonNls final String errorMessage = "Fatal error initializing class " + componentClassName + ":\n" +
371                                             ex.toString() +
372                                             "\nComplete error stacktrace was written to " + PathManager.getLogPath() + "/idea.log";
373         if (!myHeadlessMode) {
374           JOptionPane.showMessageDialog(null, errorMessage);
375         }
376         else {
377           //noinspection UseOfSystemOutOrSystemErr
378           System.out.println(errorMessage);
379         }
380       }
381       super.handleInitComponentError(ex, fatal, componentClassName);
382     }
383     finally {
384       myHandlingInitComponentError = false;
385     }
386   }
387
388   private void loadApplicationComponents() {
389     PluginManager.initPlugins(mySplash);
390     final IdeaPluginDescriptor[] plugins = PluginManager.getPlugins();
391     for (IdeaPluginDescriptor plugin : plugins) {
392       if (PluginManager.shouldSkipPlugin(plugin)) continue;
393       loadComponentsConfiguration(plugin.getAppComponents(), plugin, false);
394     }
395   }
396
397   @Override
398   protected synchronized Object createComponent(Class componentInterface) {
399     Object component = super.createComponent(componentInterface);
400     if (mySplash != null) {
401       mySplash.showProgress("", (float)(0.65f + myComponentsRegistry.getPercentageOfComponentsLoaded() * 0.35f));
402     }
403     return component;
404   }
405
406   protected MutablePicoContainer createPicoContainer() {
407     return Extensions.getRootArea().getPicoContainer();
408   }
409
410   public boolean isInternal() {
411     return myIsInternal;
412   }
413
414   public boolean isUnitTestMode() {
415     return myTestModeFlag;
416   }
417
418   public void setUnitTestMode(boolean testModeFlag) {
419     myTestModeFlag = testModeFlag;
420   }
421
422   public boolean isHeadlessEnvironment() {
423     return myHeadlessMode;
424   }
425
426   public boolean isCommandLine() {
427     return myCommandLineMode;
428   }
429
430   public Future<?> executeOnPooledThread(@NotNull final Runnable action) {
431     return ourThreadExecutorsService.submit(new Runnable() {
432       public void run() {
433         try {
434           action.run();
435         }
436         catch (ProcessCanceledException e) {
437           // ignore
438         }
439         catch (Throwable t) {
440           LOG.error(t);
441         }
442         finally {
443           //ReflectionUtil.resetThreadLocals();
444           Thread.interrupted(); // reset interrupted status
445         }
446       }
447     });
448   }
449
450   @Override
451   public <T> Future<T> executeOnPooledThread(@NotNull final Callable<T> action) {
452     return ourThreadExecutorsService.submit(new Callable<T>() {
453       public T call() {
454         try {
455           return action.call();
456         }
457         catch (ProcessCanceledException e) {
458           // ignore
459         }
460         catch (Throwable t) {
461           LOG.error(t);
462         }
463         finally {
464           //ReflectionUtil.resetThreadLocals();
465           Thread.interrupted(); // reset interrupted status
466         }
467         return null;
468       }
469     });
470   }
471
472   private static Thread ourDispatchThread = null;
473
474   public boolean isDispatchThread() {
475     return EventQueue.isDispatchThread();
476   }
477
478   @NotNull
479   public ModalityInvokator getInvokator() {
480     return myInvokator;
481   }
482
483
484   public void invokeLater(@NotNull final Runnable runnable) {
485     myInvokator.invokeLater(runnable);
486   }
487
488   public void invokeLater(@NotNull final Runnable runnable, @NotNull final Condition expired) {
489     myInvokator.invokeLater(runnable, expired);
490   }
491
492   public void invokeLater(@NotNull final Runnable runnable, @NotNull final ModalityState state) {
493     myInvokator.invokeLater(runnable, state);
494   }
495
496   public void invokeLater(@NotNull final Runnable runnable, @NotNull final ModalityState state, @NotNull final Condition expired) {
497     myInvokator.invokeLater(runnable, state, expired);
498   }
499
500   public void load(String path) throws IOException, InvalidDataException {
501     getStateStore().setOptionsPath(path);
502     getStateStore().setConfigPath(PathManager.getConfigPath());
503     myIsFiringLoadingEvent = true;
504     try {
505       fireBeforeApplicationLoaded();
506     }
507     finally {
508       myIsFiringLoadingEvent = false;
509     }
510
511     loadComponentRoamingTypes();
512
513     HeavyProcessLatch.INSTANCE.processStarted();
514     try {
515       getStateStore().load();
516     }
517     catch (StateStorageException e) {
518       throw new IOException(e.getMessage());
519     }
520     finally {
521       HeavyProcessLatch.INSTANCE.processFinished();
522     }
523   }
524
525   @Override
526   protected <T> T getComponentFromContainer(final Class<T> interfaceClass) {
527     if (myIsFiringLoadingEvent) {
528       return null;
529     }
530     return super.getComponentFromContainer(interfaceClass);
531   }
532
533   private static void loadComponentRoamingTypes() {
534     ExtensionPoint<RoamingTypeExtensionPointBean> point = Extensions.getRootArea().getExtensionPoint("com.intellij.ComponentRoamingType");
535     final RoamingTypeExtensionPointBean[] componentRoamingTypes = point.getExtensions();
536
537     for (RoamingTypeExtensionPointBean object : componentRoamingTypes) {
538
539       assert object.componentName != null;
540       assert object.roamingType != null;
541
542       final RoamingType type = RoamingType.valueOf(object.roamingType);
543
544       assert type != null;
545
546       ComponentRoamingManager.getInstance().setRoamingType(object.componentName, type);
547     }
548   }
549
550   private void fireBeforeApplicationLoaded() {
551     ExtensionPoint<ApplicationLoadListener> point = Extensions.getRootArea().getExtensionPoint("com.intellij.ApplicationLoadListener");
552     final ApplicationLoadListener[] objects = point.getExtensions();
553     for (ApplicationLoadListener object : objects) {
554       try {
555         object.beforeApplicationLoaded(this);
556       }
557       catch(Exception e) {
558         LOG.error(e);
559       }
560     }
561   }
562
563
564   public void dispose() {
565     fireApplicationExiting();
566
567     ShutDownTracker.getInstance().ensureStopperThreadsFinished();
568
569     disposeComponents();
570
571     ourThreadExecutorsService.shutdownNow();
572     myComponentStore = null;
573     super.dispose();
574     Disposer.dispose(myLastDisposable); // dispose it last
575   }
576
577   private final Object lock = new Object();
578   private void makeChangesVisibleToEDT() {
579     synchronized (lock) {
580       lock.hashCode();
581     }
582   }
583
584   public boolean runProcessWithProgressSynchronously(@NotNull final Runnable process,
585                                                      @NotNull String progressTitle,
586                                                      boolean canBeCanceled,
587                                                      Project project) {
588     return runProcessWithProgressSynchronously(process, progressTitle, canBeCanceled, project, null);
589   }
590
591   public boolean runProcessWithProgressSynchronously(@NotNull final Runnable process,
592                                                      @NotNull final String progressTitle,
593                                                      final boolean canBeCanceled,
594                                                      @Nullable final Project project,
595                                                      final JComponent parentComponent) {
596     return runProcessWithProgressSynchronously(process, progressTitle, canBeCanceled, project, parentComponent, null);
597   }
598
599   public boolean runProcessWithProgressSynchronously(@NotNull final Runnable process,
600                                                      @NotNull final String progressTitle,
601                                                      final boolean canBeCanceled,
602                                                      @Nullable final Project project,
603                                                      final JComponent parentComponent,
604                                                      final String cancelText) {
605     assertIsDispatchThread();
606
607     if (myExceptionalThreadWithReadAccessRunnable != null ||
608         ApplicationManager.getApplication().isUnitTestMode() ||
609         ApplicationManager.getApplication().isHeadlessEnvironment()) {
610       try {
611         ProgressManager.getInstance().runProcess(process, new EmptyProgressIndicator());
612       }
613       catch (ProcessCanceledException e) {
614         // ok to ignore.
615         return false;
616       }
617       return true;
618     }
619
620     final ProgressWindow progress = new ProgressWindow(canBeCanceled, false, project, parentComponent, cancelText);
621     progress.setTitle(progressTitle);
622
623     try {
624       myExceptionalThreadWithReadAccessRunnable = process;
625       final boolean[] threadStarted = {false};
626       SwingUtilities.invokeLater(new Runnable() {
627         public void run() {
628           if (myExceptionalThreadWithReadAccessRunnable != process) {
629               LOG.error("myExceptionalThreadWithReadAccessRunnable != process, process = " + myExceptionalThreadWithReadAccessRunnable);
630           }
631
632           executeOnPooledThread(new Runnable() {
633             public void run() {
634               if (myExceptionalThreadWithReadAccessRunnable != process) {
635                 LOG.error("myExceptionalThreadWithReadAccessRunnable != process, process = " + myExceptionalThreadWithReadAccessRunnable);
636               }
637
638               final boolean old = setExceptionalThreadWithReadAccessFlag(true);
639               LOG.assertTrue(isReadAccessAllowed());
640               try {
641                 ProgressManager.getInstance().runProcess(process, progress);
642               }
643               catch (ProcessCanceledException e) {
644                 progress.cancel();
645                 // ok to ignore.
646               }
647               catch (RuntimeException e) {
648                 progress.cancel();
649                 throw e;
650               }
651               finally {
652                 setExceptionalThreadWithReadAccessFlag(old);
653                 makeChangesVisibleToEDT();
654               }
655             }
656           });
657           threadStarted[0] = true;
658         }
659       });
660
661       progress.startBlocking();
662
663       LOG.assertTrue(threadStarted[0]);
664       LOG.assertTrue(!progress.isRunning());
665     }
666     finally {
667       myExceptionalThreadWithReadAccessRunnable = null;
668       makeChangesVisibleToEDT();
669     }
670
671     return !progress.isCanceled();
672   }
673
674   public boolean isInModalProgressThread() {
675     if (myExceptionalThreadWithReadAccessRunnable == null || !isExceptionalThreadWithReadAccess()) {
676       return false;
677     }
678     ProgressIndicator progressIndicator = ProgressManager.getInstance().getProgressIndicator();
679     return progressIndicator.isModal() && ((ProgressIndicatorEx)progressIndicator).isModalityEntered();
680   }
681
682   public void invokeAndWait(@NotNull Runnable runnable, @NotNull ModalityState modalityState) {
683     if (isDispatchThread()) {
684       LOG.error("invokeAndWait must not be called from event queue thread");
685       runnable.run();
686       return;
687     }
688
689     if (isExceptionalThreadWithReadAccess()) { //OK if we're in exceptional thread.
690       LaterInvocator.invokeAndWait(runnable, modalityState);
691       return;
692     }
693
694     if (myActionsLock.isReadLockAcquired()) {
695       LOG.error("Calling invokeAndWait from read-action leads to possible deadlock.");
696     }
697
698     LaterInvocator.invokeAndWait(runnable, modalityState);
699   }
700
701   @NotNull
702   public ModalityState getCurrentModalityState() {
703     Object[] entities = LaterInvocator.getCurrentModalEntities();
704     return entities.length > 0 ? new ModalityStateEx(entities) : getNoneModalityState();
705   }
706
707   @NotNull
708   public ModalityState getModalityStateForComponent(@NotNull Component c) {
709     Window window = c instanceof Window ? (Window)c : SwingUtilities.windowForComponent(c);
710     if (window == null) return getNoneModalityState(); //?
711     return LaterInvocator.modalityStateForWindow(window);
712   }
713
714   @Override
715   public ModalityState getAnyModalityState() {
716     return ANY;
717   }
718
719   @NotNull
720   public ModalityState getDefaultModalityState() {
721     if (EventQueue.isDispatchThread()) {
722       return getCurrentModalityState();
723     }
724     else {
725       ProgressIndicator progress = ProgressManager.getInstance().getProgressIndicator();
726       return progress == null ? getNoneModalityState() : progress.getModalityState();
727     }
728   }
729
730   @NotNull
731   public ModalityState getNoneModalityState() {
732     return MODALITY_STATE_NONE;
733   }
734
735   public long getStartTime() {
736     return myStartTime;
737   }
738
739   public long getIdleTime() {
740     return IdeEventQueue.getInstance().getIdleTime();
741   }
742
743   public void exit() {
744     exit(false);
745   }
746
747   public void exit(final boolean force) {
748     exit(force, true);
749   }
750
751   public void exit(final boolean force, final boolean allowListenersToCancel) {
752     if (!force && getDefaultModalityState() != ModalityState.NON_MODAL) {
753       return;
754     }
755
756     Runnable runnable = new Runnable() {
757       public void run() {
758         if (!force && !showConfirmation()) {
759           saveAll();
760           myExitCode = 0;
761           return;
762         }
763
764         getMessageBus().syncPublisher(AppLifecycleListener.TOPIC).appClosing();
765         myDisposeInProgress = true;
766
767         FileDocumentManager.getInstance().saveAllDocuments();
768
769         saveSettings();
770
771         if (allowListenersToCancel && !canExit()) {
772           myExitCode = 0;
773           return;
774         }
775
776         final boolean success = disposeSelf(allowListenersToCancel);
777         if (!success || isUnitTestMode()) {
778           myExitCode = 0;
779           return;
780         }
781
782         System.exit(myExitCode);
783       }
784     };
785
786     if (!isDispatchThread()) {
787       invokeLater(runnable, ModalityState.NON_MODAL);
788     }
789     else {
790       runnable.run();
791     }
792   }
793
794   private static boolean showConfirmation() {
795     final boolean hasUnsafeBgTasks = ProgressManager.getInstance().hasUnsafeProgressIndicator();
796
797     DialogWrapper.DoNotAskOption option = new DialogWrapper.DoNotAskOption() {
798       @Override
799       public boolean isToBeShown() {
800         return GeneralSettings.getInstance().isConfirmExit();
801       }
802
803       @Override
804       public void setToBeShown(boolean value, int exitCode) {
805         GeneralSettings.getInstance().setConfirmExit(value);
806       }
807
808       @Override
809       public boolean canBeHidden() {
810         return !hasUnsafeBgTasks;
811       }
812
813       @Override
814       public boolean shouldSaveOptionsOnCancel() {
815         return false;
816       }
817
818       @Override
819       public String getDoNotShowMessage() {
820         return "Do not ask me again";
821       }
822     };
823
824     if (hasUnsafeBgTasks || option.isToBeShown()) {
825       String message = ApplicationBundle
826         .message(hasUnsafeBgTasks ? "exit.confirm.prompt.tasks" : "exit.confirm.prompt",
827                  ApplicationNamesInfo.getInstance().getFullProductName());
828
829       if (DialogWrapper.OK_EXIT_CODE != Messages.showYesNoDialog(message, ApplicationBundle.message("exit.confirm.title"),
830                                         ApplicationBundle.message("command.exit"), "Cancel",
831                                         Messages.getQuestionIcon(), option)) {
832         return false;
833       }
834     }
835     return true;
836   }
837
838   private boolean canExit() {
839     for (ApplicationListener applicationListener : myDispatcher.getListeners()) {
840       if (!applicationListener.canExitApplication()) {
841         return false;
842       }
843     }
844
845     ProjectManagerEx projectManager = (ProjectManagerEx)ProjectManager.getInstance();
846     Project[] projects = projectManager.getOpenProjects();
847     for (Project project : projects) {
848       if (!projectManager.canClose(project)) {
849         return false;
850       }
851     }
852
853     return true;
854   }
855
856   public void runReadAction(@NotNull final Runnable action) {
857     final AccessToken token = acquireReadActionLockImpl(false);
858
859     try {
860       action.run();
861     }
862     finally {
863       token.finish();
864     }
865   }
866
867   private static final ThreadLocal<Boolean> exceptionalThreadWithReadAccessFlag = new ThreadLocal<Boolean>();
868   private static boolean isExceptionalThreadWithReadAccess() {
869     Boolean flag = exceptionalThreadWithReadAccessFlag.get();
870     return flag == Boolean.TRUE;
871   }
872
873   public static boolean setExceptionalThreadWithReadAccessFlag(boolean flag) {
874     boolean old = isExceptionalThreadWithReadAccess();
875     if (flag) {
876       exceptionalThreadWithReadAccessFlag.set(Boolean.TRUE);
877     }
878     else {
879       exceptionalThreadWithReadAccessFlag.remove();
880     }
881     return old;
882   }
883
884   public <T> T runReadAction(@NotNull final Computable<T> computation) {
885     final AccessToken token = acquireReadActionLockImpl(false);
886
887     try {
888       return computation.compute();
889     }
890     finally {
891       token.finish();
892     }
893   }
894
895   public void runWriteAction(@NotNull final Runnable action) {
896     final AccessToken token = acquireWriteActionLock(action.getClass());
897     try {
898       action.run();
899     }
900     finally {
901       token.finish();
902     }
903   }
904
905   public <T> T runWriteAction(@NotNull final Computable<T> computation) {
906     final AccessToken token = acquireWriteActionLock(computation.getClass());
907     try {
908       return computation.compute();
909     }
910     finally {
911       token.finish();
912     }
913   }
914
915   public boolean hasWriteAction(@Nullable Class<?> actionClass) {
916     assertCanRunWriteAction();
917
918     for (int i = myWriteActionsStack.size() - 1; i >= 0; i--) {
919       Class action = myWriteActionsStack.get(i);
920       if (actionClass == action || action != null && ReflectionCache.isAssignable(actionClass, action)) return true;
921     }
922     return false;
923   }
924
925   public void assertReadAccessAllowed() {
926     if (myHeadlessMode) return;
927     if (!isReadAccessAllowed()) {
928       LOG.error(
929         "Read access is allowed from event dispatch thread or inside read-action only (see com.intellij.openapi.application.Application.runReadAction())",
930         "Current thread: " + describe(Thread.currentThread()), "Our dispatch thread:" + describe(ourDispatchThread),
931         "SystemEventQueueThread: " + describe(getEventQueueThread()));
932     }
933   }
934
935   @NonNls
936   private static String describe(Thread o) {
937     if (o == null) return "null";
938     return o.toString() + " " + System.identityHashCode(o);
939   }
940
941   @Nullable
942   private static Thread getEventQueueThread() {
943     EventQueue eventQueue = Toolkit.getDefaultToolkit().getSystemEventQueue();
944     try {
945       Method method = EventQueue.class.getDeclaredMethod("getDispatchThread");
946       method.setAccessible(true);
947       return (Thread)method.invoke(eventQueue);
948     }
949     catch (Exception e1) {
950       // ok
951     }
952     return null;
953   }
954
955   public boolean isReadAccessAllowed() {
956     Thread currentThread = Thread.currentThread();
957     return ourDispatchThread == currentThread ||
958            isExceptionalThreadWithReadAccess() ||
959            myActionsLock.isReadLockAcquired() ||
960            myActionsLock.isWriteLockAcquired() ||
961            isDispatchThread();
962   }
963
964   private static void assertCanRunWriteAction() {
965     assertIsDispatchThread("Write access is allowed from event dispatch thread only");
966   }
967
968   public void assertIsDispatchThread() {
969     assertIsDispatchThread("Access is allowed from event dispatch thread only.");
970   }
971
972   private static void assertIsDispatchThread(String message) {
973     if (ShutDownTracker.isShutdownHookRunning()) return;
974     final Thread currentThread = Thread.currentThread();
975     if (ourDispatchThread == currentThread) return;
976
977     if (EventQueue.isDispatchThread()) {
978       ourDispatchThread = currentThread;
979     }
980     if (ourDispatchThread == currentThread) return;
981
982     Integer safeCounter = ourEdtSafe.get();
983     if (safeCounter != null && safeCounter > 0) return;
984
985     LOG.error(message,
986               "Current thread: " + describe(Thread.currentThread()),
987               "Our dispatch thread:" + describe(ourDispatchThread),
988               "SystemEventQueueThread: " + describe(getEventQueueThread()));
989   }
990
991   public void runEdtSafeAction(@NotNull Runnable runnable) {
992     Integer value = ourEdtSafe.get();
993     if (value == null) {
994       value = Integer.valueOf(0);
995     }
996
997     ourEdtSafe.set(value + 1);
998
999     try {
1000       runnable.run();
1001     }
1002     finally {
1003       int newValue = ourEdtSafe.get() - 1;
1004       ourEdtSafe.set(newValue >= 1 ? newValue : null);
1005     }
1006   }
1007
1008   public void assertIsDispatchThread(@Nullable final JComponent component) {
1009     if (component == null) return;
1010
1011     Thread curThread = Thread.currentThread();
1012     if (ourDispatchThread == curThread) {
1013       return;
1014     }
1015
1016     if (Boolean.TRUE.equals(component.getClientProperty(WAS_EVER_SHOWN))) {
1017       assertIsDispatchThread();
1018     }
1019     else {
1020       final JRootPane root = component.getRootPane();
1021       if (root != null) {
1022         component.putClientProperty(WAS_EVER_SHOWN, Boolean.TRUE);
1023         assertIsDispatchThread();
1024       } 
1025     }
1026   }
1027
1028   public void assertTimeConsuming() {
1029     if (myTestModeFlag || myHeadlessMode || ShutDownTracker.isShutdownHookRunning()) return;
1030     LOG.assertTrue(!isDispatchThread(), "This operation is time consuming and must not be called on EDT");
1031   }
1032
1033   public boolean tryRunReadAction(@NotNull Runnable action) {
1034     /** if we are inside read action, do not try to acquire read lock again since it will deadlock if there is a pending writeAction
1035      * see {@link com.intellij.util.concurrency.ReentrantWriterPreferenceReadWriteLock#allowReader()} */
1036     boolean mustAcquire = !isReadAccessAllowed();
1037
1038     if (mustAcquire) {
1039       LOG.assertTrue(myTestModeFlag || !Thread.holdsLock(PsiLock.LOCK), "Thread must not hold PsiLock while performing readAction");
1040       try {
1041         if (!myActionsLock.readLock().attempt(0)) return false;
1042       }
1043       catch (InterruptedException e) {
1044         throw new RuntimeInterruptedException(e);
1045       }
1046     }
1047
1048     try {
1049       action.run();
1050     }
1051     finally {
1052       if (mustAcquire) {
1053         myActionsLock.readLock().release();
1054       }
1055     }
1056     return true;
1057   }
1058
1059   public boolean tryToApplyActivationState(boolean active, Window window) {
1060     final Component frame = UIUtil.findUltimateParent(window);
1061
1062     if (frame instanceof IdeFrame) {
1063       final IdeFrame ideFrame = (IdeFrame)frame;
1064       if (isActive() != active) {
1065         myActive = Boolean.valueOf(active);
1066         System.setProperty("idea.active", myActive.toString());
1067         ApplicationActivationListener publisher = getMessageBus().syncPublisher(ApplicationActivationListener.TOPIC);
1068         if (active) {
1069           publisher.applicationActivated(ideFrame);
1070         }
1071         else {
1072           publisher.applicationDeactivated(ideFrame);
1073         }
1074         return true;
1075       }
1076     }
1077
1078     return false;
1079   }
1080
1081   public boolean isActive() {
1082     if (isUnitTestMode()) return true;
1083
1084     if (myActive == null) {
1085       Window active = KeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow();
1086       return active != null;
1087     }
1088
1089     return myActive;
1090   }
1091
1092   @Override
1093   public AccessToken acquireReadActionLock() {
1094     return acquireReadActionLockImpl(true);
1095   }
1096
1097   private AccessToken acquireReadActionLockImpl(boolean explicit) {
1098     /** if we are inside read action, do not try to acquire read lock again since it will deadlock if there is a pending writeAction
1099      * see {@link com.intellij.util.concurrency.ReentrantWriterPreferenceReadWriteLock#allowReader()} */
1100     if (isReadAccessAllowed()) return AccessToken.EMPTY_ACCESS_TOKEN;
1101
1102     return new ReadAccessToken(explicit);
1103   }
1104
1105   @Override
1106   public AccessToken acquireWriteActionLock(Class clazz) {
1107     return new WriteAccessToken(clazz);
1108   }
1109
1110   private class WriteAccessToken extends AccessToken {
1111     private final Class clazz;
1112
1113     public WriteAccessToken(Class _clazz) {
1114       clazz = _clazz;
1115       assertCanRunWriteAction();
1116
1117       ActivityTracker.getInstance().inc();
1118       fireBeforeWriteActionStart(_clazz);
1119       final AtomicBoolean stopped = new AtomicBoolean(false);
1120
1121       LOG.assertTrue(myActionsLock.isWriteLockAcquired(Thread.currentThread())
1122                      || !Thread.holdsLock(PsiLock.LOCK), "Thread must not hold PsiLock while performing writeAction");
1123       try {
1124         if (!myActionsLock.writeLock().attempt(0)) {
1125           if (ourDumpThreadsOnLongWriteActionWaiting > 0) {
1126             executeOnPooledThread(new Runnable() {
1127               @Override
1128               public void run() {
1129                 while (!stopped.get()) {
1130                   try {
1131                     Thread.sleep(ourDumpThreadsOnLongWriteActionWaiting);
1132                     if (!stopped.get()) {
1133                       PerformanceWatcher.getInstance().dumpThreads(true);
1134                     }
1135                   }
1136                   catch (InterruptedException ignored) {
1137                   }
1138                 }
1139               }
1140             });
1141           }
1142           myActionsLock.writeLock().acquire();
1143         }
1144         acquired();
1145       }
1146       catch (InterruptedException e) {
1147         throw new RuntimeInterruptedException(e);
1148       }
1149       stopped.set(true);
1150
1151       myWriteActionsStack.push(_clazz);
1152
1153       fireWriteActionStarted(_clazz);
1154     }
1155
1156     @Override
1157     public void finish() {
1158       try {
1159         fireWriteActionFinished(clazz);
1160
1161         myWriteActionsStack.pop();
1162       }
1163       finally {
1164         myActionsLock.writeLock().release();
1165         released();
1166       }
1167     }
1168   }
1169
1170   private class ReadAccessToken extends AccessToken {
1171     private final boolean myExplicit;
1172
1173     ReadAccessToken(boolean explicit) {
1174       myExplicit = explicit;
1175       LOG.assertTrue(!Thread.holdsLock(PsiLock.LOCK), "Thread must not hold PsiLock while performing readAction");
1176       try {
1177         myActionsLock.readLock().acquire();
1178         if (myExplicit) acquired();
1179       }
1180       catch (InterruptedException e) {
1181         throw new RuntimeInterruptedException(e);
1182       }
1183     }
1184
1185     @Override
1186     public void finish() {
1187       myActionsLock.readLock().release();
1188       if (myExplicit) released();
1189     }
1190   }
1191
1192   public void assertWriteAccessAllowed() {
1193     LOG.assertTrue(isWriteAccessAllowed(),
1194                    "Write access is allowed inside write-action only (see com.intellij.openapi.application.Application.runWriteAction())");
1195   }
1196
1197   public boolean isWriteAccessAllowed() {
1198     return myActionsLock.isWriteLockAcquired(Thread.currentThread());
1199   }
1200
1201   public void editorPaintStart() {
1202     myInEditorPaintCounter++;
1203   }
1204
1205   public void editorPaintFinish() {
1206     myInEditorPaintCounter--;
1207     LOG.assertTrue(myInEditorPaintCounter >= 0);
1208   }
1209
1210   public void addApplicationListener(@NotNull ApplicationListener l) {
1211     myDispatcher.addListener(l);
1212   }
1213
1214   public void addApplicationListener(@NotNull ApplicationListener l, @NotNull Disposable parent) {
1215     myDispatcher.addListener(l, parent);
1216   }
1217
1218   public void removeApplicationListener(@NotNull ApplicationListener l) {
1219     myDispatcher.removeListener(l);
1220   }
1221
1222   private void fireApplicationExiting() {
1223     myDispatcher.getMulticaster().applicationExiting();
1224   }
1225
1226   private void fireBeforeWriteActionStart(Class action) {
1227     myDispatcher.getMulticaster().beforeWriteActionStart(action);
1228   }
1229
1230   private void fireWriteActionStarted(Class action) {
1231     myDispatcher.getMulticaster().writeActionStarted(action);
1232   }
1233
1234   private void fireWriteActionFinished(Class action) {
1235     myDispatcher.getMulticaster().writeActionFinished(action);
1236   }
1237
1238   public void _saveSettings() { // public for testing purposes
1239     if (mySaveSettingsIsInProgress.compareAndSet(false, true)) {
1240       try {
1241         StoreUtil.doSave(getStateStore());
1242       }
1243       catch (final Throwable ex) {
1244         if (isUnitTestMode()) {
1245           System.out.println("Saving application settings failed");
1246           ex.printStackTrace();
1247         }
1248         else {
1249           LOG.info("Saving application settings failed", ex);
1250           invokeLater(new Runnable() {
1251             public void run() {
1252               if (ex instanceof PluginException) {
1253                 final PluginException pluginException = (PluginException)ex;
1254                 PluginManager.disablePlugin(pluginException.getPluginId().getIdString());
1255                 Messages.showMessageDialog("The plugin " +
1256                                            pluginException.getPluginId() +
1257                                            " failed to save settings and has been disabled. Please restart " +
1258                                            ApplicationNamesInfo.getInstance().getFullProductName(), CommonBundle.getErrorTitle(),
1259                                            Messages.getErrorIcon());
1260               }
1261               else {
1262                 Messages.showMessageDialog(ApplicationBundle.message("application.save.settings.error", ex.getLocalizedMessage()),
1263                                            CommonBundle.getErrorTitle(), Messages.getErrorIcon());
1264
1265               }
1266             }
1267           });
1268         }
1269       }
1270       finally {
1271         mySaveSettingsIsInProgress.set(false);
1272       }
1273     }
1274   }
1275
1276   public void saveSettings() {
1277     if (myDoNotSave) return;
1278     _saveSettings();
1279   }
1280
1281   public void saveAll() {
1282     if (myDoNotSave) return;
1283
1284     FileDocumentManager.getInstance().saveAllDocuments();
1285
1286     Project[] openProjects = ProjectManager.getInstance().getOpenProjects();
1287     for (Project openProject : openProjects) {
1288       ProjectEx project = (ProjectEx)openProject;
1289       project.save();
1290     }
1291
1292     saveSettings();
1293   }
1294
1295   public void doNotSave() {
1296     doNotSave(true);
1297   }
1298
1299   public void doNotSave(boolean value) {
1300     myDoNotSave = value;
1301   }
1302
1303   public boolean isDoNotSave() {
1304     return myDoNotSave;
1305   }
1306
1307   public <T> T[] getExtensions(final ExtensionPointName<T> extensionPointName) {
1308     return Extensions.getRootArea().getExtensionPoint(extensionPointName).getExtensions();
1309   }
1310
1311   public boolean isDisposeInProgress() {
1312     return myDisposeInProgress;
1313   }
1314
1315   public boolean isRestartCapable() {
1316     return Restarter.isSupported() || myRestartCode > 0;
1317   }
1318
1319   public void restart() {
1320     boolean restarted = false;
1321     try {
1322       restarted = Restarter.restart();
1323     }
1324     catch (Restarter.CannotRestartException e) {
1325       LOG.warn(e);
1326     }
1327
1328     if (!restarted) {
1329       myExitCode = myRestartCode;
1330     }
1331
1332     exit(true);
1333   }
1334
1335   public boolean isSaving() {
1336     if (getStateStore().isSaving()) return true;
1337     Project[] openProjects = ProjectManager.getInstance().getOpenProjects();
1338     for (Project openProject : openProjects) {
1339       ProjectEx project = (ProjectEx)openProject;
1340       if (project.getStateStore().isSaving()) return true;
1341     }
1342
1343     return false;
1344   }
1345
1346   @Override
1347   protected boolean logSlowComponents() {
1348     return super.logSlowComponents() || ApplicationInfoImpl.getShadowInstance().isEAP();
1349   }
1350
1351   @TestOnly
1352   public void setDisposeInProgress(boolean disposeInProgress) {
1353     myDisposeInProgress = disposeInProgress;
1354   }
1355
1356   @NonNls
1357   @Override
1358   public String toString() {
1359     return "Application" +
1360            (isDisposed() ? " (Disposed)" : "") +
1361            (isUnitTestMode() ? " (Unit test)" : "") +
1362            (isInternal() ? " (Internal)" : "") +
1363            (isHeadlessEnvironment() ? " (Headless)" : "") +
1364            (isCommandLine() ? " (Command line)" : "");
1365   }
1366 }