CVS: do not hung when closing a project while invoke-and-wait in another thread and...
[fedora-idea.git] / vcs-impl / src / com / intellij / lifecycle / PeriodicalTasksCloser.java
blob964efbc65865379905b26847f151ebe9567a0d20
1 package com.intellij.lifecycle;
3 import com.intellij.openapi.application.ApplicationManager;
4 import com.intellij.openapi.application.ModalityState;
5 import com.intellij.openapi.application.Application;
6 import com.intellij.openapi.application.impl.LaterInvocator;
7 import com.intellij.openapi.components.ServiceManager;
8 import com.intellij.openapi.diagnostic.Logger;
9 import com.intellij.openapi.progress.ProcessCanceledException;
10 import com.intellij.openapi.progress.ProgressIndicator;
11 import com.intellij.openapi.progress.ProgressManager;
12 import com.intellij.openapi.project.Project;
13 import com.intellij.openapi.project.ProjectManager;
14 import com.intellij.openapi.project.ProjectManagerListener;
15 import com.intellij.openapi.util.Condition;
16 import com.intellij.openapi.util.Pair;
17 import com.intellij.openapi.util.Ref;
18 import com.intellij.util.concurrency.Semaphore;
19 import org.jetbrains.annotations.NonNls;
20 import org.jetbrains.annotations.NotNull;
22 import java.util.ArrayList;
23 import java.util.HashMap;
24 import java.util.List;
25 import java.util.Map;
27 public class PeriodicalTasksCloser implements ProjectManagerListener {
28 private final static Logger LOG = Logger.getInstance("#com.intellij.lifecycle.PeriodicalTasksCloser");
29 private final static Object ourLock = new Object();
30 private final List<Pair<String, Runnable>> myInterrupters;
31 private final static Map<Project, Boolean> myStates = new HashMap<Project, Boolean>();
33 private PeriodicalTasksCloser(final Project project, final ProjectManager projectManager) {
34 myInterrupters = new ArrayList<Pair<String, Runnable>>();
35 projectManager.addProjectManagerListener(project, this);
38 public static PeriodicalTasksCloser getInstance(final Project project) {
39 return ServiceManager.getService(project, PeriodicalTasksCloser.class);
42 public void register(final String name, final Runnable runnable) {
43 myInterrupters.add(new Pair<String, Runnable>(name, runnable));
46 public void projectOpened(Project project) {
47 synchronized (ourLock) {
48 myStates.put(project, Boolean.TRUE);
52 public boolean canCloseProject(Project project) {
53 return true;
56 public void projectClosed(Project project) {
57 synchronized (ourLock) {
58 myStates.remove(project);
62 public void projectClosing(Project project) {
63 synchronized (ourLock) {
64 myStates.put(project, Boolean.FALSE);
66 ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() {
67 public void run() {
68 final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
69 for (Pair<String, Runnable> pair : myInterrupters) {
70 if (indicator != null) {
71 indicator.setText(pair.getFirst());
72 indicator.checkCanceled();
74 pair.getSecond().run();
77 }, "Please wait for safe shutdown of periodical tasks...", true, project);
80 public static<T> T safeGetComponent(@NotNull final Project project, final Class<T> componentClass) throws ProcessCanceledException {
81 final T component;
82 try {
83 component = project.getComponent(componentClass);
85 catch (Throwable t) {
86 if (t instanceof NullPointerException) {
87 } else if (t instanceof AssertionError) {
88 } else {
89 LOG.info(t);
91 throw new ProcessCanceledException();
93 synchronized (ourLock) {
94 final Boolean state = myStates.get(project);
95 // if project is already closed and project key is already removed from map - then it should have thrown an exception in the block above
96 // so ok to just check for 'closing' stage here
97 if (state != null && ! Boolean.TRUE.equals(state)) {
98 throw new ProcessCanceledException();
100 return component;
104 public static void invokeAndWaitInterruptedWhenClosing(final Project project, final Runnable runnable, final ModalityState modalityState) {
105 final Ref<Boolean> start = new Ref<Boolean>(Boolean.TRUE);
106 final Application application = ApplicationManager.getApplication();
107 LOG.assertTrue(! application.isDispatchThread());
109 final Semaphore semaphore = new Semaphore();
110 semaphore.down();
111 Runnable runnable1 = new Runnable() {
112 public void run() {
113 try {
114 runnable.run();
116 finally {
117 semaphore.up();
121 @NonNls
122 public String toString() {
123 return "PeriodicalTaskCloser's invoke and wait [" + runnable.toString() + "]";
126 LaterInvocator.invokeLater(runnable1, modalityState, new Condition<Object>() {
127 public boolean value(Object o) {
128 synchronized (start) {
129 return ! start.get();
134 while (true) {
135 if (semaphore.waitFor(1000)) {
136 return;
138 final Ref<Boolean> fire = new Ref<Boolean>();
139 synchronized (ourLock) {
140 final Boolean state = myStates.get(project);
141 if (! Boolean.TRUE.equals(state)) {
142 fire.set(Boolean.TRUE);
144 if (Boolean.TRUE.equals(fire.get())) {
145 synchronized (start) {
146 start.set(Boolean.FALSE);
147 return;