1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-07-23 08:55:25 +02:00

Bug 337893 - Support concurrent setting of bp on a running target

This commit is contained in:
Marc Khouzam 2011-03-28 17:42:56 +00:00
parent 09499bd32c
commit ccfa88d37b

View file

@ -12,13 +12,15 @@
package org.eclipse.cdt.dsf.mi.service;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Vector;
import org.eclipse.cdt.core.IAddress;
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants;
import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor;
import org.eclipse.cdt.dsf.concurrent.Immutable;
import org.eclipse.cdt.dsf.concurrent.MultiRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
import org.eclipse.cdt.dsf.concurrent.Sequence;
import org.eclipse.cdt.dsf.concurrent.Sequence.Step;
@ -907,39 +909,69 @@ public class MIRunControl extends AbstractDsfService implements IMIRunControl, I
rm.done();
}
}
/**
* @since 3.0
*/
public void executeWithTargetAvailable(IDMContext ctx, Sequence.Step[] steps, RequestMonitor rm) {
Vector<Step> totalStepsVector = new Vector<Step>();
totalStepsVector.add(new IsTargetAvailableStep(ctx));
totalStepsVector.add(new MakeTargetAvailableStep());
for (Step step : steps) {
totalStepsVector.add(step);
}
totalStepsVector.add(new RestoreTargetStateStep());
final Step[] totalSteps = totalStepsVector.toArray(new Step[totalStepsVector.size()]);
getExecutor().execute(new Sequence(getExecutor(), rm) {
@Override public Step[] getSteps() { return totalSteps; }
});
}
/* ******************************************************************************
* Section to support making operations even when the target is unavailable.
* Section to support operations even when the target is unavailable.
*
* Basically, we must make sure the container is suspended before making
* certain operations (currently breakpoints). If we don't, we must first
* suspend the container, then perform the specified operations,
* and finally resume the container.
* See https://bugs.eclipse.org/bugs/show_bug.cgi?id=242943
* and https://bugs.eclipse.org/bugs/show_bug.cgi?id=282273
* See http://bugs.eclipse.org/242943
* and http://bugs.eclipse.org/282273
*
* Note that for multi-process, the correct container must be suspended for the
* breakpoint to be inserted on that container. Not a big deal though, since
* a breakpointDmc is mapped to a specific container. Also, since we are in
* all-stop mode here, it does not really matter what we stop, everything will
* stop.
* See http://bugs.eclipse.org/337893
*
* ******************************************************************************/
private IContainerDMContext fContainerDmc = null;
private boolean fTargetAvailable = false;
/**
* Utility class to store the parameters of the executeWithTargetAvailable() operations.
* @since 4.0
*/
protected static class TargetAvailableOperationInfo {
public IDMContext ctx;
public Sequence.Step[] steps;
public RequestMonitor rm;
public TargetAvailableOperationInfo(IDMContext ctx, Step[] steps, RequestMonitor rm) {
super();
this.ctx = ctx;
this.steps = steps;
this.rm = rm;
}
};
// Keep track of if the target was available or not when we started the operation
private boolean fTargetAvailable;
// The execution context that need to be available.
private IExecutionDMContext fExecutionDmc;
// Do we currently have an executeWithTargetAvailable() operation ongoing?
private boolean fOngoingOperation;
// Are we currently executing steps passed into executeWithTargetAvailable()?
// This allows us to know if we can add more steps to execute or if we missed
// our opportunity
private boolean fCurrentlyExecutingSteps;
// MultiRequestMonitor that allows us to track all the different steps we are
// executing. Once all steps are executed, we can complete this MultiRM and
// allow the global sequence to continue.
// Note that we couldn't use a CountingRequestMonitor because that type of RM
// needs to know in advance how many subRms it will track; the MultiRM allows us
// to receive more steps to execute continuously, and be able to upate the MultiRM.
private MultiRequestMonitor<RequestMonitor> fExecuteQueuedOpsStepMonitor;
// The number of batches of steps that are still being executing for potentially
// concurrent executeWithTargetAvailable() operations.
// Once this gets to zero, we know we have executed all the steps we were aware of
// and we can complete the operation.
private int fNumStepsStillExecuting;
// Queue of executeWithTargetAvailable() operations that need to be processed.
private LinkedList<TargetAvailableOperationInfo> fOperationsPending = new LinkedList<TargetAvailableOperationInfo>();
/**
* Returns whether the target is available to perform operations
* @since 3.0
@ -948,15 +980,186 @@ public class MIRunControl extends AbstractDsfService implements IMIRunControl, I
return fTargetAvailable;
}
/** @since 4.0 */
protected void setTargetAvailable(boolean available) {
fTargetAvailable = available;
}
/**
* Returns whether the target must be suspended before performing the breakpoint operation
* Returns the execution context that needs to be suspended to perform the
* required operation.
* @since 3.0
*/
protected IExecutionDMContext getContextToSuspend() {
return fContainerDmc;
return fExecutionDmc;
}
/** @since 4.0 */
protected void setContextToSuspend(IExecutionDMContext context) {
fExecutionDmc = context;
}
/**
* Returns whether there is currently an ExecuteWithTargetAvailable() operation ongoing.
* @since 4.0
*/
protected boolean isTargetAvailableOperationOngoing() {
return fOngoingOperation;
}
/** @since 4.0 */
protected void setTargetAvailableOperationOngoing(boolean ongoing) {
fOngoingOperation = ongoing;
}
/**
* Returns whether we are current in the process of executing the steps
* that were passed to ExecuteWithTargetAvailable().
* When this value is true, we can send more steps to be executed.
* @since 4.0
*/
protected boolean isCurrentlyExecutingSteps() {
return fCurrentlyExecutingSteps;
}
/** @since 4.0 */
protected void setCurrentlyExecutingSteps(boolean executing) {
fCurrentlyExecutingSteps = executing;
}
/**
* Returns the requestMonitor that will be run once all steps sent to
* ExecuteWithTargetAvailable() have been executed.
* @since 4.0
*/
protected MultiRequestMonitor<RequestMonitor> getExecuteQueuedStepsRM() {
return fExecuteQueuedOpsStepMonitor;
}
/** @since 4.0 */
protected void setExecuteQueuedStepsRM(MultiRequestMonitor<RequestMonitor> rm) {
fExecuteQueuedOpsStepMonitor = rm;
}
/**
* Returns the number of batches of steps sent to ExecuteWithTargetAvailable()
* that are still executing. Once this number reaches zero, we can complete
* the overall ExecuteWithTargetAvailable() operation.
* @since 4.0
*/
protected int getNumStepsStillExecuting() {
return fNumStepsStillExecuting;
}
/** @since 4.0 */
protected void setNumStepsStillExecuting(int num) {
fNumStepsStillExecuting = num;
}
/**
* Returns the queue of executeWithTargetAvailable() operations that still need to be processed
* @since 4.0
*/
protected LinkedList<TargetAvailableOperationInfo> getOperationsPending() {
return fOperationsPending;
}
/**
* This method takes care of executing a batch of steps that were passed to
* ExecuteWithTargetAvailable(). The method is used to track the progress
* of all these batches of steps, so that we know exactly when all of them
* have been completed and the global sequence can be completed.
* @since 4.0
*/
protected void executeSteps(final TargetAvailableOperationInfo info) {
fNumStepsStillExecuting++;
// This RM propagates any error to the original rm of the actual steps.
// Even in case of errors for these steps, we want to continue the overall sequence
RequestMonitor stepsRm = new RequestMonitor(ImmediateExecutor.getInstance(), null) {
@Override
protected void handleCompleted() {
info.rm.setStatus(getStatus());
// It is important to call rm.done() right away.
// This is because some other operation we are performing might be waiting
// for this one to be done. If we try to wait for the entire sequence to be
// done, then we will never finish because one monitor will never show as
// done, waiting for the second one.
info.rm.done();
fExecuteQueuedOpsStepMonitor.requestMonitorDone(this);
fNumStepsStillExecuting--;
if (fNumStepsStillExecuting == 0) {
fExecuteQueuedOpsStepMonitor.doneAdding();
}
}
};
fExecuteQueuedOpsStepMonitor.add(stepsRm);
getExecutor().execute(new Sequence(getExecutor(), stepsRm) {
@Override public Step[] getSteps() { return info.steps; }
});
}
/**
* @since 3.0
*/
public void executeWithTargetAvailable(IDMContext ctx, final Sequence.Step[] steps, final RequestMonitor rm) {
if (!fOngoingOperation) {
// We are the first operation of this kind currently requested
// so we need to start the sequence
fOngoingOperation = true;
// We always go through our queue, even if we only have a single call to this method
fOperationsPending.add(new TargetAvailableOperationInfo(ctx, steps, rm));
// Steps that need to be executed to perform the operation
final Step[] sequenceSteps = new Step[] {
new IsTargetAvailableStep(ctx),
new MakeTargetAvailableStep(),
new ExecuteQueuedOperationsStep(),
new RestoreTargetStateStep(),
};
// Once all the sequence is completed, we need to see if we have received
// another request that we now need to process
RequestMonitor sequenceCompletedRm = new RequestMonitor(getExecutor(), null) {
@Override
protected void handleCompleted() {
fOngoingOperation = false;
if (fOperationsPending.size() > 0) {
// Darn, more operations came in. Trigger their processing
// by calling executeWithTargetAvailable() on the last one
TargetAvailableOperationInfo info = fOperationsPending.removeLast();
executeWithTargetAvailable(info.ctx, info.steps, info.rm);
}
// no other rm.done() needs to be called, they have all been handled already
}
};
getExecutor().execute(new Sequence(getExecutor(), sequenceCompletedRm) {
@Override public Step[] getSteps() { return sequenceSteps; }
});
} else {
// We are currently already executing such an operation
// If we are still in the process of executing steps, let's include this new set of steps.
// This is important because some steps may depend on these new ones.
if (fCurrentlyExecutingSteps) {
executeSteps(new TargetAvailableOperationInfo(ctx, steps, rm));
} else {
// Too late to execute the new steps, so queue them for later
fOperationsPending.add(new TargetAvailableOperationInfo(ctx, steps, rm));
}
}
}
/**
* This part of the sequence verifies if the execution context of interest
* is suspended or not.
* @since 3.0
*/
protected class IsTargetAvailableStep extends Sequence.Step {
@ -968,11 +1171,9 @@ public class MIRunControl extends AbstractDsfService implements IMIRunControl, I
@Override
public void execute(final RequestMonitor rm) {
fContainerDmc = DMContexts.getAncestorOfType(fCtx, IContainerDMContext.class);
if (fContainerDmc != null) {
// In all-stop, if any process is suspended, then all of them are suspended
// so we only need to check this process.
fTargetAvailable = isSuspended(fContainerDmc);
fExecutionDmc = DMContexts.getAncestorOfType(fCtx, IMIContainerDMContext.class);
if (fExecutionDmc != null) {
fTargetAvailable = isSuspended(fExecutionDmc);
rm.done();
return;
}
@ -987,12 +1188,14 @@ public class MIRunControl extends AbstractDsfService implements IMIRunControl, I
assert getData() != null;
if (getData().length == 0) {
// Happens at startup, starting with GDB 7.0
// Happens at startup, starting with GDB 7.0.
// This means the target is available
fTargetAvailable = true;
} else {
fContainerDmc = (IContainerDMContext)(getData()[0]);
fTargetAvailable = isSuspended(fContainerDmc);
// In all-stop, if any process is suspended, then all of them are suspended
// so we only need to check the first process.
fExecutionDmc = (IExecutionDMContext)(getData()[0]);
fTargetAvailable = isSuspended(fExecutionDmc);
}
rm.done();
}
@ -1001,6 +1204,8 @@ public class MIRunControl extends AbstractDsfService implements IMIRunControl, I
};
/**
* If the execution context of interest is not suspended, this step
* will interrupt it.
* @since 3.0
*/
protected class MakeTargetAvailableStep extends Sequence.Step {
@ -1033,6 +1238,44 @@ public class MIRunControl extends AbstractDsfService implements IMIRunControl, I
};
/**
* This step of the sequence takes care of executing all the steps that
* were passed to ExecuteWithTargetAvailable().
* @since 4.0
*/
protected class ExecuteQueuedOperationsStep extends Sequence.Step {
@Override
public void execute(final RequestMonitor rm) {
fCurrentlyExecutingSteps = true;
// It is important to use an ImmediateExecutor for this RM, to make sure we don't risk getting a new
// call to ExecuteWithTargetAvailable() when we just finished executing the steps.
fExecuteQueuedOpsStepMonitor = new MultiRequestMonitor<RequestMonitor>(ImmediateExecutor.getInstance(), rm) {
@Override
protected void handleCompleted() {
assert fOperationsPending.size() == 0;
// We don't handle errors here. Instead, we have already propagated any
// errors to each rm for each set of steps
fCurrentlyExecutingSteps = false;
// Continue the sequence
rm.done();
}
};
// Tell the RM that we need to confirm when we are done adding sub-rms
fExecuteQueuedOpsStepMonitor.requireDoneAdding();
// All pending operations are independent of each other so we can
// run them concurrently.
while (fOperationsPending.size() > 0) {
executeSteps(fOperationsPending.poll());
}
}
};
/**
* If the sequence had to interrupt the execution context of interest,
* this step will resume it again to reach the same state as when we started.
* @since 3.0
*/
protected class RestoreTargetStateStep extends Sequence.Step {
@ -1078,6 +1321,10 @@ public class MIRunControl extends AbstractDsfService implements IMIRunControl, I
}
};
/* ******************************************************************************
* End of section to support operations even when the target is unavailable.
* ******************************************************************************/
/**
* {@inheritDoc}
* @since 1.1