mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-04-29 19:45:01 +02:00
Bug 377447 - 'DsfTerminateCommand' doesn't wait for the session shutdown
to complete Change-Id: I0e3b8e59594971644bb7a3ce5b07ad9ab535aef4 Reviewed-on: https://git.eclipse.org/r/5751 Reviewed-by: Marc Khouzam <marc.khouzam@ericsson.com> IP-Clean: Marc Khouzam <marc.khouzam@ericsson.com> Tested-by: Marc Khouzam <marc.khouzam@ericsson.com>
This commit is contained in:
parent
99b0e4643d
commit
aa5ca7e2cb
2 changed files with 159 additions and 25 deletions
|
@ -12,6 +12,10 @@
|
||||||
package org.eclipse.cdt.dsf.gdb.internal.ui.actions;
|
package org.eclipse.cdt.dsf.gdb.internal.ui.actions;
|
||||||
|
|
||||||
import java.util.concurrent.RejectedExecutionException;
|
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.DsfExecutor;
|
||||||
import org.eclipse.cdt.dsf.concurrent.DsfRunnable;
|
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;
|
||||||
import org.eclipse.cdt.dsf.debug.service.IProcesses.IProcessDMContext;
|
import org.eclipse.cdt.dsf.debug.service.IProcesses.IProcessDMContext;
|
||||||
import org.eclipse.cdt.dsf.gdb.internal.ui.GdbUIPlugin;
|
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.DsfServicesTracker;
|
||||||
import org.eclipse.cdt.dsf.service.DsfSession;
|
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.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.IDebugCommandRequest;
|
||||||
import org.eclipse.debug.core.commands.IEnabledStateRequest;
|
import org.eclipse.debug.core.commands.IEnabledStateRequest;
|
||||||
import org.eclipse.debug.core.commands.ITerminateHandler;
|
import org.eclipse.debug.core.commands.ITerminateHandler;
|
||||||
|
|
||||||
public class DsfTerminateCommand implements ITerminateHandler {
|
public class DsfTerminateCommand implements ITerminateHandler {
|
||||||
|
|
||||||
|
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 DsfExecutor fExecutor;
|
||||||
private final DsfServicesTracker fTracker;
|
private final DsfServicesTracker fTracker;
|
||||||
|
|
||||||
public DsfTerminateCommand(DsfSession session) {
|
public DsfTerminateCommand(DsfSession session) {
|
||||||
|
fSession = session;
|
||||||
fExecutor = session.getExecutor();
|
fExecutor = session.getExecutor();
|
||||||
fTracker = new DsfServicesTracker(GdbUIPlugin.getBundleContext(), session.getId());
|
fTracker = new DsfServicesTracker(GdbUIPlugin.getBundleContext(), session.getId());
|
||||||
}
|
}
|
||||||
|
@ -41,17 +111,22 @@ public class DsfTerminateCommand implements ITerminateHandler {
|
||||||
fTracker.dispose();
|
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
|
@Override
|
||||||
public void canExecute(final IEnabledStateRequest request) {
|
public void canExecute(final IEnabledStateRequest request) {
|
||||||
if (request.getElements().length != 1 ||
|
if (request.getElements().length != 1 ||
|
||||||
!(request.getElements()[0] instanceof IDMVMContext) )
|
!(request.getElements()[0] instanceof IDMVMContext ||
|
||||||
{
|
request.getElements()[0] instanceof GdbLaunch)) {
|
||||||
request.setEnabled(false);
|
request.setEnabled(false);
|
||||||
request.done();
|
request.done();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (request.getElements()[0] instanceof GdbLaunch) {
|
||||||
|
canExecute(((GdbLaunch)request.getElements()[0]), request);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
IDMVMContext vmc = (IDMVMContext)request.getElements()[0];
|
IDMVMContext vmc = (IDMVMContext)request.getElements()[0];
|
||||||
|
|
||||||
// First check if there is an ancestor process to terminate. This is the smallest entity we can terminate
|
// First check if there is an ancestor process to terminate. This is the smallest entity we can terminate
|
||||||
|
@ -62,6 +137,71 @@ public class DsfTerminateCommand implements ITerminateHandler {
|
||||||
return;
|
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 {
|
try {
|
||||||
fExecutor.execute(
|
fExecutor.execute(
|
||||||
new DsfRunnable() {
|
new DsfRunnable() {
|
||||||
|
@ -90,23 +230,7 @@ public class DsfTerminateCommand implements ITerminateHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private boolean execute(final IProcessDMContext processDmc, final IDebugCommandRequest request) {
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
fExecutor.execute(new DsfRunnable() {
|
fExecutor.execute(new DsfRunnable() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -116,8 +240,14 @@ public class DsfTerminateCommand implements ITerminateHandler {
|
||||||
procService.terminate(processDmc, new ImmediateRequestMonitor() {
|
procService.terminate(processDmc, new ImmediateRequestMonitor() {
|
||||||
@Override
|
@Override
|
||||||
protected void handleCompleted() {
|
protected void handleCompleted() {
|
||||||
|
if (!isSuccess()) {
|
||||||
request.setStatus(getStatus());
|
request.setStatus(getStatus());
|
||||||
request.done();
|
request.done();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
WaitForTerminationJob job = new WaitForTerminationJob(fSession.getId(), request);
|
||||||
|
job.schedule();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
@ -130,5 +260,4 @@ public class DsfTerminateCommand implements ITerminateHandler {
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,7 @@ import org.eclipse.debug.core.DebugException;
|
||||||
import org.eclipse.debug.core.DebugPlugin;
|
import org.eclipse.debug.core.DebugPlugin;
|
||||||
import org.eclipse.debug.core.ILaunch;
|
import org.eclipse.debug.core.ILaunch;
|
||||||
import org.eclipse.debug.core.ILaunchConfiguration;
|
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.IDisconnect;
|
||||||
import org.eclipse.debug.core.model.IMemoryBlockRetrieval;
|
import org.eclipse.debug.core.model.IMemoryBlockRetrieval;
|
||||||
import org.eclipse.debug.core.model.ISourceLocator;
|
import org.eclipse.debug.core.model.ISourceLocator;
|
||||||
|
@ -300,6 +301,10 @@ public class GdbLaunch extends DsfLaunch
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
@Override
|
@Override
|
||||||
public Object getAdapter(Class adapter) {
|
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.
|
// Must force adapters to be loaded.
|
||||||
Platform.getAdapterManager().loadAdapter(this, adapter.getName());
|
Platform.getAdapterManager().loadAdapter(this, adapter.getName());
|
||||||
return super.getAdapter(adapter);
|
return super.getAdapter(adapter);
|
||||||
|
|
Loading…
Add table
Reference in a new issue