mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-04-29 19:45:01 +02:00
[280461] Introduce a way to add extra executor depths to preserve the order of MI events and MI command results
This commit is contained in:
parent
29e5a63132
commit
75e6f8510f
8 changed files with 266 additions and 22 deletions
|
@ -36,6 +36,7 @@ import org.eclipse.cdt.dsf.debug.service.IRunControl.IExitedDMEvent;
|
|||
import org.eclipse.cdt.dsf.debug.service.IRunControl.IResumedDMEvent;
|
||||
import org.eclipse.cdt.dsf.debug.service.IRunControl.IStartedDMEvent;
|
||||
import org.eclipse.cdt.dsf.debug.service.IRunControl.ISuspendedDMEvent;
|
||||
import org.eclipse.cdt.dsf.debug.service.command.BufferedCommandControl;
|
||||
import org.eclipse.cdt.dsf.debug.service.command.CommandCache;
|
||||
import org.eclipse.cdt.dsf.debug.service.command.IEventListener;
|
||||
import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext;
|
||||
|
@ -428,12 +429,23 @@ public class GDBProcesses_7_0 extends AbstractDsfService
|
|||
|
||||
fCommandControl = getServicesTracker().getService(IGDBControl.class);
|
||||
fBackend = getServicesTracker().getService(IGDBBackend.class);
|
||||
BufferedCommandControl bufferedCommandControl = new BufferedCommandControl(fCommandControl, getExecutor(), 2);
|
||||
|
||||
fContainerCommandCache = new CommandCache(getSession(), fCommandControl);
|
||||
// These caches store the result of a command when received; also, these caches
|
||||
// are manipulated when receiving events. Currently, events are received after
|
||||
// three scheduling of the executor, while command results after only one. This
|
||||
// can cause problems because command results might be processed before an event
|
||||
// that actually arrived before the command result.
|
||||
// To solve this, we use a bufferedCommandControl that will delay the command
|
||||
// result by two scheduling of the executor.
|
||||
// See bug 280461
|
||||
fContainerCommandCache = new CommandCache(getSession(), bufferedCommandControl);
|
||||
fContainerCommandCache.setContextAvailable(fCommandControl.getContext(), true);
|
||||
fThreadCommandCache = new CommandCache(getSession(), fCommandControl);
|
||||
fThreadCommandCache = new CommandCache(getSession(), bufferedCommandControl);
|
||||
fThreadCommandCache.setContextAvailable(fCommandControl.getContext(), true);
|
||||
|
||||
// No need to use the bufferedCommandControl for the listThreadGroups cache
|
||||
// because it is not being affected by events.
|
||||
fListThreadGroupsAvailableCache = new CommandCache(getSession(), fCommandControl);
|
||||
fListThreadGroupsAvailableCache.setContextAvailable(fCommandControl.getContext(), true);
|
||||
|
||||
|
@ -757,7 +769,6 @@ public class GDBProcesses_7_0 extends AbstractDsfService
|
|||
final ICommandControlDMContext controlDmc = DMContexts.getAncestorOfType(dmc, ICommandControlDMContext.class);
|
||||
|
||||
if (controlDmc != null) {
|
||||
// Don't cache this command since the list can change at any time.
|
||||
fListThreadGroupsAvailableCache.execute(
|
||||
new MIListThreadGroups(controlDmc, true),
|
||||
new DataRequestMonitor<MIListThreadGroupsInfo>(getExecutor(), rm) {
|
||||
|
|
|
@ -37,6 +37,7 @@ import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerSuspendedDMEvent;
|
|||
import org.eclipse.cdt.dsf.debug.service.IRunControl.IResumedDMEvent;
|
||||
import org.eclipse.cdt.dsf.debug.service.IRunControl.ISuspendedDMEvent;
|
||||
import org.eclipse.cdt.dsf.debug.service.IRunControl.StateChangeReason;
|
||||
import org.eclipse.cdt.dsf.debug.service.command.BufferedCommandControl;
|
||||
import org.eclipse.cdt.dsf.debug.service.command.CommandCache;
|
||||
import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService;
|
||||
import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin;
|
||||
|
@ -127,7 +128,17 @@ public class MIMemory extends AbstractDsfService implements IMemory, ICachingSer
|
|||
private void doInitialize(final RequestMonitor requestMonitor) {
|
||||
// Create the command cache
|
||||
ICommandControlService commandControl = getServicesTracker().getService(ICommandControlService.class);
|
||||
fCommandCache = new CommandCache(getSession(), commandControl);
|
||||
BufferedCommandControl bufferedCommandControl = new BufferedCommandControl(commandControl, getExecutor(), 2);
|
||||
|
||||
// This cache stores the result of a command when received; also, this cache
|
||||
// is manipulated when receiving events. Currently, events are received after
|
||||
// three scheduling of the executor, while command results after only one. This
|
||||
// can cause problems because command results might be processed before an event
|
||||
// that actually arrived before the command result.
|
||||
// To solve this, we use a bufferedCommandControl that will delay the command
|
||||
// result by two scheduling of the executor.
|
||||
// See bug 280461
|
||||
fCommandCache = new CommandCache(getSession(), bufferedCommandControl);
|
||||
fCommandCache.setContextAvailable(commandControl.getContext(), true);
|
||||
|
||||
// Register this service
|
||||
|
|
|
@ -28,6 +28,7 @@ import org.eclipse.cdt.dsf.debug.service.IRunControl.IExitedDMEvent;
|
|||
import org.eclipse.cdt.dsf.debug.service.IRunControl.IResumedDMEvent;
|
||||
import org.eclipse.cdt.dsf.debug.service.IRunControl.IStartedDMEvent;
|
||||
import org.eclipse.cdt.dsf.debug.service.IRunControl.ISuspendedDMEvent;
|
||||
import org.eclipse.cdt.dsf.debug.service.command.BufferedCommandControl;
|
||||
import org.eclipse.cdt.dsf.debug.service.command.CommandCache;
|
||||
import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService;
|
||||
import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext;
|
||||
|
@ -347,7 +348,17 @@ public class MIProcesses extends AbstractDsfService implements IMIProcesses, ICa
|
|||
// new Hashtable<String, String>());
|
||||
|
||||
fCommandControl = getServicesTracker().getService(ICommandControlService.class);
|
||||
fContainerCommandCache = new CommandCache(getSession(), fCommandControl);
|
||||
BufferedCommandControl bufferedCommandControl = new BufferedCommandControl(fCommandControl, getExecutor(), 2);
|
||||
|
||||
// This cache stores the result of a command when received; also, this cache
|
||||
// is manipulated when receiving events. Currently, events are received after
|
||||
// three scheduling of the executor, while command results after only one. This
|
||||
// can cause problems because command results might be processed before an event
|
||||
// that actually arrived before the command result.
|
||||
// To solve this, we use a bufferedCommandControl that will delay the command
|
||||
// result by two scheduling of the executor.
|
||||
// See bug 280461
|
||||
fContainerCommandCache = new CommandCache(getSession(), bufferedCommandControl);
|
||||
fContainerCommandCache.setContextAvailable(fCommandControl.getContext(), true);
|
||||
getSession().addServiceEventListener(this, null);
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ import org.eclipse.cdt.dsf.debug.service.IRunControl;
|
|||
import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext;
|
||||
import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext;
|
||||
import org.eclipse.cdt.dsf.debug.service.IRunControl.StateChangeReason;
|
||||
import org.eclipse.cdt.dsf.debug.service.command.BufferedCommandControl;
|
||||
import org.eclipse.cdt.dsf.debug.service.command.CommandCache;
|
||||
import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService;
|
||||
import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin;
|
||||
|
@ -172,8 +173,20 @@ public class MIRegisters extends AbstractDsfService implements IRegisters, ICach
|
|||
* Create the lower level register cache.
|
||||
*/
|
||||
ICommandControlService commandControl = getServicesTracker().getService(ICommandControlService.class);
|
||||
fRegisterValueCache = new CommandCache(getSession(), commandControl);
|
||||
BufferedCommandControl bufferedCommandControl = new BufferedCommandControl(commandControl, getExecutor(), 2);
|
||||
|
||||
// This cache stores the result of a command when received; also, this cache
|
||||
// is manipulated when receiving events. Currently, events are received after
|
||||
// three scheduling of the executor, while command results after only one. This
|
||||
// can cause problems because command results might be processed before an event
|
||||
// that actually arrived before the command result.
|
||||
// To solve this, we use a bufferedCommandControl that will delay the command
|
||||
// result by two scheduling of the executor.
|
||||
// See bug 280461
|
||||
fRegisterValueCache = new CommandCache(getSession(), bufferedCommandControl);
|
||||
fRegisterValueCache.setContextAvailable(commandControl.getContext(), true);
|
||||
|
||||
// This cache is not affected by events so does not need the bufferedCommandControl
|
||||
fRegisterNameCache = new CommandCache(getSession(), commandControl);
|
||||
fRegisterNameCache.setContextAvailable(commandControl.getContext(), true);
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ import org.eclipse.cdt.dsf.datamodel.IDMContext;
|
|||
import org.eclipse.cdt.dsf.datamodel.IDMEvent;
|
||||
import org.eclipse.cdt.dsf.debug.service.ICachingService;
|
||||
import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext;
|
||||
import org.eclipse.cdt.dsf.debug.service.command.BufferedCommandControl;
|
||||
import org.eclipse.cdt.dsf.debug.service.command.CommandCache;
|
||||
import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService;
|
||||
import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlShutdownDMEvent;
|
||||
|
@ -288,7 +289,17 @@ public class MIRunControl extends AbstractDsfService implements IMIRunControl, I
|
|||
|
||||
private void doInitialize(final RequestMonitor rm) {
|
||||
fConnection = getServicesTracker().getService(ICommandControlService.class);
|
||||
fMICommandCache = new CommandCache(getSession(), fConnection);
|
||||
BufferedCommandControl bufferedCommandControl = new BufferedCommandControl(fConnection, getExecutor(), 2);
|
||||
|
||||
// This cache stores the result of a command when received; also, this cache
|
||||
// is manipulated when receiving events. Currently, events are received after
|
||||
// three scheduling of the executor, while command results after only one. This
|
||||
// can cause problems because command results might be processed before an event
|
||||
// that actually arrived before the command result.
|
||||
// To solve this, we use a bufferedCommandControl that will delay the command
|
||||
// result by two scheduling of the executor.
|
||||
// See bug 280461
|
||||
fMICommandCache = new CommandCache(getSession(), bufferedCommandControl);
|
||||
fMICommandCache.setContextAvailable(fConnection.getContext(), true);
|
||||
getSession().addServiceEventListener(this, null);
|
||||
rm.done();
|
||||
|
|
|
@ -31,6 +31,7 @@ import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext;
|
|||
import org.eclipse.cdt.dsf.debug.service.IRunControl.IResumedDMEvent;
|
||||
import org.eclipse.cdt.dsf.debug.service.IRunControl.ISuspendedDMEvent;
|
||||
import org.eclipse.cdt.dsf.debug.service.IRunControl.StateChangeReason;
|
||||
import org.eclipse.cdt.dsf.debug.service.command.BufferedCommandControl;
|
||||
import org.eclipse.cdt.dsf.debug.service.command.CommandCache;
|
||||
import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService;
|
||||
import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin;
|
||||
|
@ -190,7 +191,17 @@ public class MIStack extends AbstractDsfService
|
|||
|
||||
private void doInitialize(RequestMonitor rm) {
|
||||
ICommandControlService commandControl = getServicesTracker().getService(ICommandControlService.class);
|
||||
fMICommandCache = new CommandCache(getSession(), commandControl);
|
||||
BufferedCommandControl bufferedCommandControl = new BufferedCommandControl(commandControl, getExecutor(), 2);
|
||||
|
||||
// This cache stores the result of a command when received; also, this cache
|
||||
// is manipulated when receiving events. Currently, events are received after
|
||||
// three scheduling of the executor, while command results after only one. This
|
||||
// can cause problems because command results might be processed before an event
|
||||
// that actually arrived before the command result.
|
||||
// To solve this, we use a bufferedCommandControl that will delay the command
|
||||
// result by two scheduling of the executor.
|
||||
// See bug 280461
|
||||
fMICommandCache = new CommandCache(getSession(), bufferedCommandControl);
|
||||
fMICommandCache.setContextAvailable(commandControl.getContext(), true);
|
||||
fRunControl = getServicesTracker().getService(IRunControl.class);
|
||||
|
||||
|
|
|
@ -0,0 +1,149 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2009 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.debug.service.command;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
|
||||
import org.eclipse.cdt.dsf.concurrent.DsfExecutor;
|
||||
import org.eclipse.cdt.dsf.concurrent.DsfRunnable;
|
||||
import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor;
|
||||
|
||||
/**
|
||||
* A command control which delays the <strong>results</strong> of commands
|
||||
* sent to a command control, as well as events from the command control.
|
||||
* The delay is specified in the constructor using a number of executor
|
||||
* dispatch cycles.
|
||||
*
|
||||
* @since 2.1
|
||||
*/
|
||||
public class BufferedCommandControl implements ICommandControl {
|
||||
|
||||
private DsfExecutor fExecutor;
|
||||
private ICommandControl fControlDelegate;
|
||||
private int fDepth;
|
||||
|
||||
private ICommandListener fCommandListener = new ICommandListener() {
|
||||
public void commandQueued(ICommandToken token) {
|
||||
for (ICommandListener processor : fCommandProcessors) {
|
||||
processor.commandQueued(token);
|
||||
}
|
||||
};
|
||||
|
||||
public void commandRemoved(ICommandToken token) {
|
||||
for (ICommandListener processor : fCommandProcessors) {
|
||||
processor.commandRemoved(token);
|
||||
}
|
||||
};
|
||||
|
||||
public void commandSent(final ICommandToken token) {
|
||||
for (ICommandListener processor : fCommandProcessors) {
|
||||
processor.commandSent(token);
|
||||
}
|
||||
};
|
||||
|
||||
public void commandDone(final ICommandToken token, final ICommandResult result) {
|
||||
buffer(fDepth, new DsfRunnable() {
|
||||
public void run() {
|
||||
for (ICommandListener processor : fCommandProcessors) {
|
||||
processor.commandDone(token, result);
|
||||
}
|
||||
};
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
private IEventListener fEventListener = new IEventListener() {
|
||||
public void eventReceived(final Object output) {
|
||||
buffer(fDepth, new DsfRunnable() {
|
||||
public void run() {
|
||||
for (IEventListener processor : fEventProcessors) {
|
||||
processor.eventReceived(output);
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
private final List<ICommandListener> fCommandProcessors = new ArrayList<ICommandListener>();
|
||||
private final List<IEventListener> fEventProcessors = new ArrayList<IEventListener>();
|
||||
|
||||
public BufferedCommandControl(ICommandControl controlDelegate, DsfExecutor executor, int depth) {
|
||||
fControlDelegate = controlDelegate;
|
||||
fExecutor = executor;
|
||||
fDepth = depth;
|
||||
assert fDepth > 0;
|
||||
}
|
||||
|
||||
public void addCommandListener(ICommandListener listener) {
|
||||
if (fCommandProcessors.isEmpty()) {
|
||||
fControlDelegate.addCommandListener(fCommandListener);
|
||||
}
|
||||
fCommandProcessors.add(listener);
|
||||
}
|
||||
|
||||
|
||||
public void removeCommandListener(ICommandListener listener) {
|
||||
fCommandProcessors.remove(listener);
|
||||
if (fCommandProcessors.isEmpty()) {
|
||||
fControlDelegate.removeCommandListener(fCommandListener);
|
||||
}
|
||||
}
|
||||
|
||||
public void addEventListener(IEventListener listener) {
|
||||
if (fEventProcessors.isEmpty()) {
|
||||
fControlDelegate.addEventListener(fEventListener);
|
||||
}
|
||||
fEventProcessors.add(listener);
|
||||
}
|
||||
|
||||
public void removeEventListener(IEventListener listener) {
|
||||
fEventProcessors.remove(listener);
|
||||
if (fEventProcessors.isEmpty()) {
|
||||
fControlDelegate.removeEventListener(fEventListener);
|
||||
}
|
||||
}
|
||||
|
||||
public <V extends ICommandResult> ICommandToken queueCommand(final ICommand<V> command, final DataRequestMonitor<V> rm) {
|
||||
return fControlDelegate.queueCommand(
|
||||
command,
|
||||
new DataRequestMonitor<V>(ImmediateExecutor.getInstance(), rm) {
|
||||
@Override
|
||||
protected void handleCompleted() {
|
||||
buffer(fDepth, new DsfRunnable() {
|
||||
public void run() {
|
||||
rm.setData(getData());
|
||||
rm.setStatus(getStatus());
|
||||
rm.done();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void removeCommand(ICommandToken token) {
|
||||
fControlDelegate.removeCommand(token);
|
||||
}
|
||||
|
||||
private void buffer(final int depth, final DsfRunnable runnable) {
|
||||
if (depth == 0) {
|
||||
runnable.run();
|
||||
} else {
|
||||
fExecutor.execute(new DsfRunnable() {
|
||||
public void run() {
|
||||
buffer(depth - 1, runnable);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -22,7 +22,9 @@ import java.util.Map;
|
|||
import java.util.Set;
|
||||
|
||||
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.DMContexts;
|
||||
import org.eclipse.cdt.dsf.datamodel.IDMContext;
|
||||
import org.eclipse.cdt.dsf.internal.DsfPlugin;
|
||||
|
@ -118,6 +120,13 @@ public class CommandCache implements ICommandListener
|
|||
|
||||
private DsfSession fSession;
|
||||
|
||||
/**
|
||||
* The command control to be used to send commands to the backend.
|
||||
* In certain cases, a {@link BufferedCommandControl} should be used
|
||||
* to artificially delay the processing of command results; these
|
||||
* artificial delays are important to keep events and command results
|
||||
* ordered as received by the backend.
|
||||
*/
|
||||
private ICommandControl fCommandControl;
|
||||
|
||||
/*
|
||||
|
@ -384,7 +393,24 @@ public class CommandCache implements ICommandListener
|
|||
|
||||
finalCachedCmd.fToken = fCommandControl.queueCommand(
|
||||
finalCachedCmd.getCommand(),
|
||||
new DataRequestMonitor<ICommandResult>(fSession.getExecutor(), null) {
|
||||
new DataRequestMonitor<ICommandResult>(ImmediateExecutor.getInstance(), null) {
|
||||
@Override
|
||||
public synchronized void done() {
|
||||
// protect against the cache being called in non-session thread, but at
|
||||
// the same time avoid adding extra dispatch cycles to command processing.
|
||||
if (fSession.getExecutor().isInExecutorThread()) {;
|
||||
super.done();
|
||||
} else {
|
||||
fSession.getExecutor().execute(new DsfRunnable() {
|
||||
public void run() {
|
||||
superDone();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
private void superDone() {
|
||||
super.done();
|
||||
}
|
||||
@Override
|
||||
public void handleCompleted() {
|
||||
|
||||
|
@ -462,20 +488,21 @@ public class CommandCache implements ICommandListener
|
|||
}
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* This is an original request which completed. Indicate success or
|
||||
* failure to the original requesters.
|
||||
*/
|
||||
CommandResultInfo resultInfo = new CommandResultInfo(result, status);
|
||||
// Save the command result in cache, but only if the command's context
|
||||
// is still available. Otherwise an error may get cached incorrectly.
|
||||
if (isTargetAvailable(context)) {
|
||||
CommandResultInfo resultInfo = new CommandResultInfo(result, status);
|
||||
|
||||
if (fCachedContexts.get(context) != null){
|
||||
fCachedContexts.get(context).put(finalCachedCmd, resultInfo);
|
||||
} else {
|
||||
HashMap<CommandInfo, CommandResultInfo> map = new HashMap<CommandInfo, CommandResultInfo>();
|
||||
map.put(finalCachedCmd, resultInfo);
|
||||
fCachedContexts.put(context, map);
|
||||
}
|
||||
|
||||
if (fCachedContexts.get(context) != null){
|
||||
fCachedContexts.get(context).put(finalCachedCmd, resultInfo);
|
||||
} else {
|
||||
HashMap<CommandInfo, CommandResultInfo> map = new HashMap<CommandInfo, CommandResultInfo>();
|
||||
map.put(finalCachedCmd, resultInfo);
|
||||
fCachedContexts.put(context, map);
|
||||
}
|
||||
}
|
||||
// This is an original request which completed. Indicate success or
|
||||
// failure to the original requesters.
|
||||
if (!isSuccess()) {
|
||||
/*
|
||||
* We had some form of error with the original command. So notify the
|
||||
|
|
Loading…
Add table
Reference in a new issue