1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-06-06 09:16:02 +02:00

[305178] Handling of failure cases when interrupting the target.

This commit is contained in:
Marc Khouzam 2010-03-09 19:59:33 +00:00
parent 2edac02083
commit 2ff6a31767
6 changed files with 209 additions and 43 deletions

View file

@ -36,7 +36,9 @@ import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin;
import org.eclipse.cdt.dsf.gdb.launching.LaunchUtils;
import org.eclipse.cdt.dsf.gdb.service.command.GDBControl.InitializationShutdownStep;
import org.eclipse.cdt.dsf.mi.service.IMIBackend;
import org.eclipse.cdt.dsf.mi.service.command.events.MISignalEvent;
import org.eclipse.cdt.dsf.service.AbstractDsfService;
import org.eclipse.cdt.dsf.service.DsfServiceEventHandler;
import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.cdt.utils.spawner.ProcessFactory;
import org.eclipse.cdt.utils.spawner.Spawner;
@ -99,6 +101,14 @@ public class GDBBackend extends AbstractDsfService implements IGDBBackend {
private int fGDBExitValue;
private int fGDBLaunchTimeout = 30;
/**
* A Job that will set a failed status
* in the proper request monitor, if the interrupt
* did not succeed after a certain time.
*/
private MonitorInterruptJob fInterruptFailedJob;
public GDBBackend(DsfSession session, ILaunchConfiguration lc) {
super(session);
fBackendId = "gdb[" +Integer.toString(fgInstanceCounter++) + "]"; //$NON-NLS-1$//$NON-NLS-2$
@ -364,6 +374,20 @@ public class GDBBackend extends AbstractDsfService implements IGDBBackend {
}
}
/**
* @since 3.0
*/
public void interrupt(final RequestMonitor rm) {
if (fProcess instanceof Spawner) {
Spawner gdbSpawner = (Spawner) fProcess;
gdbSpawner.interrupt();
fInterruptFailedJob = new MonitorInterruptJob(rm);
} else {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.NOT_SUPPORTED, "Cannot interrupt.", null)); //$NON-NLS-1$
rm.done();
}
}
public void destroy() {
// destroy() should be supported even if it's not spawner.
if (getState() == State.STARTED) {
@ -558,6 +582,8 @@ public class GDBBackend extends AbstractDsfService implements IGDBBackend {
IGDBBackend.class.getName() },
new Hashtable<String,String>());
getSession().addServiceEventListener(GDBBackend.this, null);
/*
* This event is not consumed by any one at present, instead it's
* the GDBControlInitializedDMEvent that's used to indicate that GDB
@ -574,6 +600,7 @@ public class GDBBackend extends AbstractDsfService implements IGDBBackend {
@Override
protected void shutdown(RequestMonitor requestMonitor) {
unregister();
getSession().removeServiceEventListener(GDBBackend.this);
requestMonitor.done();
}
}
@ -625,4 +652,50 @@ public class GDBBackend extends AbstractDsfService implements IGDBBackend {
}
}
/**
* Monitors an ongoing interrupt to be able to properly
* deal with the request monitor.
*/
private class MonitorInterruptJob extends Job {
private final RequestMonitor fRequestMonitor;
public MonitorInterruptJob(RequestMonitor rm) {
super("Interrupt monitor job.");
setSystem(true);
fRequestMonitor = rm;
schedule(5000); // Give the interrupt 5 seconds to succeed
}
@Override
protected IStatus run(IProgressMonitor monitor) {
getExecutor().submit(
new DsfRunnable() {
public void run() {
fRequestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.REQUEST_FAILED, "Interrupt failed.", null)); //$NON-NLS-1$
fRequestMonitor.done();
}
});
return Status.OK_STATUS;
}
public RequestMonitor getRequestMonitor() { return fRequestMonitor; }
}
/*
* We must listen for an MI event and not a higher-level ISuspendedEvent.
* The reason is that some ISuspendedEvent are not sent when the target stops,
* in cases where we don't want to views to update.
* For example, if we want to interrupt the target to set a breakpoint,
* this interruption is done silently; we will receive the MI event though.
*/
/** @since 3.0 */
@DsfServiceEventHandler
public void eventDispatched(final MISignalEvent e) {
if (fInterruptFailedJob != null) {
if (fInterruptFailedJob.cancel()) {
fInterruptFailedJob.getRequestMonitor().done();
}
fInterruptFailedJob = null;
}
}
}

View file

@ -138,11 +138,11 @@ public class GDBRunControl extends MIRunControl {
@Override
protected void handleSuccess() {
if (getData()) {
fGdb.interrupt();
fGdb.interrupt(rm);
} else {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Context cannot be suspended.", null)); //$NON-NLS-1$
rm.done();
}
rm.done();
}
});
}

View file

@ -144,11 +144,11 @@ public class GDBRunControl_7_0 extends MIRunControl implements IReverseRunContro
@Override
protected void handleSuccess() {
if (getData()) {
fGdb.interrupt();
fGdb.interrupt(rm);
} else {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Context cannot be suspended.", null)); //$NON-NLS-1$
rm.done();
}
rm.done();
}
});
}

