diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/META-INF/MANIFEST.MF b/dsf-gdb/org.eclipse.cdt.dsf.gdb/META-INF/MANIFEST.MF index f7477f1a159..02ad924b5b5 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/META-INF/MANIFEST.MF +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-Vendor: %providerName Bundle-SymbolicName: org.eclipse.cdt.dsf.gdb;singleton:=true -Bundle-Version: 4.4.0.qualifier +Bundle-Version: 4.5.0.qualifier Bundle-Activator: org.eclipse.cdt.dsf.gdb.internal.GdbPlugin Bundle-Localization: plugin Require-Bundle: org.eclipse.core.runtime, diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/pom.xml b/dsf-gdb/org.eclipse.cdt.dsf.gdb/pom.xml index 8bd0a815b64..a838863d064 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/pom.xml +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/pom.xml @@ -11,7 +11,7 @@ ../../pom.xml - 4.4.0-SNAPSHOT + 4.5.0-SNAPSHOT org.eclipse.cdt.dsf.gdb eclipse-plugin 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 d7a97edbeea..2936281ee6b 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 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2006, 2013 Wind River Systems and others. + * Copyright (c) 2006, 2014 Wind River Systems and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -12,6 +12,7 @@ * Marc Khouzam (Ericsson) - Support for operations on multiple execution contexts (bug 330974) * Alvaro Sanchez-Leon (Ericsson AB) - Support for Step into selection (bug 244865) * Alvaro Sanchez-Leon (Ericsson AB) - Bug 415362 + * Marc Khouzam (Ericsson) - Wait for *stopped event when suspending (bug 429621) *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.service; @@ -30,6 +31,7 @@ import org.eclipse.cdt.core.IAddress; import org.eclipse.cdt.core.model.IFunctionDeclaration; import org.eclipse.cdt.dsf.concurrent.CountingRequestMonitor; import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DsfRunnable; import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; import org.eclipse.cdt.dsf.concurrent.ImmediateCountingRequestMonitor; import org.eclipse.cdt.dsf.concurrent.ImmediateDataRequestMonitor; @@ -102,8 +104,10 @@ import org.eclipse.cdt.dsf.mi.service.command.output.MIThreadInfoInfo; import org.eclipse.cdt.dsf.service.AbstractDsfService; import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; import org.eclipse.cdt.dsf.service.DsfSession; +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.DebugException; import org.osgi.framework.BundleContext; @@ -501,7 +505,10 @@ public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunCo rm.done(doCanSuspend(context)); } - private boolean doCanSuspend(IExecutionDMContext context) { + /** + * @since 4.5 + */ + protected boolean doCanSuspend(IExecutionDMContext context) { // Thread case if (context instanceof IMIExecutionDMContext) { MIThreadRunState threadState = fThreadRunStates.get(context); @@ -548,7 +555,11 @@ public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunCo rm.done(); } - private void doSuspend(IMIExecutionDMContext context, final RequestMonitor rm) { + /** + * Request the suspend for a single thread and wait for a proper *stopped event before + * indicating success. + */ + private void doSuspend(final IMIExecutionDMContext context, final RequestMonitor rm) { if (!doCanSuspend(context)) { rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, "Given context: " + context + ", is already suspended.", null)); //$NON-NLS-1$ //$NON-NLS-2$ @@ -556,10 +567,35 @@ public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunCo return; } - fConnection.queueCommand(fCommandFactory.createMIExecInterrupt(context), new DataRequestMonitor(getExecutor(), rm)); + // Start the job before sending the interrupt command + // to make sure we don't miss the *stopped event + final MonitorSuspendJob monitorJob = new MonitorSuspendJob(context, 0, rm); + fConnection.queueCommand( + fCommandFactory.createMIExecInterrupt(context), + new ImmediateDataRequestMonitor() { + @Override + protected void handleSuccess() { + // Nothing to do in the case of success, the monitoring job + // will take care of completing the RM once it gets the + // *stopped event. + } + + @Override + protected void handleFailure() { + // In case of failure, we must cancel the monitoring job + // and indicate the failure in the rm. + monitorJob.cleanAndCancel(); + rm.done(getStatus()); + } + }); } - private void doSuspend(IMIContainerDMContext context, final RequestMonitor rm) { + /** + * Request the suspend for a process. In this case we don't wait for any *stopped events explicitly + * because we would need to wait for one per thread and manage all those events. It is not necessary. + * @since 4.5 + */ + protected void doSuspend(IMIContainerDMContext context, final RequestMonitor rm) { if (!doCanSuspend(context)) { rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, "Given context: " + context + ", is already suspended.", null)); //$NON-NLS-1$ //$NON-NLS-2$ @@ -570,6 +606,85 @@ public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunCo String groupId = context.getGroupId(); fConnection.queueCommand(fCommandFactory.createMIExecInterrupt(context, groupId), new DataRequestMonitor(getExecutor(), rm)); } + + /** + * Job that waits for a *stopped event after a suspend operation on a thread. + * + * If the suspend operation receives its corresponding *stopped event in time, + * the job will mark the RM with a success status. If the event is not received + * before the timeout, the job will fail the request monitor. + * + * @since 4.5 + */ + protected class MonitorSuspendJob extends Job { + // Bug 310274. Until we have a preference to configure timeouts, + // we need a large enough default timeout to accommodate slow + // remote sessions. + private final static int TIMEOUT_DEFAULT_VALUE = 5000; + + private final RequestMonitor fRequestMonitor; + private final IMIExecutionDMContext fThread; + + public MonitorSuspendJob(IMIExecutionDMContext dmc, int timeout, RequestMonitor rm) { + super("Suspend monitor job."); //$NON-NLS-1$ + setSystem(true); + fThread = dmc; + fRequestMonitor = rm; + + if (timeout <= 0) { + timeout = TIMEOUT_DEFAULT_VALUE; // default of 5 seconds + } + + // Register to listen for the stopped event + getSession().addServiceEventListener(this, null); + + schedule(timeout); + } + + /** + * Cleanup job and cancel it. + * This method is required because super.canceling() is only called + * if the job is actually running. + */ + public boolean cleanAndCancel() { + if (getExecutor().isInExecutorThread()) { + getSession().removeServiceEventListener(this); + } else { + getExecutor().submit( + new DsfRunnable() { + @Override + public void run() { + getSession().removeServiceEventListener(MonitorSuspendJob.this); + } + }); + } + return cancel(); + } + + @DsfServiceEventHandler + public void eventDispatched(MIStoppedEvent e) { + if (fThread.equals(e.getDMContext())) { + // The thread we were waiting for did stop + if (cleanAndCancel()) { + fRequestMonitor.done(); + } + } + } + + @Override + protected IStatus run(IProgressMonitor monitor) { + // This will be called when the timeout is hit and no *stopped event was received + getExecutor().submit( + new DsfRunnable() { + @Override + public void run() { + getSession().removeServiceEventListener(MonitorSuspendJob.this); + fRequestMonitor.done(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.REQUEST_FAILED, "Suspend operation timeout.", null)); //$NON-NLS-1$ + } + }); + return Status.OK_STATUS; + } + } // ------------------------------------------------------------------------ // Resume diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBRunControl_7_2_NS.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBRunControl_7_2_NS.java index faabbe0d4b2..b341e54743a 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBRunControl_7_2_NS.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBRunControl_7_2_NS.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2011, 2012 Ericsson and others. + * Copyright (c) 2011, 2014 Ericsson and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -7,6 +7,7 @@ * * Contributors: * Ericsson - initial API and implementation + * Marc Khouzam (Ericsson) - Wait for *stopped event when suspending (bug 429621) *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.service; @@ -105,31 +106,16 @@ public class GDBRunControl_7_2_NS extends GDBRunControl_7_0_NS // by GDB 7.2, we have to make sure not to use it twice. // Bug 340262 @Override - public void suspend(final IExecutionDMContext context, final RequestMonitor rm) { - assert context != null; - - IMIExecutionDMContext thread = DMContexts.getAncestorOfType(context, IMIExecutionDMContext.class); - IMIContainerDMContext container = DMContexts.getAncestorOfType(context, IMIContainerDMContext.class); - if (thread == null && container == null) { - rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, "Invalid context type.", null)); //$NON-NLS-1$ - rm.done(); + protected void doSuspend(IMIContainerDMContext context, RequestMonitor rm) { + if (!doCanSuspend(context)) { + rm.done(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, + "Given context: " + context + ", is already suspended.", null)); //$NON-NLS-1$ //$NON-NLS-2$ return; } - - canSuspend(context, new ImmediateDataRequestMonitor(rm) { - @Override - protected void handleSuccess() { - if (getData()) { - fConnection.queueCommand(fCommandFactory.createMIExecInterrupt(context), new DataRequestMonitor(getExecutor(), rm)); - } else { - rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, - "Given context: " + context + ", is already suspended.", null)); //$NON-NLS-1$ //$NON-NLS-2$ - rm.done(); - } - } - }); + + fConnection.queueCommand(fCommandFactory.createMIExecInterrupt(context), new DataRequestMonitor(getExecutor(), rm)); } - + // Now that the flag --thread-group is globally supported // by GDB 7.2, we have to make sure not to use it twice. // Bug 340262