diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBBackend.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBBackend.java index d4c0f88f914..449efeebd62 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBBackend.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBBackend.java @@ -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()); + 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; + } + } } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBRunControl.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBRunControl.java index 26f666b03c5..0ee263647ce 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBRunControl.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBRunControl.java @@ -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(); } }); } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBRunControl_7_0.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBRunControl_7_0.java index 772d28ac953..ec7d521857d 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBRunControl_7_0.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBRunControl_7_0.java @@ -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(); } }); } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBRunControl_7_0_NS.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBRunControl_7_0_NS.java index f0408f74531..39157e3078e 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBRunControl_7_0_NS.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBRunControl_7_0_NS.java @@ -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(getExecutor(), rm)); + new DataRequestMonitor(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; diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/IGDBBackend.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/IGDBBackend.java index 99e0f90d44c..970f0a65724 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/IGDBBackend.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/IGDBBackend.java @@ -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 */ diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIRunControl.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIRunControl.java index f5878929634..cb96e6084fe 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIRunControl.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIRunControl.java @@ -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(getExecutor(), rm)); + new DataRequestMonitor(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();