import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.impl.SimpleDataContext;
import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProcessCanceledException;
import org.jetbrains.annotations.TestOnly;
import javax.swing.*;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
+import java.util.*;
public class ExecutionManagerImpl extends ExecutionManager implements Disposable {
public static final Key<Object> EXECUTION_SESSION_ID_KEY = Key.create("EXECUTION_SESSION_ID_KEY");
private static final ProcessHandler[] EMPTY_PROCESS_HANDLERS = new ProcessHandler[0];
private final Project myProject;
- private final Alarm awaitingTerminationAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD);
+ private final Alarm myAwaitingTerminationAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD);
+ private final Map<RunProfile, ExecutionEnvironment> myAwaitingRunProfiles = ContainerUtil.newHashMap();
private final List<Trinity<RunContentDescriptor, RunnerAndConfigurationSettings, Executor>> myRunningConfigurations =
ContainerUtil.createLockFreeCopyOnWriteList();
private RunContentManagerImpl myContentManager;
}
}
- awaitingTerminationAlarm.addRequest(new Runnable() {
+ if (myAwaitingRunProfiles.get(environment.getRunProfile()) == environment) {
+ // defense from rerunning exactly the same ExecutionEnvironment
+ return;
+ }
+ myAwaitingRunProfiles.put(environment.getRunProfile(), environment);
+
+ awaitTermination(new Runnable() {
@Override
public void run() {
+ if (myAwaitingRunProfiles.get(environment.getRunProfile()) != environment) {
+ // a new rerun has been requested before starting this one, ignore this rerun
+ return;
+ }
if ((DumbService.getInstance(myProject).isDumb() && !Registry.is("dumb.aware.run.configurations")) || ExecutorRegistry.getInstance().isStarting(environment)) {
- awaitingTerminationAlarm.addRequest(this, 100);
+ awaitTermination(this, 100);
return;
}
for (RunContentDescriptor descriptor : runningOfTheSameType) {
ProcessHandler processHandler = descriptor.getProcessHandler();
if (processHandler != null && !processHandler.isProcessTerminated()) {
- awaitingTerminationAlarm.addRequest(this, 100);
+ awaitTermination(this, 100);
return;
}
}
+ myAwaitingRunProfiles.remove(environment.getRunProfile());
start(environment);
}
}, 50);
}
+ private void awaitTermination(@NotNull Runnable request, long delayMillis) {
+ if (ApplicationManager.getApplication().isUnitTestMode()) {
+ ApplicationManager.getApplication().invokeLater(request, ModalityState.any());
+ }
+ else {
+ myAwaitingTerminationAlarm.addRequest(request, delayMillis);
+ }
+ }
+
@TestOnly
public void setForceCompilationInTests(boolean forceCompilationInTests) {
myForceCompilationInTests = forceCompilationInTests;
--- /dev/null
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.execution.impl;
+
+import com.intellij.execution.RunManagerConfig;
+import com.intellij.execution.RunnerAndConfigurationSettings;
+import com.intellij.execution.executors.DefaultRunExecutor;
+import com.intellij.execution.process.ProcessHandler;
+import com.intellij.execution.runners.ExecutionEnvironment;
+import com.intellij.execution.runners.ExecutionEnvironmentBuilder;
+import com.intellij.execution.ui.RunContentDescriptor;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Conditions;
+import com.intellij.testFramework.LightPlatformTestCase;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.ui.UIUtil;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+public class ExecutionManagerTest extends LightPlatformTestCase {
+
+ private boolean myRestartRequiresConfirmation;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ RunManagerConfig config = RunManagerImpl.getInstanceImpl(getProject()).getConfig();
+ myRestartRequiresConfirmation = config.isRestartRequiresConfirmation();
+ config.setRestartRequiresConfirmation(false);
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ try {
+ RunManagerConfig config = RunManagerImpl.getInstanceImpl(getProject()).getConfig();
+ config.setRestartRequiresConfirmation(myRestartRequiresConfirmation);
+ }
+ finally {
+ super.tearDown();
+ }
+ }
+
+ public void testRerunSingleton() throws Exception {
+ Project project = getProject();
+ ExecutionManagerImpl executionManager = ExecutionManagerImpl.getInstance(project);
+
+ FakeRunConfiguration rc = new FakeRunConfiguration(project, true);
+ RunnerAndConfigurationSettingsImpl settings = new RunnerAndConfigurationSettingsImpl(
+ RunManagerImpl.getInstanceImpl(project), rc, false
+ );
+ settings.setSingleton(true);
+
+ ExecutionEnvironment env1 = createEnv(project, settings);
+ executionManager.restartRunProfile(env1);
+ UIUtil.dispatchAllInvocationEvents();
+ ProcessHandler processHandler1 = getProcessHandler(executionManager);
+
+ ExecutionEnvironment env2 = createEnv(project, settings);
+ executionManager.restartRunProfile(env2);
+ UIUtil.dispatchInvocationEvent();
+
+ ProcessHandler processHandler2 = getProcessHandler(executionManager);
+ assertTrue(processHandler1 == processHandler2);
+ assertTrue(processHandler1.isProcessTerminating());
+
+ ExecutionEnvironment env3 = createEnv(project, settings);
+ executionManager.restartRunProfile(env3);
+ UIUtil.dispatchAllInvocationEvents();
+
+ FakeProcessHandler processHandler3 = getProcessHandler(executionManager);
+ assertTrue(processHandler1 != processHandler3);
+
+ assertTrue(!processHandler3.isProcessTerminating() && !processHandler3.isProcessTerminated());
+ processHandler3.killProcess();
+ }
+
+ @NotNull
+ private static FakeProcessHandler getProcessHandler(@NotNull ExecutionManagerImpl executionManager) {
+ List<RunContentDescriptor> descriptors = executionManager.getRunningDescriptors(Conditions.alwaysTrue());
+ assertEquals(1, descriptors.size());
+ RunContentDescriptor descriptor = ContainerUtil.getFirstItem(descriptors);
+ assertNotNull(descriptor);
+ ProcessHandler processHandler = descriptor.getProcessHandler();
+ assertNotNull(processHandler);
+ return (FakeProcessHandler)processHandler;
+ }
+
+ @NotNull
+ private static ExecutionEnvironment createEnv(@NotNull Project project, @NotNull RunnerAndConfigurationSettings settings) {
+ return new ExecutionEnvironmentBuilder(project, DefaultRunExecutor.getRunExecutorInstance())
+ .runnerAndSettings(FakeProgramRunner.INSTANCE, settings)
+ .build();
+ }
+}
--- /dev/null
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.execution.impl;
+
+import com.intellij.execution.configurations.ConfigurationFactory;
+import com.intellij.execution.configurations.RunConfiguration;
+import com.intellij.openapi.project.Project;
+import org.jetbrains.annotations.NotNull;
+
+public class FakeConfigurationFactory extends ConfigurationFactory {
+
+ public static final FakeConfigurationFactory INSTANCE = new FakeConfigurationFactory();
+
+ public FakeConfigurationFactory() {
+ super(new FakeConfigurationType());
+ }
+
+ @NotNull
+ @Override
+ public RunConfiguration createTemplateConfiguration(@NotNull Project project) {
+ throw new UnsupportedOperationException("Not Implemented");
+ }
+}
+
+
+
--- /dev/null
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.execution.impl;
+
+import com.intellij.execution.configurations.ConfigurationFactory;
+import com.intellij.execution.configurations.ConfigurationType;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+
+public class FakeConfigurationType implements ConfigurationType {
+ @Override
+ public String getDisplayName() {
+ return "Fake";
+ }
+
+ @Override
+ public String getConfigurationTypeDescription() {
+ return "Fake";
+ }
+
+ @Override
+ public Icon getIcon() {
+ return null;
+ }
+
+ @Override
+ @NotNull
+ public String getId() {
+ return FakeConfigurationType.class.getSimpleName();
+ }
+
+ @Override
+ public ConfigurationFactory[] getConfigurationFactories() {
+ return new ConfigurationFactory[0];
+ }
+}
--- /dev/null
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.execution.impl;
+
+import com.intellij.execution.KillableProcess;
+import com.intellij.execution.process.ProcessHandler;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.OutputStream;
+
+public class FakeProcessHandler extends ProcessHandler implements KillableProcess {
+
+ private final boolean mySurviveSoftKill;
+
+ public FakeProcessHandler(boolean surviveSoftKill) {
+ mySurviveSoftKill = surviveSoftKill;
+ }
+
+ @Override
+ protected void destroyProcessImpl() {
+ if (!mySurviveSoftKill) {
+ notifyProcessTerminated(0);
+ }
+ }
+
+ @Override
+ protected void detachProcessImpl() {
+ notifyProcessTerminated(0);
+ }
+
+ @Override
+ public boolean detachIsDefault() {
+ return false;
+ }
+
+ @Nullable
+ @Override
+ public OutputStream getProcessInput() {
+ return null;
+ }
+
+ @Override
+ public boolean canKillProcess() {
+ return true;
+ }
+
+ @Override
+ public void killProcess() {
+ notifyProcessTerminated(0);
+ }
+}
--- /dev/null
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.execution.impl;
+
+import com.intellij.execution.ExecutionException;
+import com.intellij.execution.ExecutionResult;
+import com.intellij.execution.configurations.RunProfile;
+import com.intellij.execution.configurations.RunProfileState;
+import com.intellij.execution.runners.ExecutionEnvironment;
+import com.intellij.execution.runners.GenericProgramRunner;
+import com.intellij.execution.runners.RunContentBuilder;
+import com.intellij.execution.ui.RunContentDescriptor;
+import org.jetbrains.annotations.NotNull;
+
+public class FakeProgramRunner extends GenericProgramRunner {
+
+ public static final FakeProgramRunner INSTANCE = new FakeProgramRunner();
+
+ @NotNull
+ @Override
+ public String getRunnerId() {
+ return "MockProgramRunner";
+ }
+
+ @Override
+ protected RunContentDescriptor doExecute(@NotNull RunProfileState state, @NotNull ExecutionEnvironment env) throws ExecutionException {
+ ExecutionResult executionResult = state.execute(env.getExecutor(), this);
+ if (executionResult == null) {
+ return null;
+ }
+ return new RunContentBuilder(executionResult, env).showRunContent(env.getContentToReuse());
+ }
+
+ @Override
+ public boolean canRun(@NotNull String executorId, @NotNull RunProfile profile) {
+ return true;
+ }
+}
--- /dev/null
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.execution.impl;
+
+import com.intellij.execution.DefaultExecutionResult;
+import com.intellij.execution.ExecutionException;
+import com.intellij.execution.ExecutionResult;
+import com.intellij.execution.Executor;
+import com.intellij.execution.configurations.LocatableConfigurationBase;
+import com.intellij.execution.configurations.RunConfiguration;
+import com.intellij.execution.configurations.RunProfileState;
+import com.intellij.execution.runners.ExecutionEnvironment;
+import com.intellij.execution.runners.ProgramRunner;
+import com.intellij.openapi.options.SettingsEditor;
+import com.intellij.openapi.project.Project;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class FakeRunConfiguration extends LocatableConfigurationBase {
+
+ private static AtomicInteger CREATED_INSTANCES = new AtomicInteger(0);
+
+ private final boolean mySurviveSoftKill;
+
+ protected FakeRunConfiguration(@NotNull Project project, boolean surviveSoftKill) {
+ super(project, FakeConfigurationFactory.INSTANCE, nextName());
+ mySurviveSoftKill = surviveSoftKill;
+ }
+
+ private static String nextName() {
+ return "Fake #" + CREATED_INSTANCES.incrementAndGet();
+ }
+
+ @NotNull
+ @Override
+ public SettingsEditor<? extends RunConfiguration> getConfigurationEditor() {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ @Nullable
+ @Override
+ public RunProfileState getState(@NotNull Executor executor, @NotNull ExecutionEnvironment environment) throws ExecutionException {
+ return new FakeRunProfileState();
+ }
+
+ public class FakeRunProfileState implements RunProfileState {
+ @Nullable
+ @Override
+ public ExecutionResult execute(Executor executor, @NotNull ProgramRunner runner) throws ExecutionException {
+ return new DefaultExecutionResult(null, new FakeProcessHandler(mySurviveSoftKill));
+ }
+ }
+}