1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-04-23 14:42:11 +02:00

[298909] - [launch] DSF suspend trigger susceptible to race conditions

This commit is contained in:
Pawel Piech 2010-01-05 23:51:29 +00:00
parent 47e8b801a0
commit e31d06b003
10 changed files with 389 additions and 27 deletions

View file

@ -345,5 +345,15 @@
id="org.eclipse.cdt.dsf.gdb.ui.GdbDebugTextHover">
</hover>
</extension>
<extension
point="org.eclipse.debug.ui.contextViewBindings">
</extension>
<extension
point="org.eclipse.debug.ui.debugModelContextBindings">
<modelContextBinding
contextId="org.eclipse.cdt.debug.ui.debugging"
debugModelId="org.eclipse.cdt.dsf.gdb">
</modelContextBinding>
</extension>
</plugin>

View file

@ -29,7 +29,6 @@ import org.eclipse.cdt.dsf.debug.ui.actions.DsfStepIntoCommand;
import org.eclipse.cdt.dsf.debug.ui.actions.DsfStepOverCommand;
import org.eclipse.cdt.dsf.debug.ui.actions.DsfStepReturnCommand;
import org.eclipse.cdt.dsf.debug.ui.actions.DsfSuspendCommand;
import org.eclipse.cdt.dsf.debug.ui.contexts.DsfSuspendTrigger;
import org.eclipse.cdt.dsf.debug.ui.sourcelookup.DsfSourceDisplayAdapter;
import org.eclipse.cdt.dsf.debug.ui.viewmodel.SteppingController;
import org.eclipse.cdt.dsf.debug.ui.viewmodel.actions.DefaultRefreshAllTarget;
@ -99,7 +98,7 @@ public class GdbAdapterFactory
final GdbConnectCommand fConnectCommand;
final GdbDisconnectCommand fDisconnectCommand;
final IDebugModelProvider fDebugModelProvider;
final DsfSuspendTrigger fSuspendTrigger;
final GdbSuspendTrigger fSuspendTrigger;
final GdbSteppingModeTarget fSteppingModeTarget;
final IModelSelectionPolicyFactory fModelSelectionPolicyFactory;
final SteppingController fSteppingController;
@ -137,7 +136,7 @@ public class GdbAdapterFactory
fTerminateCommand = new DsfTerminateCommand(session);
fConnectCommand = new GdbConnectCommand(session);
fDisconnectCommand = new GdbDisconnectCommand(session);
fSuspendTrigger = new DsfSuspendTrigger(session, fLaunch);
fSuspendTrigger = new GdbSuspendTrigger(session, fLaunch);
fModelSelectionPolicyFactory = new DefaultDsfModelSelectionPolicyFactory();
fRefreshAllTarget = new DefaultRefreshAllTarget();
fReverseToggleTarget = new GdbReverseToggleCommand(session);

View file

@ -0,0 +1,81 @@
/*******************************************************************************
* Copyright (c) 2008 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
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Wind River Systems - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.dsf.gdb.internal.ui;
import java.util.concurrent.RejectedExecutionException;
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.ImmediateExecutor;
import org.eclipse.cdt.dsf.datamodel.IDMContext;
import org.eclipse.cdt.dsf.debug.service.IProcesses;
import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext;
import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService;
import org.eclipse.cdt.dsf.debug.ui.contexts.DsfSuspendTrigger;
import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.ILaunch;
/**
* @since 2.1
*/
public class GdbSuspendTrigger extends DsfSuspendTrigger {
public GdbSuspendTrigger(DsfSession session, ILaunch launch) {
super(session, launch);
}
@Override
protected void getLaunchTopContainers(final DataRequestMonitor<IContainerDMContext[]> rm) {
try {
getSession().getExecutor().execute(new DsfRunnable() {
public void run() {
IProcesses processService = getServicesTracker().getService(IProcesses.class);
ICommandControlService controlService = getServicesTracker().getService(ICommandControlService.class);
if (processService == null || controlService == null) {
rm.setStatus(new Status(IStatus.ERROR, GdbUIPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_STATE, "Not available", null)); //$NON-NLS-1$
rm.done();
return;
}
processService.getProcessesBeingDebugged(
controlService.getContext(),
new DataRequestMonitor<IDMContext[]>(ImmediateExecutor.getInstance(), rm) {
@Override
public void handleSuccess() {
IContainerDMContext[] containers = new IContainerDMContext[getData().length];
for (int i = 0; i < containers.length; i++) {
if (getData()[i] instanceof IContainerDMContext) {
containers[i] = (IContainerDMContext)getData()[i];
} else {
// By convention the processes should be containers, but the API
// does not enforce this.
assert false;
rm.setData(new IContainerDMContext[0]);
rm.done();
return;
}
}
rm.setData(containers);
rm.done();
}
});
}
});
} catch (RejectedExecutionException e) {
rm.setStatus(new Status(IStatus.ERROR, GdbUIPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_STATE, "Not available", e)); //$NON-NLS-1$
rm.done();
}
}
}

