add transactions for startup & exit

This commit is contained in:
peter
2016-03-10 14:41:55 +01:00
parent 7ea9d1acaf
commit 26aed8eba5
5 changed files with 42 additions and 54 deletions
@@ -97,6 +97,19 @@ public abstract class TransactionGuard {
getInstance().submitMergeableTransaction(TransactionKind.NO_MERGE, transaction);
}
/**
* Runs the given code synchronously inside a transaction. Fails if transactions of given kind are not allowed at this moment.
* @see #startSynchronousTransaction(TransactionKind)
*/
public static void syncTransaction(@NotNull TransactionKind kind, @NotNull Runnable transaction) {
AccessToken token = getInstance().startSynchronousTransaction(kind);
try {
transaction.run();
} finally {
token.finish();
}
}
/**
* Schedules a transaction and waits for it to be completed. Fails if invoked on UI thread inside an incompatible transaction,
* or inside a read action on non-UI thread.
@@ -19,8 +19,7 @@ import com.intellij.ProjectTopics;
import com.intellij.notification.Notification;
import com.intellij.notification.NotificationType;
import com.intellij.notification.Notifications;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.*;
import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
import com.intellij.openapi.diagnostic.Logger;
@@ -126,26 +125,15 @@ public class ModuleManagerComponent extends ModuleManagerImpl {
return;
}
Runnable runnableWithProgress = new Runnable() {
@Override
public void run() {
for (final Module module : myModuleModel.myModules.values()) {
final Application app = ApplicationManager.getApplication();
final Runnable swingRunnable = new Runnable() {
@Override
public void run() {
fireModuleAddedInWriteAction(module);
}
};
ProgressIndicator pi = ProgressManager.getInstance().getProgressIndicator();
app.invokeAndWait(swingRunnable, pi.getModalityState());
}
Runnable runnableWithProgress = () -> {
for (final Module module : myModuleModel.myModules.values()) {
TransactionGuard.getInstance().submitTransactionAndWait(TransactionKind.ANY_CHANGE, () -> fireModuleAddedInWriteAction(module));
}
};
ProgressIndicator progressIndicator = myProgressManager.getProgressIndicator();
if (progressIndicator == null) {
myProgressManager.runProcessWithProgressSynchronously(runnableWithProgress, "Initializing modules...", false, myProject);
myProgressManager.runProcessWithProgressSynchronously(runnableWithProgress, "Initializing Modules...", false, myProject);
}
else {
runnableWithProgress.run();
@@ -23,6 +23,7 @@ import com.intellij.ide.IdeBundle;
import com.intellij.ide.caches.FileContent;
import com.intellij.ide.startup.StartupManagerEx;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.TransactionGuard;
import com.intellij.openapi.components.AbstractProjectComponent;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.fileTypes.FileTypeManager;
@@ -80,15 +81,10 @@ public class FileBasedIndexProjectHandler extends AbstractProjectComponent imple
public void run() {
PushedFilePropertiesUpdater.getInstance(project).initializeProperties();
// dumb mode should start before post-startup activities
// only when queueTask is called from UI thread, we can guarantee that
// when the method returns, the application has entered dumb mode
UIUtil.invokeLaterIfNeeded(new Runnable() {
@Override
public void run() {
if (!project.isDisposed() && FileBasedIndex.getInstance() instanceof FileBasedIndexImpl) {
DumbService.getInstance(project).queueTask(new UnindexedFilesUpdater(project, true));
}
// schedule dumb mode start after the read action we're currently in
TransactionGuard.submitTransaction(() -> {
if (!project.isDisposed() && FileBasedIndex.getInstance() instanceof FileBasedIndexImpl) {
DumbService.getInstance(project).queueTask(new UnindexedFilesUpdater(project, true));
}
});
@@ -308,12 +308,7 @@ public class ApplicationImpl extends PlatformComponentManagerImpl implements App
}
}
}
runWriteAction(new Runnable() {
@Override
public void run() {
Disposer.dispose(ApplicationImpl.this);
}
});
TransactionGuard.syncTransaction(TransactionKind.ANY_CHANGE, () -> runWriteAction(() -> Disposer.dispose(this)));
Disposer.assertIsEmpty();
return true;
@@ -376,6 +371,11 @@ public class ApplicationImpl extends PlatformComponentManagerImpl implements App
@Override
public Future<?> executeOnPooledThread(@NotNull final Runnable action) {
return ourThreadExecutorsService.submit(new Runnable() {
@Override
public String toString() {
return action.toString();
}
@Override
public void run() {
assert !isReadAccessAllowed(): describe(Thread.currentThread());
@@ -864,7 +864,7 @@ public class ApplicationImpl extends PlatformComponentManagerImpl implements App
}
private boolean doExit(boolean allowListenersToCancel, boolean restart) {
saveSettings();
TransactionGuard.syncTransaction(TransactionKind.ANY_CHANGE, this::saveSettings);
if (allowListenersToCancel && !canExit()) {
return false;
@@ -1230,8 +1230,8 @@ public class ApplicationImpl extends PlatformComponentManagerImpl implements App
assertIsDispatchThread(getStatus(), "Write access is allowed from event dispatch thread only");
HeavyProcessLatch.INSTANCE.stopThreadPrioritizing(); // let non-cancellable read actions complete faster, if present
if (!TransactionGuard.getInstance().isInsideTransaction() && Registry.is("ide.require.transaction.for.model.changes", false)) {
// please assign exceptions that occur here to Peter
LOG.error("Write access is allowed from model transactions only, see TransactionGuard documentation for details");
//todo throw new IllegalStateException("Write access is allowed from model transactions only, see TransactionGuard documentation for details");
}
boolean writeActionPending = myWriteActionPending;
myWriteActionPending = true;
@@ -28,10 +28,7 @@ import com.intellij.notification.NotificationListener;
import com.intellij.notification.NotificationType;
import com.intellij.notification.NotificationsManager;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ApplicationNamesInfo;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.application.*;
import com.intellij.openapi.components.impl.stores.StorageUtil;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.fileEditor.FileDocumentManager;
@@ -351,9 +348,8 @@ public class ProjectManagerImpl extends ProjectManagerEx implements Disposable {
}
fireProjectOpened(project);
DumbService.allowStartingDumbModeInside(DumbModePermission.MAY_START_BACKGROUND, new Runnable() {
@Override
public void run() {
try (AccessToken ignored = TransactionGuard.getInstance().startSynchronousTransaction(TransactionKind.ANY_CHANGE)) {
DumbService.allowStartingDumbModeInside(DumbModePermission.MAY_START_BACKGROUND, () ->
DumbService.getInstance(project).queueTask(new DumbModeTask() {
@Override
public void performInDumbMode(@NotNull ProgressIndicator indicator) {
@@ -364,9 +360,9 @@ public class ProjectManagerImpl extends ProjectManagerEx implements Disposable {
public String toString() {
return "wait for file watcher";
}
});
}
});
})
);
}
final StartupManagerImpl startupManager = (StartupManagerImpl)StartupManager.getInstance(project);
boolean ok = myProgressManager.runProcessWithProgressSynchronously(new Runnable() {
@@ -374,15 +370,10 @@ public class ProjectManagerImpl extends ProjectManagerEx implements Disposable {
public void run() {
startupManager.runStartupActivities();
// dumb mode should start before post-startup activities
// only when startCacheUpdate is called from UI thread, we can guarantee that
// when the method returns, the application has entered dumb mode
UIUtil.invokeAndWaitIfNeeded(new Runnable() {
@Override
public void run() {
startupManager.startCacheUpdate();
}
});
// Startup activities (e.g. the one in FileBasedIndexProjectHandler) have scheduled dumb mode to begin "later"
// Now we schedule-and-wait to the same event queue to guarantee that the dumb mode really begins now:
// Post-startup activities should not ever see unindexed and at the same time non-dumb state
TransactionGuard.getInstance().submitTransactionAndWait(TransactionKind.ANY_CHANGE, startupManager::startCacheUpdate);
startupManager.runPostStartupActivitiesFromExtensions();
@@ -615,7 +606,7 @@ public class ProjectManagerImpl extends ProjectManagerEx implements Disposable {
if (checkCanClose && !canClose(project)) return false;
final ShutDownTracker shutDownTracker = ShutDownTracker.getInstance();
shutDownTracker.registerStopperThread(Thread.currentThread());
try {
try (AccessToken ignored = TransactionGuard.getInstance().startSynchronousTransaction(TransactionKind.NO_MERGE)) {
if (save) {
FileDocumentManager.getInstance().saveAllDocuments();
project.save();