View file

@ -265,13 +265,17 @@ public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunCo
private RunToLineActiveOperation fRunToLineActiveOperation = null;
/**
* A counter of MIRunning/MIStopped events that should be kept silent for the specified thread.
* Indicates that the next MIRunning event for this thread should be silenced.
*/
private class DisableRunningAndStoppedEvents {
public IMIExecutionDMContext executionDmc = null;
public int count = 0;
};
private DisableRunningAndStoppedEvents fDisableRunningAndStoppedEvents = new DisableRunningAndStoppedEvents();
private IMIExecutionDMContext fDisableNextRunningEventDmc;
/**
* Indicates that the next MISignal (MIStopped) event for this thread should be silenced.
*/
private IMIExecutionDMContext fDisableNextSignalEventDmc;
/**
* Stores the silenced MIStopped event in case we need to use it for a failure.
*/
private MIStoppedEvent fSilencedSignalEvent;
///////////////////////////////////////////////////////////////////////////
// Initialization and shutdown
@ -925,12 +929,22 @@ public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunCo
fExecutionDmcToSuspend = (IMIExecutionDMContext)getData()[0];
// Don't broadcast the coming stopped/running events
assert fDisableRunningAndStoppedEvents.count == 0;
fDisableRunningAndStoppedEvents.count++;
fDisableRunningAndStoppedEvents.executionDmc = fExecutionDmcToSuspend;
assert fDisableNextRunningEventDmc == null;
assert fDisableNextSignalEventDmc == null;
suspend(fExecutionDmcToSuspend, rm);
// Don't broadcast the next stopped signal event
fDisableNextSignalEventDmc = fExecutionDmcToSuspend;
suspend(fExecutionDmcToSuspend,
new RequestMonitor(getExecutor(), rm) {
@Override
protected void handleFailure() {
// We weren't able to suspend, so abort the operation
fDisableNextSignalEventDmc = null;
super.handleFailure();
};
});
}
});
} else {
@ -946,15 +960,39 @@ public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunCo
@Override
public void execute(final RequestMonitor rm) {
if (!fTargetAvailable) {
assert fDisableRunningAndStoppedEvents.count == 0 || fDisableRunningAndStoppedEvents.count == 1;
fDisableRunningAndStoppedEvents.count++;
fDisableRunningAndStoppedEvents.executionDmc = fExecutionDmcToSuspend;
assert fDisableNextRunningEventDmc == null;
fDisableNextRunningEventDmc = fExecutionDmcToSuspend;
// Can't use the resume() call because we 'silently' stopped
// so resume() will not know we are actually stopped
fConnection.queueCommand(
fCommandFactory.createMIExecContinue(fExecutionDmcToSuspend),
new DataRequestMonitor<MIInfo>(getExecutor(), rm));
new DataRequestMonitor<MIInfo>(getExecutor(), rm) {
@Override
protected void handleSuccess() {
fSilencedSignalEvent = null;
super.handleSuccess();
}
@Override
protected void handleFailure() {
// Darn, we're unable to restart the target. Must cleanup!
fDisableNextRunningEventDmc = null;
// We must also sent the Stopped event that we had kept silent
if (fSilencedSignalEvent != null) {
eventDispatched(fSilencedSignalEvent);
fSilencedSignalEvent = null;
} else {
// Maybe the stopped event didn't arrive yet.
// We don't want to silence it anymore
fDisableNextSignalEventDmc = null;
}
super.handleFailure();
}
});
} else {
// We didn't suspend the thread, so we don't need to resume it
rm.done();
@ -972,10 +1010,10 @@ public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunCo
*/
@DsfServiceEventHandler
public void eventDispatched(final MIRunningEvent e) {
if (fDisableRunningAndStoppedEvents.count > 0 &&
fDisableRunningAndStoppedEvents.executionDmc.equals(e.getDMContext())) {
if (fDisableNextRunningEventDmc != null &&
fDisableNextRunningEventDmc.equals(e.getDMContext())) {
fDisableRunningAndStoppedEvents.count--;
fDisableNextRunningEventDmc = null;
// Don't broadcast the running event
return;
@ -1041,10 +1079,11 @@ public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunCo
}
}
}
if (fDisableRunningAndStoppedEvents.count > 0 &&
fDisableRunningAndStoppedEvents.executionDmc.equals(e.getDMContext())) {
if (fDisableNextSignalEventDmc != null && e instanceof MISignalEvent &&
fDisableNextSignalEventDmc.equals(e.getDMContext())) {
fDisableRunningAndStoppedEvents.count--;
fDisableNextSignalEventDmc = null;
fSilencedSignalEvent = e;
// Don't broadcast the stopped event
return;

View file

@ -14,6 +14,7 @@ package org.eclipse.cdt.dsf.gdb.service;
import java.util.List;
import java.util.Properties;
import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
import org.eclipse.cdt.dsf.mi.service.IMIBackend;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
@ -100,7 +101,17 @@ public interface IGDBBackend extends IMIBackend {
* Sends an interrupt signal to the GDB process.
*/
public void interrupt();
/**
* Interrupts the backend.
* The request monitor indicates if the interrupt succeeded or not;
* it may or may not indicate that the backend is already interrupted,
* but does indicate if the backend will become interrupted.
*
* @since 3.0
*/
public void interrupt(RequestMonitor rm);
/**
* @return The type of the session currently ongoing with the backend
*/

View file

@ -324,10 +324,18 @@ public class MIRunControl extends AbstractDsfService implements IMIRunControl, I
private StateChangeReason fStateChangeReason;
private IExecutionDMContext fStateChangeTriggeringContext;
/**
* A counter of MIRunning/MIStopped events that should
* be kept silent.
* Indicates that the next MIRunning event should be silenced.
*/
private int fDisableRunningAndStoppedEventsCount = 0;
private boolean fDisableNextRunningEvent;
/**
* Indicates that the next MISignal (MIStopped) event should be silenced.
*/
private boolean fDisableNextSignalEvent;
/**
* Stores the silenced MIStopped event in case we need to use it
* for a failure.
*/
private MIStoppedEvent fSilencedSignalEvent;
private static final int FAKE_THREAD_ID = 0;
@ -396,9 +404,9 @@ public class MIRunControl extends AbstractDsfService implements IMIRunControl, I
*/
@DsfServiceEventHandler
public void eventDispatched(final MIRunningEvent e) {
if (fDisableRunningAndStoppedEventsCount > 0) {
fDisableRunningAndStoppedEventsCount--;
// We don't broadcast running events right now
if (fDisableNextRunningEvent) {
fDisableNextRunningEvent = false;
// We don't broadcast this running event
return;
}
@ -421,9 +429,10 @@ public class MIRunControl extends AbstractDsfService implements IMIRunControl, I
*/
@DsfServiceEventHandler
public void eventDispatched(final MIStoppedEvent e) {
if (fDisableRunningAndStoppedEventsCount > 0) {
fDisableRunningAndStoppedEventsCount--;
// We don't broadcast stopped events right now
if (fDisableNextSignalEvent && e instanceof MISignalEvent) {
fDisableNextSignalEvent = false;
fSilencedSignalEvent = e;
// We don't broadcast this stopped event
return;
}
@ -443,7 +452,7 @@ public class MIRunControl extends AbstractDsfService implements IMIRunControl, I
// Set the triggering context only if it's not the container context, since we are looking for a thread.
IExecutionDMContext triggeringCtx = !e.getDMContext().equals(containerDmc) ? e.getDMContext() : null;
if (triggeringCtx == null) {
// Still no thread. Let's ask the bakend for one.
// Still no thread. Let's ask the backend for one.
// We need a proper thread id for the debug view to select the right thread
// Bug 300096 comment #15 and Bug 302597
getConnection().queueCommand(
@ -934,10 +943,20 @@ public class MIRunControl extends AbstractDsfService implements IMIRunControl, I
@Override
public void execute(final RequestMonitor rm) {
if (!isTargetAvailable()) {
// Don't broadcast the coming stopped/running events
assert fDisableRunningAndStoppedEventsCount == 0;
fDisableRunningAndStoppedEventsCount++;
suspend(getContextToSuspend(), rm);
assert fDisableNextRunningEvent == false;
assert fDisableNextSignalEvent == false;
// Don't broadcast the coming stopped signal event
fDisableNextSignalEvent = true;
suspend(getContextToSuspend(),
new RequestMonitor(getExecutor(), rm) {
@Override
protected void handleFailure() {
// We weren't able to suspend, so abort the operation
fDisableNextSignalEvent = false;
super.handleFailure();
};
});
} else {
rm.done();
}
@ -951,14 +970,38 @@ public class MIRunControl extends AbstractDsfService implements IMIRunControl, I
@Override
public void execute(final RequestMonitor rm) {
if (!isTargetAvailable()) {
assert fDisableRunningAndStoppedEventsCount == 0 || fDisableRunningAndStoppedEventsCount == 1;
fDisableRunningAndStoppedEventsCount++;
assert fDisableNextRunningEvent == false;
fDisableNextRunningEvent = true;
// Can't use the resume() call because we 'silently' stopped
// so resume() will not know we are actually stopped
fConnection.queueCommand(
fCommandFactory.createMIExecContinue(getContextToSuspend()),
new DataRequestMonitor<MIInfo>(getExecutor(), rm));
new DataRequestMonitor<MIInfo>(getExecutor(), rm) {
@Override
protected void handleSuccess() {
fSilencedSignalEvent = null;
super.handleSuccess();
}
@Override
protected void handleFailure() {
// Darn, we're unable to restart the target. Must cleanup!
fDisableNextRunningEvent = false;
// We must also sent the Stopped event that we had kept silent
if (fSilencedSignalEvent != null) {
eventDispatched(fSilencedSignalEvent);
fSilencedSignalEvent = null;
} else {
// Maybe the stopped event didn't arrive yet.
// We don't want to silence it anymore
fDisableNextSignalEvent = false;
}
super.handleFailure();
}
});
} else {
// We didn't suspend the container, so we don't need to resume it
rm.done();