View file

@ -13,11 +13,19 @@ package org.eclipse.cdt.dsf.debug.ui.contexts;
import java.util.concurrent.RejectedExecutionException;
import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor;
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.ImmediateExecutor;
import org.eclipse.cdt.dsf.concurrent.ThreadSafe;
import org.eclipse.cdt.dsf.datamodel.DataModelInitializedEvent;
import org.eclipse.cdt.dsf.debug.service.IRunControl;
import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext;
import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext;
import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin;
import org.eclipse.cdt.dsf.service.DsfServiceEventHandler;
import org.eclipse.cdt.dsf.service.DsfServicesTracker;
import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.ISafeRunnable;
@ -45,8 +53,9 @@ public class DsfSuspendTrigger implements ISuspendTrigger {
private final DsfSession fSession;
private final ILaunch fLaunch;
private boolean fDisposed = false;
private volatile boolean fDisposed = false;
private boolean fEventListenerRegisterd = false;
private final DsfServicesTracker fServicesTracker;
@ThreadSafe
private final ListenerList fListeners = new ListenerList();
@ -55,6 +64,7 @@ public class DsfSuspendTrigger implements ISuspendTrigger {
public DsfSuspendTrigger(DsfSession session, ILaunch launch) {
fSession = session;
fLaunch = launch;
fServicesTracker = new DsfServicesTracker(DsfUIPlugin.getBundleContext(), fSession.getId());
try {
fSession.getExecutor().execute(new DsfRunnable() {
public void run() {
@ -68,28 +78,93 @@ public class DsfSuspendTrigger implements ISuspendTrigger {
}
@ThreadSafe
public void addSuspendTriggerListener(ISuspendTriggerListener listener) {
if (fListeners != null) {
fListeners.add(listener);
}
public void addSuspendTriggerListener(final ISuspendTriggerListener listener) {
fListeners.add(listener);
// Check if an execution context in the model is already suspended.
// If so notify the listener.
getIsLaunchSuspended(new DataRequestMonitor<Boolean>(ImmediateExecutor.getInstance(), null) {
@Override
protected void handleSuccess() {
if (!fDisposed && getData().booleanValue()) {
listener.suspended(fLaunch, null);
}
}
});
}
@ThreadSafe
public void removeSuspendTriggerListener(ISuspendTriggerListener listener) {
if (fListeners != null) {
fListeners.remove(listener);
}
fListeners.remove(listener);
}
@ThreadSafe
public void dispose() {
if (fEventListenerRegisterd) {
fSession.removeServiceEventListener(this);
}
fServicesTracker.dispose();
fDisposed = true;
}
/**
* @noreference This method is not intended to be referenced by clients.
*/
@DsfServiceEventHandler
public void eventDispatched(IRunControl.ISuspendedDMEvent e) {
fireSuspended(null);
}
/**
* @noreference This method is not intended to be referenced by clients.
*/
@DsfServiceEventHandler
public void eventDispatched(DataModelInitializedEvent e) {
getIsLaunchSuspended(new DataRequestMonitor<Boolean>(ImmediateExecutor.getInstance(), null) {
@Override
protected void handleSuccess() {
if (!fDisposed && getData().booleanValue()) {
fireSuspended(null);
}
}
});
}
/**
* Returns the services tracker used by the suspend trigger.
* @since 2.1
*/
protected DsfServicesTracker getServicesTracker() {
return fServicesTracker;
}
/**
* Returns the launch for this suspend trigger.
* @since 2.1
*/
@ThreadSafe
protected ILaunch getLaunch() {
return fLaunch;
}
/**
* Returns the DSF session for this suspend trigger.
* @since 2.1
*/
@ThreadSafe
protected DsfSession getSession() {
return fSession;
}
/**
* Notifies the listeners that a suspend event was received.
*
* @param context
*
* @since 2.1
*/
@ThreadSafe
protected void fireSuspended(final Object context) {
final Object[] listeners = fListeners.getListeners();
if (listeners.length != 0) {
new Job("DSF Suspend Trigger Notify") { //$NON-NLS-1$
@ -104,7 +179,7 @@ public class DsfSuspendTrigger implements ISuspendTrigger {
final ISuspendTriggerListener listener = (ISuspendTriggerListener) listeners[i];
SafeRunner.run(new ISafeRunnable() {
public void run() throws Exception {
listener.suspended(fLaunch, null);
listener.suspended(fLaunch, context);
}
public void handleException(Throwable exception) {
@ -120,4 +195,144 @@ public class DsfSuspendTrigger implements ISuspendTrigger {
}
}
/**
* Retrieves the top-level containers for this launch. This method should
* be overriden by specific debugger integrations.
* @param rm
*
* @since 2.1
*/
@ThreadSafe
protected void getLaunchTopContainers(DataRequestMonitor<IContainerDMContext[]> rm) {
rm.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.NOT_SUPPORTED, "Not implemented.", null)); //$NON-NLS-1$
rm.done();
}
/**
* Checks if the given launch is currently suspended.
*
* @param rm Request monitor.
*
* @since 2.1
*/
@ThreadSafe
private void getIsLaunchSuspended(final DataRequestMonitor<Boolean> rm) {
getLaunchTopContainers(new DataRequestMonitor<IContainerDMContext[]>(fSession.getExecutor(), rm) {
@Override
protected void handleSuccess() {
final CountingRequestMonitor crm = new CountingRequestMonitor(fSession.getExecutor(), rm) {
@Override
protected void handleSuccess() {
if (rm.getData() == null) {
rm.setData(Boolean.FALSE);
}
rm.done();
};
};
int count = 0;
for (final IContainerDMContext containerCtx : getData()) {
getIsContainerSuspended(containerCtx, new DataRequestMonitor<Boolean>(ImmediateExecutor.getInstance(), crm) {
@Override
protected void handleSuccess() {
if (getData().booleanValue()) {
rm.setData(Boolean.TRUE);
}
crm.done();
};
});
count++;
}
crm.setDoneCount(count);
}
});
}
/**
* Recursively checks if the given container or any of its execution
* contexts are suspended.
*
* @param container Container to check.
* @param rm Request monitor.
*
* @since 2.1
*/
@ConfinedToDsfExecutor("fSession.getExecutor()")
private void getIsContainerSuspended(final IContainerDMContext container, final DataRequestMonitor<Boolean> rm) {
// Check if run control service is still available.
IRunControl rc = fServicesTracker.getService(IRunControl.class);
if (rc == null) {
rm.setData(Boolean.FALSE);
rm.done();
return;
}
// Check if container is suspended. If so, stop searching.
if (rc.isSuspended(container)) {
rm.setData(Boolean.TRUE);
rm.done();
return;
}
// Retrieve the execution contexts and check if any of them are suspended.
rc.getExecutionContexts(
container,
new DataRequestMonitor<IExecutionDMContext[]>(fSession.getExecutor(), rm) {
@Override
protected void handleSuccess() {
// Check if run control service is still available.
IRunControl rc = fServicesTracker.getService(IRunControl.class);
if (rc == null) {
rm.setData(Boolean.FALSE);
rm.done();
return;
}
// If any of the execution contexts are suspended, stop searching
boolean hasContainers = false;
for (IExecutionDMContext execCtx : getData()) {
if (rc.isSuspended(execCtx)) {
rm.setData(Boolean.TRUE);
rm.done();
return;
}
hasContainers = hasContainers || execCtx instanceof IContainerDMContext;
}
// If any of the returned contexts were containers, check them recursively.
if (hasContainers) {
final CountingRequestMonitor crm = new CountingRequestMonitor(fSession.getExecutor(), rm) {
@Override
protected void handleSuccess() {
if (rm.getData() == null) {
rm.setData(Boolean.FALSE);
}
rm.done();
};
};
int count = 0;
for (IExecutionDMContext execCtx : getData()) {
if (execCtx instanceof IContainerDMContext) {
getIsContainerSuspended(
(IContainerDMContext)execCtx,
new DataRequestMonitor<Boolean>(ImmediateExecutor.getInstance(), crm) {
@Override
protected void handleSuccess() {
if (getData().booleanValue()) {
rm.setData(Boolean.TRUE);
}
crm.done();
};
});
count++;
}
}
crm.setDoneCount(count);
} else {
rm.setData(Boolean.FALSE);
rm.done();
}
}
});
}
}

View file

@ -70,7 +70,7 @@
point="org.eclipse.debug.ui.contextViewBindings">
<contextViewBinding
contextId="org.eclipse.cdt.examples.dsf.pda.debugging"
viewId="org.eclipse.cdt.examples.dsf.pda.dataStackView"/>
viewId="org.eclipse.debug.ui.ExpressionView"/>
</extension>
<extension

View file

@ -0,0 +1,56 @@
/*******************************************************************************
* Copyright (c) 2008 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
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Wind River Systems - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.examples.dsf.pda.ui;
import java.util.concurrent.RejectedExecutionException;
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.debug.service.IRunControl.IContainerDMContext;
import org.eclipse.cdt.dsf.debug.ui.contexts.DsfSuspendTrigger;
import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.cdt.examples.dsf.pda.service.PDACommandControl;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.ILaunch;
/**
* @since 2.1
*/
public class PDASuspendTrigger extends DsfSuspendTrigger {
public PDASuspendTrigger(DsfSession session, ILaunch launch) {
super(session, launch);
}
@Override
protected void getLaunchTopContainers(final DataRequestMonitor<IContainerDMContext[]> rm) {
try {
getSession().getExecutor().execute(new DsfRunnable() {
public void run() {
PDACommandControl control =
getServicesTracker().getService(PDACommandControl.class);
if (control != null) {
rm.setData(new IContainerDMContext[] { control.getContext() });
} else {
rm.setStatus(new Status(IStatus.ERROR, PDAUIPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_STATE, "Not available", null));
}
rm.done();
}
});
} catch (RejectedExecutionException e) {
rm.setStatus(new Status(IStatus.ERROR, PDAUIPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_STATE, "Not available", e));
rm.done();
}
}
}

View file

@ -10,24 +10,19 @@
*******************************************************************************/
package org.eclipse.cdt.examples.dsf.pda.ui;
import org.eclipse.cdt.dsf.datamodel.IDMContext;
import org.eclipse.cdt.dsf.debug.ui.actions.DsfResumeCommand;
import org.eclipse.cdt.dsf.debug.ui.actions.DsfStepIntoCommand;
import org.eclipse.cdt.dsf.debug.ui.actions.DsfStepOverCommand;
import org.eclipse.cdt.dsf.debug.ui.actions.DsfStepReturnCommand;
import org.eclipse.cdt.dsf.debug.ui.actions.DsfSuspendCommand;
import org.eclipse.cdt.dsf.debug.ui.contexts.DsfSuspendTrigger;
import org.eclipse.cdt.dsf.debug.ui.sourcelookup.DsfSourceDisplayAdapter;
import org.eclipse.cdt.dsf.debug.ui.viewmodel.SteppingController;
import org.eclipse.cdt.dsf.debug.ui.viewmodel.launch.DefaultDsfModelSelectionPolicyFactory;
import org.eclipse.cdt.dsf.debug.ui.viewmodel.launch.DefaultDsfSelectionPolicy;
import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.IDMVMContext;
import org.eclipse.cdt.examples.dsf.pda.PDAPlugin;
import org.eclipse.cdt.examples.dsf.pda.launch.PDALaunch;
import org.eclipse.cdt.examples.dsf.pda.ui.actions.PDATerminateCommand;
import org.eclipse.cdt.examples.dsf.pda.ui.viewmodel.PDAVMAdapter;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.commands.IResumeHandler;
import org.eclipse.debug.core.commands.IStepIntoHandler;
import org.eclipse.debug.core.commands.IStepOverHandler;
@ -36,10 +31,7 @@ import org.eclipse.debug.core.commands.ISuspendHandler;
import org.eclipse.debug.core.commands.ITerminateHandler;
import org.eclipse.debug.core.model.IDebugModelProvider;
import org.eclipse.debug.core.sourcelookup.ISourceLookupDirector;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelSelectionPolicy;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelSelectionPolicyFactory;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext;
import org.eclipse.debug.ui.IDebugUIConstants;
import org.eclipse.debug.ui.sourcelookup.ISourceDisplay;
/**
@ -59,7 +51,7 @@ class SessionAdapterSet {
final DsfSuspendCommand fSuspendCommand;
final DsfResumeCommand fResumeCommand;
final PDATerminateCommand fTerminateCommand;
final DsfSuspendTrigger fSuspendTrigger;
final PDASuspendTrigger fSuspendTrigger;
// Adapters for integration with other UI actions
final IDebugModelProvider fDebugModelProvider;
@ -96,7 +88,7 @@ class SessionAdapterSet {
fSuspendCommand = new DsfSuspendCommand(session);
fResumeCommand = new DsfResumeCommand(session);
fTerminateCommand = new PDATerminateCommand(session);
fSuspendTrigger = new DsfSuspendTrigger(session, fLaunch);
fSuspendTrigger = new PDASuspendTrigger(session, fLaunch);
session.registerModelAdapter(IStepIntoHandler.class, fStepIntoCommand);
session.registerModelAdapter(IStepOverHandler.class, fStepOverCommand);

View file

@ -12,6 +12,7 @@ package org.eclipse.cdt.examples.dsf.pda.launch;
import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
import org.eclipse.cdt.dsf.concurrent.Sequence;
import org.eclipse.cdt.dsf.datamodel.DataModelInitializedEvent;
import org.eclipse.cdt.dsf.debug.service.BreakpointsMediator;
import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.cdt.examples.dsf.pda.service.PDABackend;
@ -105,12 +106,18 @@ public class PDAServicesInitSequence extends Sequence {
new PDARegisters(fSession).initialize(requestMonitor);
}
},
/*
* Indicate that the Data Model has been filled. This will trigger the Debug view to expand.
*/
new Step() {
@Override
public void execute(RequestMonitor requestMonitor) {
fRunControl.resume(fCommandControl.getContext(), requestMonitor);
public void execute(final RequestMonitor requestMonitor) {
fSession.dispatchEvent(
new DataModelInitializedEvent(fCommandControl.getContext()),
fCommandControl.getProperties());
requestMonitor.done();
}
},
}
};
// Sequence input parameters, used in initializing services.

View file

@ -192,6 +192,7 @@ public class PDAExpressions extends AbstractDsfService implements ICachingServic
fCommandControl = getServicesTracker().getService(PDACommandControl.class);
fStack = getServicesTracker().getService(PDAStack.class);
fCommandCache = new CommandCache(getSession(), fCommandControl);
fCommandCache.setContextAvailable(fCommandControl.getContext(), true);
getSession().addServiceEventListener(this, null);

View file

@ -207,6 +207,7 @@ public class PDAStack extends AbstractDsfService implements IStack, ICachingServ
// Create the commands cache
fCommandCache = new CommandCache(getSession(), fCommandControl);
fCommandCache.setContextAvailable(fCommandControl.getContext(), true);
// Register to listen for run control events, to clear cache accordingly.
getSession().addServiceEventListener(this, null);