diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/actions/DsfTerminateCommand.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/actions/DsfTerminateCommand.java index 4850e0781d1..904611a4b3b 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/actions/DsfTerminateCommand.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/actions/DsfTerminateCommand.java @@ -12,6 +12,10 @@ package org.eclipse.cdt.dsf.gdb.internal.ui.actions; import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; import org.eclipse.cdt.dsf.concurrent.DsfExecutor; import org.eclipse.cdt.dsf.concurrent.DsfRunnable; @@ -21,18 +25,84 @@ import org.eclipse.cdt.dsf.datamodel.DMContexts; import org.eclipse.cdt.dsf.debug.service.IProcesses; import org.eclipse.cdt.dsf.debug.service.IProcesses.IProcessDMContext; import org.eclipse.cdt.dsf.gdb.internal.ui.GdbUIPlugin; +import org.eclipse.cdt.dsf.gdb.launching.GdbLaunch; +import org.eclipse.cdt.dsf.gdb.service.command.IGDBControl; import org.eclipse.cdt.dsf.service.DsfServicesTracker; import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.dsf.service.DsfSession.SessionEndedListener; import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.IDMVMContext; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; import org.eclipse.debug.core.commands.IDebugCommandRequest; import org.eclipse.debug.core.commands.IEnabledStateRequest; import org.eclipse.debug.core.commands.ITerminateHandler; public class DsfTerminateCommand implements ITerminateHandler { - private final DsfExecutor fExecutor; + + private class WaitForTerminationJob extends Job implements SessionEndedListener { + + final private IDebugCommandRequest fRequest; + final private String fSessionId; + final private Lock fLock = new ReentrantLock(); + final private Condition fTerminated = fLock.newCondition(); + + public WaitForTerminationJob(String sessionId, IDebugCommandRequest request) { + super("Wait for termination job"); //$NON-NLS-1$ + setUser(false); + setSystem(true); + fSessionId = sessionId; + fRequest = request; + DsfSession.addSessionEndedListener(WaitForTerminationJob.this); + } + + @Override + protected IStatus run(IProgressMonitor monitor) { + // Wait for all processes associated with the launch + // and the shutdown sequence to be completed. + // The wait time is restricted to stop the job in case + // of termination error. + boolean result = !DsfSession.isSessionActive(fSessionId); + if (!result) { + fLock.lock(); + try { + result = fTerminated.await(1, TimeUnit.MINUTES); + } + catch(InterruptedException e) { + } + finally { + fLock.unlock(); + } + } + // Marking the request as cancelled will prevent the removal of + // the launch from the Debug view in case of "Terminate and Remove". + fRequest.setStatus(result ? Status.OK_STATUS : Status.CANCEL_STATUS); + fRequest.done(); + DsfSession.removeSessionEndedListener(WaitForTerminationJob.this); + return Status.OK_STATUS; + } + + @Override + public void sessionEnded(DsfSession session) { + if (fSessionId.equals(session.getId())) { + fLock.lock(); + try { + fTerminated.signal(); + } + finally { + fLock.unlock(); + } + } + } + } + + private final DsfSession fSession; + private final DsfExecutor fExecutor; private final DsfServicesTracker fTracker; public DsfTerminateCommand(DsfSession session) { + fSession = session; fExecutor = session.getExecutor(); fTracker = new DsfServicesTracker(GdbUIPlugin.getBundleContext(), session.getId()); } @@ -41,17 +111,22 @@ public class DsfTerminateCommand implements ITerminateHandler { fTracker.dispose(); } - // Run control may not be avilable after a connection is terminated and shut down. + // Run control may not be available after a connection is terminated and shut down. @Override public void canExecute(final IEnabledStateRequest request) { if (request.getElements().length != 1 || - !(request.getElements()[0] instanceof IDMVMContext) ) - { + !(request.getElements()[0] instanceof IDMVMContext || + request.getElements()[0] instanceof GdbLaunch)) { request.setEnabled(false); request.done(); return; } + if (request.getElements()[0] instanceof GdbLaunch) { + canExecute(((GdbLaunch)request.getElements()[0]), request); + return; + } + IDMVMContext vmc = (IDMVMContext)request.getElements()[0]; // First check if there is an ancestor process to terminate. This is the smallest entity we can terminate @@ -61,7 +136,72 @@ public class DsfTerminateCommand implements ITerminateHandler { request.done(); return; } - + + canExecute(processDmc, request); + } + + @Override + public boolean execute(final IDebugCommandRequest request) { + if (request.getElements().length != 1 || + !(request.getElements()[0] instanceof IDMVMContext || + request.getElements()[0] instanceof GdbLaunch)) { + request.done(); + return false; + } + + if (request.getElements()[0] instanceof GdbLaunch) { + return execute(((GdbLaunch)request.getElements()[0]), request); + } + + IDMVMContext vmc = (IDMVMContext)request.getElements()[0]; + + // First check if there is an ancestor process to terminate. This is the smallest entity we can terminate + final IProcessDMContext processDmc = DMContexts.getAncestorOfType(vmc.getDMContext(), IProcessDMContext.class); + if (processDmc == null) { + request.done(); + return false; + } + + return execute(processDmc, request); + } + + private void canExecute(GdbLaunch launch, IEnabledStateRequest request) { + request.setEnabled(launch.canTerminate()); + request.done(); + } + + private boolean execute(GdbLaunch launch, final IDebugCommandRequest request) { + try { + fExecutor.execute(new DsfRunnable() { + @Override + public void run() { + final IGDBControl commandControl = fTracker.getService(IGDBControl.class); + if (commandControl != null) { + commandControl.terminate(new ImmediateRequestMonitor() { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + request.setStatus(getStatus()); + request.done(); + } + else { + WaitForTerminationJob job = new WaitForTerminationJob(fSession.getId(), request); + job.schedule(); + } + }; + }); + } else { + request.done(); + } + } + }); + } catch (RejectedExecutionException e) { + request.done(); + } + return false; + } + + private void canExecute(final IProcessDMContext processDmc, final IEnabledStateRequest request) { try { fExecutor.execute( new DsfRunnable() { @@ -90,23 +230,7 @@ public class DsfTerminateCommand implements ITerminateHandler { } } - @Override - public boolean execute(final IDebugCommandRequest request) { - if (request.getElements().length != 1 || - !(request.getElements()[0] instanceof IDMVMContext)) { - request.done(); - return false; - } - - IDMVMContext vmc = (IDMVMContext)request.getElements()[0]; - - // First check if there is an ancestor process to terminate. This is the smallest entity we can terminate - final IProcessDMContext processDmc = DMContexts.getAncestorOfType(vmc.getDMContext(), IProcessDMContext.class); - if (processDmc == null) { - request.done(); - return false; - } - + private boolean execute(final IProcessDMContext processDmc, final IDebugCommandRequest request) { try { fExecutor.execute(new DsfRunnable() { @Override @@ -116,8 +240,14 @@ public class DsfTerminateCommand implements ITerminateHandler { procService.terminate(processDmc, new ImmediateRequestMonitor() { @Override protected void handleCompleted() { - request.setStatus(getStatus()); - request.done(); + if (!isSuccess()) { + request.setStatus(getStatus()); + request.done(); + } + else { + WaitForTerminationJob job = new WaitForTerminationJob(fSession.getId(), request); + job.schedule(); + } }; }); } else { @@ -130,5 +260,4 @@ public class DsfTerminateCommand implements ITerminateHandler { } return false; } - } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GdbLaunch.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GdbLaunch.java index 70d33d94110..c65e8f9404e 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GdbLaunch.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GdbLaunch.java @@ -56,6 +56,7 @@ import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.commands.ITerminateHandler; import org.eclipse.debug.core.model.IDisconnect; import org.eclipse.debug.core.model.IMemoryBlockRetrieval; import org.eclipse.debug.core.model.ISourceLocator; @@ -300,6 +301,10 @@ public class GdbLaunch extends DsfLaunch @SuppressWarnings("rawtypes") @Override public Object getAdapter(Class adapter) { + // We replace the standard terminate handler by DsfTerminateHandler + // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=377447. + if (adapter.equals(ITerminateHandler.class)) + return getSession().getModelAdapter(adapter); // Must force adapters to be loaded. Platform.getAdapterManager().loadAdapter(this, adapter.getName()); return super.getAdapter(adapter);