diff --git a/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/internal/provisional/ui/viewmodel/launch/StackFramesVMNode.java b/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/internal/provisional/ui/viewmodel/launch/StackFramesVMNode.java index 046b2830e2f..1981bfddd96 100644 --- a/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/internal/provisional/ui/viewmodel/launch/StackFramesVMNode.java +++ b/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/internal/provisional/ui/viewmodel/launch/StackFramesVMNode.java @@ -71,7 +71,11 @@ public class StackFramesVMNode extends AbstractDMVMNode @Override protected void updateElementsInSessionThread(final IChildrenUpdate update) { - if (!checkService(IStack.class, null, update)) return; + + if ( getServicesTracker().getService(IStack.class) == null ) { + handleFailedUpdate(update); + return; + } final IExecutionDMContext execDmc = findDmcInPath(update.getViewerInput(), update.getElementPath(), IExecutionDMContext.class); if (execDmc == null) { @@ -88,7 +92,10 @@ public class StackFramesVMNode extends AbstractDMVMNode // Failed to retrieve frames. If we are stepping, we // might still be able to retrieve just the top stack // frame, which would still be useful in Debug View. - if (!checkService(IRunControl.class, null, update)) return; + if ( getServicesTracker().getService(IRunControl.class) == null ) { + handleFailedUpdate(update); + return; + } if (getServicesTracker().getService(IRunControl.class).isStepping(execDmc)) { getElementsTopStackFrameOnly(update); } else { @@ -121,8 +128,11 @@ public class StackFramesVMNode extends AbstractDMVMNode try { getSession().getExecutor().execute(new DsfRunnable() { public void run() { - if (!checkService(IStack.class, null, update)) return; - + if ( getServicesTracker().getService(IStack.class) == null ) { + handleFailedUpdate(update); + return; + } + getServicesTracker().getService(IStack.class).getTopFrame( execDmc, new DataRequestMonitor(getExecutor(), null) { @@ -173,7 +183,16 @@ public class StackFramesVMNode extends AbstractDMVMNode protected void updateLabelInSessionThread(ILabelUpdate[] updates) { for (final ILabelUpdate update : updates) { final IFrameDMContext dmc = findDmcInPath(update.getViewerInput(), update.getElementPath(), IFrameDMContext.class); - if (!checkDmc(dmc, update) || !checkService(IStack.class, null, update)) continue; + + if ( dmc == null ) { + handleFailedUpdate(update); + continue; + } + if ( getServicesTracker().getService(IStack.class) == null ) { + handleFailedUpdate(update); + continue; + } + getDMVMProvider().getModelData( this, update, diff --git a/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/internal/provisional/ui/viewmodel/modules/ModulesVMNode.java b/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/internal/provisional/ui/viewmodel/modules/ModulesVMNode.java index cdcef5106ba..3071973199e 100644 --- a/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/internal/provisional/ui/viewmodel/modules/ModulesVMNode.java +++ b/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/internal/provisional/ui/viewmodel/modules/ModulesVMNode.java @@ -42,7 +42,11 @@ public class ModulesVMNode extends AbstractDMVMNode @Override protected void updateElementsInSessionThread(final IChildrenUpdate update) { - if (!checkService(IModules.class, null, update)) return; + + if ( getServicesTracker().getService(IModules.class) == null ) { + handleFailedUpdate(update); + return; + } final ISymbolDMContext symDmc = findDmcInPath(update.getViewerInput(), update.getElementPath(), ISymbolDMContext.class) ; @@ -83,7 +87,16 @@ public class ModulesVMNode extends AbstractDMVMNode protected void updateLabelInSessionThread(ILabelUpdate[] updates) { for (final ILabelUpdate update : updates) { final IModuleDMContext dmc = findDmcInPath(update.getViewerInput(), update.getElementPath(), IModuleDMContext.class); - if (!checkDmc(dmc, update) || !checkService(IModules.class, null, update)) continue; + // If either update or service are not valid, fail the update and exit. + if ( dmc == null ) { + handleFailedUpdate(update); + continue; + } + if ( getServicesTracker().getService(IModules.class) == null ) { + handleFailedUpdate(update); + continue; + } + // Use different image for loaded and unloaded symbols when event to report loading of symbols is implemented. update.setImageDescriptor(DsfDebugUIPlugin.getImageDescriptor(IDsfDebugUIConstants.IMG_OBJS_SHARED_LIBRARY_SYMBOLS_LOADED), 0); diff --git a/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/internal/provisional/ui/viewmodel/register/RegisterBitFieldVMNode.java b/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/internal/provisional/ui/viewmodel/register/RegisterBitFieldVMNode.java index 0d29ee3042e..0d11212aadb 100644 --- a/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/internal/provisional/ui/viewmodel/register/RegisterBitFieldVMNode.java +++ b/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/internal/provisional/ui/viewmodel/register/RegisterBitFieldVMNode.java @@ -29,6 +29,7 @@ import org.eclipse.dd.dsf.debug.internal.provisional.ui.viewmodel.numberformat.I import org.eclipse.dd.dsf.debug.internal.provisional.ui.viewmodel.register.RegisterBitFieldCellModifier.BitFieldEditorStyle; import org.eclipse.dd.dsf.debug.internal.ui.DsfDebugUIPlugin; import org.eclipse.dd.dsf.debug.service.IFormattedValues; +import org.eclipse.dd.dsf.debug.service.IMemory; import org.eclipse.dd.dsf.debug.service.IRegisters; import org.eclipse.dd.dsf.debug.service.IRunControl; import org.eclipse.dd.dsf.debug.service.IFormattedValues.FormattedValueDMContext; @@ -69,9 +70,8 @@ import org.eclipse.swt.widgets.Composite; @SuppressWarnings("restriction") public class RegisterBitFieldVMNode extends AbstractExpressionVMNode - implements IElementEditor, IElementLabelProvider + implements IElementEditor, IElementLabelProvider { - protected class BitFieldVMC extends DMVMContext implements IFormattedValueVMContext { @@ -271,7 +271,10 @@ public class RegisterBitFieldVMNode extends AbstractExpressionVMNode ); } - + /* + * (non-Javadoc) + * @see org.eclipse.debug.internal.ui.viewers.model.provisional.IElementLabelProvider#update(org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate[]) + */ public void update(final ILabelUpdate[] updates) { try { getSession().getExecutor().execute(new DsfRunnable() { @@ -285,17 +288,25 @@ public class RegisterBitFieldVMNode extends AbstractExpressionVMNode } } - + /* + * Updates the requested label based on the specified column. + */ protected void updateLabelInSessionThread(ILabelUpdate[] updates) { for (final ILabelUpdate update : updates) { - if (!checkService(IRegisters.class, null, update)) continue; + final IRegisters regService = getServicesTracker().getService(IRegisters.class); + + if ( regService == null ) { + handleFailedUpdate(update); + continue; + } final IBitFieldDMContext dmc = findDmcInPath(update.getViewerInput(), update.getElementPath(), IRegisters.IBitFieldDMContext.class); getDMVMProvider().getModelData( - this, update, - getServicesTracker().getService(IRegisters.class), + this, + update, + regService, dmc, new DataRequestMonitor(getSession().getExecutor(), null) { @Override @@ -422,6 +433,10 @@ public class RegisterBitFieldVMNode extends AbstractExpressionVMNode } } + /* + * (non-Javadoc) + * @see org.eclipse.dd.dsf.ui.viewmodel.datamodel.AbstractDMVMNode#updateElementsInSessionThread(org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate) + */ @Override protected void updateElementsInSessionThread(final IChildrenUpdate update) { final IRegisterDMContext regDmc = findDmcInPath(update.getViewerInput(), update.getElementPath(), IRegisterDMContext.class); @@ -431,9 +446,15 @@ public class RegisterBitFieldVMNode extends AbstractExpressionVMNode return; } - if (!checkService(IRegisters.class, null, update)) return; + IRegisters regService = getServicesTracker().getService(IRegisters.class); - getServicesTracker().getService(IRegisters.class).getBitFields( + if ( regService == null ) { + handleFailedUpdate(update); + return; + } + + + regService.getBitFields( regDmc, new DataRequestMonitor(getSession().getExecutor(), null) { @Override @@ -449,11 +470,19 @@ public class RegisterBitFieldVMNode extends AbstractExpressionVMNode }); } + /* + * (non-Javadoc) + * @see org.eclipse.dd.dsf.ui.viewmodel.datamodel.AbstractDMVMNode#createVMContext(org.eclipse.dd.dsf.datamodel.IDMContext) + */ @Override protected IDMVMContext createVMContext(IDMContext dmc) { return new BitFieldVMC(dmc); } + /* + * (non-Javadoc) + * @see org.eclipse.dd.dsf.ui.viewmodel.IVMNode#getDeltaFlags(java.lang.Object) + */ public int getDeltaFlags(Object e) { if (e instanceof IRunControl.ISuspendedDMEvent) { return IModelDelta.CONTENT; @@ -463,6 +492,10 @@ public class RegisterBitFieldVMNode extends AbstractExpressionVMNode return IModelDelta.STATE; } + if (e instanceof IMemory.IMemoryChangedEvent) { + return IModelDelta.CONTENT; + } + if (e instanceof PropertyChangeEvent && ((PropertyChangeEvent)e).getProperty() == IDebugVMConstants.CURRENT_FORMAT_STORAGE) { @@ -472,6 +505,10 @@ public class RegisterBitFieldVMNode extends AbstractExpressionVMNode return IModelDelta.NO_CHANGE; } + /* + * (non-Javadoc) + * @see org.eclipse.dd.dsf.ui.viewmodel.IVMNode#buildDelta(java.lang.Object, org.eclipse.dd.dsf.ui.viewmodel.VMDelta, int, org.eclipse.dd.dsf.concurrent.RequestMonitor) + */ public void buildDelta(Object e, VMDelta parentDelta, int nodeOffset, RequestMonitor rm) { if (e instanceof IRunControl.ISuspendedDMEvent) { // Create a delta that the whole register group has changed. @@ -479,14 +516,17 @@ public class RegisterBitFieldVMNode extends AbstractExpressionVMNode } if (e instanceof IRegisters.IBitFieldChangedDMEvent) { - /* * Create a delta indicating the bit field has changed. */ parentDelta.addNode( createVMContext(((IRegisters.IBitFieldChangedDMEvent)e).getDMContext()), IModelDelta.STATE ); } - if (e instanceof PropertyChangeEvent && + if (e instanceof IMemory.IMemoryChangedEvent) { + parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.CONTENT); + } + + if (e instanceof PropertyChangeEvent && ((PropertyChangeEvent)e).getProperty() == IDebugVMConstants.CURRENT_FORMAT_STORAGE) { parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.CONTENT); @@ -495,6 +535,10 @@ public class RegisterBitFieldVMNode extends AbstractExpressionVMNode rm.done(); } + /* + * (non-Javadoc) + * @see org.eclipse.debug.internal.ui.viewers.model.provisional.IElementEditor#getCellEditor(org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext, java.lang.String, java.lang.Object, org.eclipse.swt.widgets.Composite) + */ public CellEditor getCellEditor(IPresentationContext context, String columnId, Object element, Composite parent) { if (IDebugVMConstants.COLUMN_ID__VALUE.equals(columnId)) { @@ -541,6 +585,10 @@ public class RegisterBitFieldVMNode extends AbstractExpressionVMNode return null; } + /* + * (non-Javadoc) + * @see org.eclipse.debug.internal.ui.viewers.model.provisional.IElementEditor#getCellModifier(org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext, java.lang.Object) + */ public ICellModifier getCellModifier(IPresentationContext context, Object element) { /* @@ -628,6 +676,10 @@ public class RegisterBitFieldVMNode extends AbstractExpressionVMNode return null; } + /* + * (non-Javadoc) + * @see org.eclipse.dd.dsf.debug.internal.provisional.ui.viewmodel.expression.AbstractExpressionVMNode#testElementForExpression(java.lang.Object, org.eclipse.debug.core.model.IExpression, org.eclipse.dd.dsf.concurrent.DataRequestMonitor) + */ @Override protected void testElementForExpression(Object element, IExpression expression, final DataRequestMonitor rm) { if (!(element instanceof IDMVMContext)) { @@ -670,6 +722,10 @@ public class RegisterBitFieldVMNode extends AbstractExpressionVMNode } } + /* + * (non-Javadoc) + * @see org.eclipse.dd.dsf.debug.internal.provisional.ui.viewmodel.expression.AbstractExpressionVMNode#associateExpression(java.lang.Object, org.eclipse.debug.core.model.IExpression) + */ @Override protected void associateExpression(Object element, IExpression expression) { if (element instanceof BitFieldVMC) { @@ -677,33 +733,56 @@ public class RegisterBitFieldVMNode extends AbstractExpressionVMNode } } + /* + * (non-Javadoc) + * @see org.eclipse.dd.dsf.debug.internal.provisional.ui.viewmodel.expression.IExpressionVMNode#getDeltaFlagsForExpression(org.eclipse.debug.core.model.IExpression, java.lang.Object) + */ public int getDeltaFlagsForExpression(IExpression expression, Object event) { if (event instanceof IRunControl.ISuspendedDMEvent) { return IModelDelta.CONTENT; } if (event instanceof PropertyChangeEvent && - ((PropertyChangeEvent)event).getProperty() == IDebugVMConstants.CURRENT_FORMAT_STORAGE) - { + ((PropertyChangeEvent)event).getProperty() == IDebugVMConstants.CURRENT_FORMAT_STORAGE) { return IModelDelta.CONTENT; } + if (event instanceof IMemory.IMemoryChangedEvent) { + return IModelDelta.CONTENT; + } + return IModelDelta.NO_CHANGE; } + /* + * (non-Javadoc) + * @see org.eclipse.dd.dsf.debug.internal.provisional.ui.viewmodel.expression.IExpressionVMNode#buildDeltaForExpression(org.eclipse.debug.core.model.IExpression, int, java.lang.Object, org.eclipse.dd.dsf.ui.viewmodel.VMDelta, org.eclipse.jface.viewers.TreePath, org.eclipse.dd.dsf.concurrent.RequestMonitor) + */ public void buildDeltaForExpression(final IExpression expression, final int elementIdx, final Object event, final VMDelta parentDelta, final TreePath path, final RequestMonitor rm) { if (event instanceof ISuspendedDMEvent) { // Mark the parent delta indicating that elements were added and/or removed. parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.CONTENT); - } else if (event instanceof IRegisters.IRegisterChangedDMEvent) { + } + else if (event instanceof IRegisters.IRegisterChangedDMEvent) { parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.CONTENT); } + else if (event instanceof IMemory.IMemoryChangedEvent) { + parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.CONTENT); + } rm.done(); } + /* + * (non-Javadoc) + * @see org.eclipse.dd.dsf.debug.internal.provisional.ui.viewmodel.expression.IExpressionVMNode#buildDeltaForExpressionElement(java.lang.Object, int, java.lang.Object, org.eclipse.dd.dsf.ui.viewmodel.VMDelta, org.eclipse.dd.dsf.concurrent.RequestMonitor) + */ public void buildDeltaForExpressionElement(Object element, int elementIdx, Object event, VMDelta parentDelta, final RequestMonitor rm) { + if (event instanceof IMemory.IMemoryChangedEvent) { + parentDelta.addNode(element, IModelDelta.STATE); + } + if (event instanceof IBitFieldChangedDMEvent) { parentDelta.addNode(element, IModelDelta.STATE); } diff --git a/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/internal/provisional/ui/viewmodel/register/RegisterGroupVMNode.java b/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/internal/provisional/ui/viewmodel/register/RegisterGroupVMNode.java index 52a93c95a77..17c4295ad56 100644 --- a/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/internal/provisional/ui/viewmodel/register/RegisterGroupVMNode.java +++ b/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/internal/provisional/ui/viewmodel/register/RegisterGroupVMNode.java @@ -58,7 +58,6 @@ import org.eclipse.swt.widgets.Composite; public class RegisterGroupVMNode extends AbstractExpressionVMNode implements IElementEditor, IElementLabelProvider { - protected class RegisterGroupVMC extends DMVMContext { private IExpression fExpression; @@ -134,10 +133,21 @@ public class RegisterGroupVMNode extends AbstractExpressionVMNode return fSyncRegisterDataAccess; } + /* + * (non-Javadoc) + * @see org.eclipse.dd.dsf.ui.viewmodel.datamodel.AbstractDMVMNode#updateElementsInSessionThread(org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate) + */ @Override protected void updateElementsInSessionThread(final IChildrenUpdate update) { - if (!checkService(IRegisters.class, null, update)) return; - getServicesTracker().getService(IRegisters.class).getRegisterGroups( + + IRegisters regService = getServicesTracker().getService(IRegisters.class); + + if ( regService == null ) { + handleFailedUpdate(update); + return; + } + + regService.getRegisterGroups( createCompositeDMVMContext(update), new DataRequestMonitor(getSession().getExecutor(), null) { @Override @@ -151,12 +161,19 @@ public class RegisterGroupVMNode extends AbstractExpressionVMNode }}); } + /* + * (non-Javadoc) + * @see org.eclipse.dd.dsf.ui.viewmodel.datamodel.AbstractDMVMNode#createVMContext(org.eclipse.dd.dsf.datamodel.IDMContext) + */ @Override protected IDMVMContext createVMContext(IDMContext dmc) { return new RegisterGroupVMC(dmc); } - + /* + * (non-Javadoc) + * @see org.eclipse.debug.internal.ui.viewers.model.provisional.IElementLabelProvider#update(org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate[]) + */ public void update(final ILabelUpdate[] updates) { try { getSession().getExecutor().execute(new DsfRunnable() { @@ -170,15 +187,28 @@ public class RegisterGroupVMNode extends AbstractExpressionVMNode } } - + /* + * Updates the labels with the required information for each visible column. + */ protected void updateLabelInSessionThread(ILabelUpdate[] updates) { for (final ILabelUpdate update : updates) { + final IRegisterGroupDMContext dmc = findDmcInPath(update.getViewerInput(), update.getElementPath(), IRegisterGroupDMContext.class); - if (!checkDmc(dmc, update) || !checkService(IRegisters.class, null, update)) continue; + if ( dmc == null ) { + handleFailedUpdate(update); + continue; + } + + IRegisters regService = getServicesTracker().getService(IRegisters.class); + if ( regService == null ) { + handleFailedUpdate(update); + continue; + } getDMVMProvider().getModelData( - this, update, - getServicesTracker().getService(IRegisters.class, null), + this, + update, + regService, dmc, new DataRequestMonitor(getSession().getExecutor(), null) { @Override @@ -214,6 +244,9 @@ public class RegisterGroupVMNode extends AbstractExpressionVMNode } } + /* + * Based on the specified visible column, provide the appropriate value/label. + */ protected void fillColumnLabel(IRegisterGroupDMContext dmContext, IRegisterGroupDMData dmData, String columnId, int idx, ILabelUpdate update) { @@ -247,7 +280,11 @@ public class RegisterGroupVMNode extends AbstractExpressionVMNode update.setImageDescriptor(DebugPluginImages.getImageDescriptor(IDebugUIConstants.IMG_OBJS_REGISTER_GROUP), idx); } } - + + /* + * (non-Javadoc) + * @see org.eclipse.dd.dsf.ui.viewmodel.IVMNode#getDeltaFlags(java.lang.Object) + */ public int getDeltaFlags(Object e) { if (e instanceof IRunControl.ISuspendedDMEvent) { return IModelDelta.CONTENT; @@ -261,6 +298,10 @@ public class RegisterGroupVMNode extends AbstractExpressionVMNode return IModelDelta.NO_CHANGE; } + /* + * (non-Javadoc) + * @see org.eclipse.dd.dsf.ui.viewmodel.IVMNode#buildDelta(java.lang.Object, org.eclipse.dd.dsf.ui.viewmodel.VMDelta, int, org.eclipse.dd.dsf.concurrent.RequestMonitor) + */ public void buildDelta(Object e, VMDelta parentDelta, int nodeOffset, RequestMonitor rm) { if (e instanceof IRunControl.ISuspendedDMEvent) { // Create a delta that indicates all groups have changed @@ -277,6 +318,10 @@ public class RegisterGroupVMNode extends AbstractExpressionVMNode rm.done(); } + /* + * (non-Javadoc) + * @see org.eclipse.dd.dsf.debug.internal.provisional.ui.viewmodel.expression.IExpressionVMNode#canParseExpression(org.eclipse.debug.core.model.IExpression) + */ public boolean canParseExpression(IExpression expression) { return parseExpressionForGroupName(expression.getExpressionText()) != null; } @@ -298,6 +343,10 @@ public class RegisterGroupVMNode extends AbstractExpressionVMNode return null; } + /* + * (non-Javadoc) + * @see org.eclipse.dd.dsf.debug.internal.provisional.ui.viewmodel.expression.IExpressionVMNode#getDeltaFlagsForExpression(org.eclipse.debug.core.model.IExpression, java.lang.Object) + */ public int getDeltaFlagsForExpression(IExpression expression, Object event) { if (event instanceof IRunControl.ISuspendedDMEvent) { return IModelDelta.CONTENT; @@ -306,6 +355,10 @@ public class RegisterGroupVMNode extends AbstractExpressionVMNode return IModelDelta.NO_CHANGE; } + /* + * (non-Javadoc) + * @see org.eclipse.dd.dsf.debug.internal.provisional.ui.viewmodel.expression.IExpressionVMNode#buildDeltaForExpression(org.eclipse.debug.core.model.IExpression, int, java.lang.Object, org.eclipse.dd.dsf.ui.viewmodel.VMDelta, org.eclipse.jface.viewers.TreePath, org.eclipse.dd.dsf.concurrent.RequestMonitor) + */ public void buildDeltaForExpression(IExpression expression, int elementIdx, Object event, VMDelta parentDelta, TreePath path, RequestMonitor rm) { @@ -316,6 +369,10 @@ public class RegisterGroupVMNode extends AbstractExpressionVMNode rm.done(); } + /* + * (non-Javadoc) + * @see org.eclipse.dd.dsf.debug.internal.provisional.ui.viewmodel.expression.IExpressionVMNode#buildDeltaForExpressionElement(java.lang.Object, int, java.lang.Object, org.eclipse.dd.dsf.ui.viewmodel.VMDelta, org.eclipse.dd.dsf.concurrent.RequestMonitor) + */ public void buildDeltaForExpressionElement(Object element, int elementIdx, Object event, VMDelta parentDelta, final RequestMonitor rm) { if (event instanceof IRegisters.IGroupsChangedDMEvent) { @@ -327,6 +384,10 @@ public class RegisterGroupVMNode extends AbstractExpressionVMNode rm.done(); } + /* + * (non-Javadoc) + * @see org.eclipse.dd.dsf.debug.internal.provisional.ui.viewmodel.expression.AbstractExpressionVMNode#testElementForExpression(java.lang.Object, org.eclipse.debug.core.model.IExpression, org.eclipse.dd.dsf.concurrent.DataRequestMonitor) + */ @Override protected void testElementForExpression(Object element, IExpression expression, final DataRequestMonitor rm) { if (!(element instanceof IDMVMContext)) { @@ -368,6 +429,10 @@ public class RegisterGroupVMNode extends AbstractExpressionVMNode } } + /* + * (non-Javadoc) + * @see org.eclipse.dd.dsf.debug.internal.provisional.ui.viewmodel.expression.AbstractExpressionVMNode#associateExpression(java.lang.Object, org.eclipse.debug.core.model.IExpression) + */ @Override protected void associateExpression(Object element, IExpression expression) { if (element instanceof RegisterGroupVMC) { @@ -375,15 +440,22 @@ public class RegisterGroupVMNode extends AbstractExpressionVMNode } } + /* + * (non-Javadoc) + * @see org.eclipse.debug.internal.ui.viewers.model.provisional.IElementEditor#getCellEditor(org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext, java.lang.String, java.lang.Object, org.eclipse.swt.widgets.Composite) + */ public CellEditor getCellEditor(IPresentationContext context, String columnId, Object element, Composite parent) { if (IDebugVMConstants.COLUMN_ID__EXPRESSION.equals(columnId)) { return new TextCellEditor(parent); } return null; } - + + /* + * (non-Javadoc) + * @see org.eclipse.debug.internal.ui.viewers.model.provisional.IElementEditor#getCellModifier(org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext, java.lang.Object) + */ public ICellModifier getCellModifier(IPresentationContext context, Object element) { return fWatchExpressionCellModifier; } - -} +} \ No newline at end of file diff --git a/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/internal/provisional/ui/viewmodel/register/RegisterVMNode.java b/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/internal/provisional/ui/viewmodel/register/RegisterVMNode.java index f58ff9a74db..89f8bef2937 100644 --- a/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/internal/provisional/ui/viewmodel/register/RegisterVMNode.java +++ b/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/internal/provisional/ui/viewmodel/register/RegisterVMNode.java @@ -28,6 +28,7 @@ import org.eclipse.dd.dsf.debug.internal.provisional.ui.viewmodel.numberformat.I import org.eclipse.dd.dsf.debug.internal.provisional.ui.viewmodel.numberformat.IFormattedValueVMContext; import org.eclipse.dd.dsf.debug.internal.ui.DsfDebugUIPlugin; import org.eclipse.dd.dsf.debug.service.IFormattedValues; +import org.eclipse.dd.dsf.debug.service.IMemory; import org.eclipse.dd.dsf.debug.service.IRegisters; import org.eclipse.dd.dsf.debug.service.IRunControl; import org.eclipse.dd.dsf.debug.service.IFormattedValues.FormattedValueDMContext; @@ -159,9 +160,13 @@ public class RegisterVMNode extends AbstractExpressionVMNode */ private void updateFormattedRegisterValue(final ILabelUpdate update, final int labelIndex, final IRegisterDMContext dmc) { - if (!checkService(IRegisters.class, null, update)) return; - final IRegisters regService = getServicesTracker().getService(IRegisters.class); + + if ( regService == null ) { + handleFailedUpdate(update); + return; + } + /* * First select the format to be used. This involves checking so see that the preference * page format is supported by the register service. If the format is not supported then @@ -259,7 +264,11 @@ public class RegisterVMNode extends AbstractExpressionVMNode } ); } - + + /* + * (non-Javadoc) + * @see org.eclipse.debug.internal.ui.viewers.model.provisional.IElementLabelProvider#update(org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate[]) + */ public void update(final ILabelUpdate[] updates) { try { @@ -274,15 +283,29 @@ public class RegisterVMNode extends AbstractExpressionVMNode } } - + /* + * Updates the labels which are controlled by the column being requested. + */ protected void updateLabelInSessionThread(ILabelUpdate[] updates) { for (final ILabelUpdate update : updates) { + final IRegisterDMContext dmc = findDmcInPath(update.getViewerInput(), update.getElementPath(), IRegisters.IRegisterDMContext.class); - if (!checkDmc(dmc, update) || !checkService(IRegisters.class, null, update)) continue; + if ( dmc == null ) { + handleFailedUpdate(update); + continue; + } + + IRegisters regService = getServicesTracker().getService(IRegisters.class); + if ( regService == null ) { + handleFailedUpdate(update); + continue; + } + getDMVMProvider().getModelData( - this, update, - getServicesTracker().getService(IRegisters.class), + this, + update, + regService, dmc, new DataRequestMonitor(getSession().getExecutor(), null) { @Override @@ -409,10 +432,21 @@ public class RegisterVMNode extends AbstractExpressionVMNode } } + /* + * (non-Javadoc) + * @see org.eclipse.dd.dsf.ui.viewmodel.datamodel.AbstractDMVMNode#updateElementsInSessionThread(org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate) + */ @Override protected void updateElementsInSessionThread(final IChildrenUpdate update) { - if (!checkService(IRegisters.class, null, update)) return; - getServicesTracker().getService(IRegisters.class).getRegisters( + + IRegisters regService = getServicesTracker().getService(IRegisters.class); + + if ( regService == null ) { + handleFailedUpdate(update); + return; + } + + regService.getRegisters( createCompositeDMVMContext(update), new DataRequestMonitor(getSession().getExecutor(), null) { @Override @@ -427,21 +461,20 @@ public class RegisterVMNode extends AbstractExpressionVMNode }); } + /* + * (non-Javadoc) + * @see org.eclipse.dd.dsf.ui.viewmodel.datamodel.AbstractDMVMNode#createVMContext(org.eclipse.dd.dsf.datamodel.IDMContext) + */ @Override protected IDMVMContext createVMContext(IDMContext dmc) { return new RegisterVMC(dmc); } + /* + * (non-Javadoc) + * @see org.eclipse.dd.dsf.ui.viewmodel.IVMNode#getDeltaFlags(java.lang.Object) + */ public int getDeltaFlags(Object e) { - /* In theory we want each node to act independently in terms of events. It might be - * the case that we would only have elements of this type at the root level. It is - * the case that the current layout model always starts with the GROUPS followed by - * REGISTERS followed by BITFIELDS. But if we do this when a run-control event has - * occured we generate a DELTA for every element, which can create a massive list - * of entries all of which say update the entire view. So for now we will just have - * the GROUP LAYOUT node do this. Later we need to revisit the logic and make sure - * there is a way for the nodes to operate independently and efficiently. - */ if (e instanceof IRunControl.ISuspendedDMEvent) { return IModelDelta.CONTENT; } @@ -450,10 +483,14 @@ public class RegisterVMNode extends AbstractExpressionVMNode return IModelDelta.CONTENT; } + if (e instanceof IMemory.IMemoryChangedEvent) { + return IModelDelta.CONTENT; + } + if (e instanceof IRegisters.IRegisterChangedDMEvent) { /* - * Logically one would think that STATE should be specified here. But we specifiy CONTENT - * as well so that if there are subregisters ( BIT FIELDS ) they will be forced to update + * Logically one would think that STATE should be specified here. But we specify CONTENT + * as well so that if there are sub-registers ( BIT FIELDS ) they will be forced to update * and show new values when the total register changes. */ return IModelDelta.CONTENT; @@ -467,6 +504,10 @@ public class RegisterVMNode extends AbstractExpressionVMNode return IModelDelta.NO_CHANGE; } + /* + * (non-Javadoc) + * @see org.eclipse.dd.dsf.ui.viewmodel.IVMNode#buildDelta(java.lang.Object, org.eclipse.dd.dsf.ui.viewmodel.VMDelta, int, org.eclipse.dd.dsf.concurrent.RequestMonitor) + */ public void buildDelta(Object e, VMDelta parentDelta, int nodeOffset, RequestMonitor rm) { if (e instanceof IRunControl.ISuspendedDMEvent) { // Create a delta that the whole register group has changed. @@ -477,6 +518,11 @@ public class RegisterVMNode extends AbstractExpressionVMNode parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.CONTENT);; } + if (e instanceof IMemory.IMemoryChangedEvent) { + // Mark the parent delta indicating that elements were added and/or removed. + parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.CONTENT); + } + if (e instanceof IRegisters.IRegisterChangedDMEvent) { parentDelta.addNode( createVMContext(((IRegisterChangedDMEvent)e).getDMContext()), IModelDelta.CONTENT | IModelDelta.STATE ); } @@ -539,6 +585,11 @@ public class RegisterVMNode extends AbstractExpressionVMNode return null; } + + /* + * (non-Javadoc) + * @see org.eclipse.dd.dsf.debug.internal.provisional.ui.viewmodel.expression.AbstractExpressionVMNode#testElementForExpression(java.lang.Object, org.eclipse.debug.core.model.IExpression, org.eclipse.dd.dsf.concurrent.DataRequestMonitor) + */ @Override protected void testElementForExpression(Object element, IExpression expression, final DataRequestMonitor rm) { if (!(element instanceof IDMVMContext)) { @@ -580,6 +631,10 @@ public class RegisterVMNode extends AbstractExpressionVMNode } } + /* + * (non-Javadoc) + * @see org.eclipse.dd.dsf.debug.internal.provisional.ui.viewmodel.expression.AbstractExpressionVMNode#associateExpression(java.lang.Object, org.eclipse.debug.core.model.IExpression) + */ @Override protected void associateExpression(Object element, IExpression expression) { if (element instanceof RegisterVMC) { @@ -587,6 +642,10 @@ public class RegisterVMNode extends AbstractExpressionVMNode } } + /* + * (non-Javadoc) + * @see org.eclipse.dd.dsf.debug.internal.provisional.ui.viewmodel.expression.IExpressionVMNode#getDeltaFlagsForExpression(org.eclipse.debug.core.model.IExpression, java.lang.Object) + */ public int getDeltaFlagsForExpression(IExpression expression, Object event) { if (event instanceof IRunControl.ISuspendedDMEvent) { return IModelDelta.CONTENT; @@ -597,9 +656,17 @@ public class RegisterVMNode extends AbstractExpressionVMNode return IModelDelta.CONTENT; } + if (event instanceof IMemory.IMemoryChangedEvent) { + return IModelDelta.CONTENT; + } + return IModelDelta.NO_CHANGE; } + /* + * (non-Javadoc) + * @see org.eclipse.dd.dsf.debug.internal.provisional.ui.viewmodel.expression.IExpressionVMNode#buildDeltaForExpression(org.eclipse.debug.core.model.IExpression, int, java.lang.Object, org.eclipse.dd.dsf.ui.viewmodel.VMDelta, org.eclipse.jface.viewers.TreePath, org.eclipse.dd.dsf.concurrent.RequestMonitor) + */ public void buildDeltaForExpression(IExpression expression, int elementIdx, Object event, VMDelta parentDelta, TreePath path, RequestMonitor rm) { @@ -607,9 +674,18 @@ public class RegisterVMNode extends AbstractExpressionVMNode // Mark the parent delta indicating that elements were added and/or removed. parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.CONTENT); } + + if (event instanceof IMemory.IMemoryChangedEvent) { + parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.CONTENT); + } + rm.done(); } + /* + * (non-Javadoc) + * @see org.eclipse.dd.dsf.debug.internal.provisional.ui.viewmodel.expression.IExpressionVMNode#buildDeltaForExpressionElement(java.lang.Object, int, java.lang.Object, org.eclipse.dd.dsf.ui.viewmodel.VMDelta, org.eclipse.dd.dsf.concurrent.RequestMonitor) + */ public void buildDeltaForExpressionElement(Object element, int elementIdx, Object event, VMDelta parentDelta, final RequestMonitor rm) { if (event instanceof IRegisters.IRegisterChangedDMEvent) { @@ -620,6 +696,10 @@ public class RegisterVMNode extends AbstractExpressionVMNode parentDelta.addNode(element, IModelDelta.STATE); } + if (event instanceof IMemory.IMemoryChangedEvent) { + parentDelta.addNode(element, IModelDelta.CONTENT); + } + if (event instanceof PropertyChangeEvent && ((PropertyChangeEvent)event).getProperty() == IDebugVMConstants.CURRENT_FORMAT_STORAGE) { @@ -629,7 +709,10 @@ public class RegisterVMNode extends AbstractExpressionVMNode rm.done(); } - + /* + * (non-Javadoc) + * @see org.eclipse.debug.internal.ui.viewers.model.provisional.IElementEditor#getCellEditor(org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext, java.lang.String, java.lang.Object, org.eclipse.swt.widgets.Composite) + */ public CellEditor getCellEditor(IPresentationContext context, String columnId, Object element, Composite parent) { if (IDebugVMConstants.COLUMN_ID__EXPRESSION.equals(columnId)) { return new TextCellEditor(parent); @@ -648,6 +731,10 @@ public class RegisterVMNode extends AbstractExpressionVMNode return null; } + /* + * (non-Javadoc) + * @see org.eclipse.debug.internal.ui.viewers.model.provisional.IElementEditor#getCellModifier(org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext, java.lang.Object) + */ public ICellModifier getCellModifier(IPresentationContext context, Object element) { return new RegisterCellModifier( getDMVMProvider(), fFormattedPrefStore, fSyncRegisterDataAccess ); diff --git a/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/internal/provisional/ui/viewmodel/variable/VariableVMNode.java b/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/internal/provisional/ui/viewmodel/variable/VariableVMNode.java index 924aa52b57e..8c72b68971e 100644 --- a/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/internal/provisional/ui/viewmodel/variable/VariableVMNode.java +++ b/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/internal/provisional/ui/viewmodel/variable/VariableVMNode.java @@ -68,10 +68,10 @@ import org.eclipse.swt.widgets.Composite; @SuppressWarnings({"restriction", "nls"}) public class VariableVMNode extends AbstractExpressionVMNode - implements IElementEditor, IElementLabelProvider + implements IElementEditor, IElementLabelProvider { - private final static int MAX_STRING_VALUE_LENGTH = 40; + //private final static int MAX_STRING_VALUE_LENGTH = 40; public int getDeltaFlags(Object e) { /* @@ -408,11 +408,9 @@ public class VariableVMNode extends AbstractExpressionVMNode update.setFontData(JFaceResources.getFontDescriptor(IInternalDebugUIConstants.VARIABLE_TEXT_FONT).getFontData()[0], labelIndex); // Color based on change history - FormattedValueDMData oldData = (FormattedValueDMData) getDMVMProvider().getArchivedModelData( - VariableVMNode.this, update, valueDmc); + FormattedValueDMData oldData = (FormattedValueDMData) getDMVMProvider().getArchivedModelData(VariableVMNode.this, update, valueDmc); - IExpressionDMData oldDMData = (IExpressionDMData) getDMVMProvider().getArchivedModelData( - VariableVMNode.this, update, dmc); + IExpressionDMData oldDMData = (IExpressionDMData) getDMVMProvider().getArchivedModelData(VariableVMNode.this, update, dmc); /* Commented out, to be replaced. See bug 225612. String oldStringValue = oldDMData == null ? null : oldDMData.getStringValue();*/ @@ -421,9 +419,7 @@ public class VariableVMNode extends AbstractExpressionVMNode /* Commented out, to be replaced. See bug 225612. || (oldStringValue != null && !oldStringValue.equals(stringValue))) {*/ update.setBackground( - DebugUIPlugin.getPreferenceColor( - IInternalDebugUIConstants.PREF_CHANGED_VALUE_BACKGROUND).getRGB(), - labelIndex); + DebugUIPlugin.getPreferenceColor(IInternalDebugUIConstants.PREF_CHANGED_VALUE_BACKGROUND).getRGB(), labelIndex); } update.done(); diff --git a/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/datamodel/AbstractDMVMNode.java b/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/datamodel/AbstractDMVMNode.java index 1548288d6e7..7d6c58dc9ac 100644 --- a/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/datamodel/AbstractDMVMNode.java +++ b/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/datamodel/AbstractDMVMNode.java @@ -182,8 +182,25 @@ abstract public class AbstractDMVMNode extends AbstractVMNode implements IVMNode * appropriate error message is set in the update. * @param dmc Data Model Context (DMC) to check. * @param update Update to handle in case the DMC is null. - * @return true if the DMC is NOT null, indicating that it's OK to proceed. + * @return true if the DMC is NOT null, indicating that it's OK to proceed. + * + * This method has been deprecated. Users should simply perform this functionality in-line + * + * Example : + * + * IExampleDmc dmc = final TimerDMContext dmc = findDmcInPath(...) + * if ( dmc == null ) { + * handleFailedUpdate(update); + * // + * // Perform whatever cleanup or completion is needed because of a lack of + * // a valid data model context. + * // + * ........ + * return; + * } */ + + @Deprecated protected boolean checkDmc(IDMContext dmc, IViewerUpdate update) { if (dmc == null) { update.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_STATE, @@ -200,7 +217,21 @@ abstract public class AbstractDMVMNode extends AbstractVMNode implements IVMNode * @param serviceClass Service class to find. * @param filter Service filter to use in addition to the service class name. * @param update Update object to fill in. - * @return true if service IS found, indicating that it's OK to proceed. + * @return true if service IS found, indicating that it's OK to proceed. + * + * This method has been deprecated. Users should simply perform this functionality in-line + * + * Example : + * + * IExampleService service = getServicesTracker().getService(IExampleService.class,null); + * if ( service == null ) { + * handleFailedUpdate(update); + * // + * // Perform whatever cleanup or completion is needed because of a lack of the service. + * // + * ........ + * return; + * } */ @Deprecated protected boolean checkService(Class serviceClass, String filter, IViewerUpdate update) { diff --git a/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/dataviewer/AsyncDataViewer.java b/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/dataviewer/AsyncDataViewer.java new file mode 100644 index 00000000000..54f2caad63a --- /dev/null +++ b/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/dataviewer/AsyncDataViewer.java @@ -0,0 +1,272 @@ +/******************************************************************************* + * Copyright (c) 2006, 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.dd.examples.dsf.dataviewer; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import org.eclipse.dd.dsf.concurrent.ConfinedToDsfExecutor; +import org.eclipse.dd.dsf.concurrent.ThreadSafe; +import org.eclipse.dd.dsf.concurrent.DataRequestMonitor; +import org.eclipse.dd.dsf.concurrent.DsfExecutor; +import org.eclipse.dd.dsf.concurrent.ImmediateExecutor; +import org.eclipse.dd.dsf.concurrent.Query; +import org.eclipse.dd.dsf.ui.concurrent.DisplayDsfExecutor; +import org.eclipse.jface.viewers.ILazyContentProvider; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Table; + +/** + * Data viewer based on a table, which reads data using asynchronous methods. + *

+ * This viewer implements the {@link ILazyContentProvider} interface + * which is used by the JFace TableViewer class to populate a Table. This + * interface contains separate asynchronous methods for requesting the count + * and values for individual indexes, which neatly correspond to the methods + * in {@link IDataGenerator}. As an added optimization, this viewer + * implementation checks for the range of visible items in the view upon each + * request, and it cancels old requests which scroll out of view but have not + * been completed yet. However, it is up to the data generator implementation + * to check the canceled state of the requests and ignore them. + *

+ */ +@ConfinedToDsfExecutor("fDisplayExecutor") +public class AsyncDataViewer + implements ILazyContentProvider, IDataGenerator.Listener +{ + // Executor to use instead of Display.asyncExec(). + @ThreadSafe + final private DsfExecutor fDisplayExecutor; + + // The viewer and generator that this content provider using. + final private TableViewer fViewer; + final private IDataGenerator fDataGenerator; + + // Fields used in request cancellation logic. + private List fItemDataRequestMonitors = new LinkedList(); + private Set fIndexesToCancel = new HashSet(); + private int fCancelCallsPending = 0; + + public AsyncDataViewer(TableViewer viewer, IDataGenerator generator) { + fViewer = viewer; + fDisplayExecutor = DisplayDsfExecutor.getDisplayDsfExecutor(fViewer.getTable().getDisplay()); + fDataGenerator = generator; + fDataGenerator.addListener(this); + } + + public void dispose() { + fDataGenerator.removeListener(this); + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + // Set the initial count to the viewer after the input is set. + queryItemCount(); + } + + public void updateElement(final int index) { + // Calculate the visible index range. + final int topIdx = fViewer.getTable().getTopIndex(); + final int botIdx = topIdx + getVisibleItemCount(topIdx); + + // Request the item for the given index. + queryValue(index); + + // Invoke a cancel task with a delay. The delay allows multiple cancel + // calls to be combined together improving performance of the viewer. + fCancelCallsPending++; + fDisplayExecutor.schedule( + new Runnable() { public void run() { + cancelStaleRequests(topIdx, botIdx); + }}, + 1, TimeUnit.MILLISECONDS); + } + + private int getVisibleItemCount(int top) { + Table table = fViewer.getTable(); + int itemCount = table.getItemCount(); + return Math.min((table.getBounds().height / table.getItemHeight()) + 2, itemCount - top); + } + + @ThreadSafe + public void countChanged() { + queryItemCount(); + } + + @ThreadSafe + public void valuesChanged(final Set indexes) { + // Mark the changed items in table viewer as dirty, this will + // trigger update requests for these indexes if they are + // visible in the viewer. + final TableViewer tableViewer = fViewer; + fDisplayExecutor.execute( new Runnable() { + public void run() { + if (!fViewer.getTable().isDisposed()) { + for (Integer index : indexes) { + tableViewer.clear(index); + } + } + }}); + } + + + private void queryItemCount() { + // Request count from data provider. When the count is returned, we + // have to re-dispatch into the display thread to avoid calling + // the table widget on the DSF dispatch thread. + fIndexesToCancel.clear(); + fDataGenerator.getCount( + // Use the display executor to construct the request monitor, this + // will cause the handleCompleted() method to be automatically + // called on the display thread. + new DataRequestMonitor(fDisplayExecutor, null) { + @Override + protected void handleCompleted() { + if (!fViewer.getTable().isDisposed()) { + fViewer.setItemCount(getData()); + fViewer.getTable().clearAll(); + } + } + }); + + } + + + // Dedicated class for data item requests. This class holds the index + // argument so it can be examined when canceling stale requests. + private class ValueDataRequestMonitor extends DataRequestMonitor { + + /** Index is used when canceling stale requests. */ + int fIndex; + + ValueDataRequestMonitor(int index) { + super(fDisplayExecutor, null); + fIndex = index; + } + + @Override + protected void handleCompleted() { + fItemDataRequestMonitors.remove(this); + + // Check if the request completed successfully, otherwise ignore it. + if (isSuccess()) { + if (!fViewer.getTable().isDisposed()) { + fViewer.replace(getData(), fIndex); + } + } + } + } + + private void queryValue(final int index) { + ValueDataRequestMonitor rm = new ValueDataRequestMonitor(index); + fItemDataRequestMonitors.add(rm); + fDataGenerator.getValue(index, rm); + } + + private void cancelStaleRequests(int topIdx, int botIdx) { + // Decrement the count of outstanding cancel calls. + fCancelCallsPending--; + + // Must check again, in case disposed while re-dispatching. + if (fDataGenerator == null || fViewer.getTable().isDisposed()) return; + + // Go through the outstanding requests and cancel any that + // are not visible anymore. + for (Iterator itr = fItemDataRequestMonitors.iterator(); itr.hasNext();) { + ValueDataRequestMonitor item = itr.next(); + if (item.fIndex < topIdx || item.fIndex > botIdx) { + // Set the item to canceled status, so that the data provider + // will ignore it. + item.cancel(); + + // Add the item index to list of indexes that were canceled, + // which will be sent to the table widget. + fIndexesToCancel.add(item.fIndex); + + // Remove the item from the outstanding cancel requests. + itr.remove(); + } + } + if (!fIndexesToCancel.isEmpty() && fCancelCallsPending == 0) { + Set canceledIdxs = fIndexesToCancel; + fIndexesToCancel = new HashSet(); + + // Clear the indexes of the canceled request, so that the + // viewer knows to request them again when needed. + // Note: clearing using TableViewer.clear(int) seems very + // inefficient, it's better to use Table.clear(int[]). + int[] canceledIdxsArray = new int[canceledIdxs.size()]; + int i = 0; + for (Integer index : canceledIdxs) { + canceledIdxsArray[i++] = index; + } + fViewer.getTable().clear(canceledIdxsArray); + } + } + + + public static void main(String[] args) { + // Create the shell to hold the viewer. + Display display = new Display(); + Shell shell = new Shell(display, SWT.SHELL_TRIM); + shell.setLayout(new GridLayout()); + GridData data = new GridData(GridData.FILL_BOTH); + shell.setLayoutData(data); + Font font = new Font(display, "Courier", 10, SWT.NORMAL); + + // Create the table viewer. + TableViewer tableViewer = new TableViewer(shell, SWT.BORDER | SWT.VIRTUAL); + tableViewer.getControl().setLayoutData(data); + + // Create the data generator. + final IDataGenerator generator = new DataGeneratorWithExecutor(); + + // Create the content provider which will populate the viewer. + AsyncDataViewer contentProvider = new AsyncDataViewer(tableViewer, generator); + tableViewer.setContentProvider(contentProvider); + tableViewer.setInput(new Object()); + + // Open the shell and service the display dispatch loop until user + // closes the shell. + shell.open(); + while (!shell.isDisposed()) { + if (!display.readAndDispatch()) + display.sleep(); + } + + // The IDataGenerator.shutdown() method is asynchronous, this requires + // using a query again in order to wait for its completion. + Query shutdownQuery = new Query() { + @Override + protected void execute(DataRequestMonitor rm) { + generator.shutdown(rm); + } + }; + ImmediateExecutor.getInstance().execute(shutdownQuery); + try { + shutdownQuery.get(); + } catch (Exception e) {} + + // Shut down the display. + font.dispose(); + display.dispose(); + } +} diff --git a/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/dataviewer/DataGeneratorWithExecutor.java b/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/dataviewer/DataGeneratorWithExecutor.java new file mode 100644 index 00000000000..1b742b3fc62 --- /dev/null +++ b/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/dataviewer/DataGeneratorWithExecutor.java @@ -0,0 +1,336 @@ +/******************************************************************************* + * Copyright (c) 2006 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.dd.examples.dsf.dataviewer; + +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Random; +import java.util.Set; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.TimeUnit; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.dd.dsf.concurrent.DataRequestMonitor; +import org.eclipse.dd.dsf.concurrent.DefaultDsfExecutor; +import org.eclipse.dd.dsf.concurrent.DsfExecutor; +import org.eclipse.dd.dsf.concurrent.DsfRunnable; +import org.eclipse.dd.dsf.concurrent.RequestMonitor; +import org.eclipse.dd.examples.dsf.DsfExamplesPlugin; + +/** + * DSF Executor-based implementation of the data generator. + *

+ * This generator uses a queue of client requests and processes these + * requests periodically using a DSF executor. The main feature of this + * generator is that it uses the executor as its only synchronization object. + * This means that all the fields with the exception of the executor can only + * be accessed while running in the executor thread. + *

+ */ +//TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) +//indicating allowed thread access to this class/method/member +public class DataGeneratorWithExecutor implements IDataGenerator { + + // Request objects are used to serialize the interface calls into objects + // which can then be pushed into a queue. + // TODO Ecercise 4 - Add an annotationindicating allowed concurrency access + // Hint: Request and its subclasses have all their fields declared as final. + abstract class Request { + final RequestMonitor fRequestMonitor; + + Request(RequestMonitor rm) { + fRequestMonitor = rm; + } + } + + // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) + // indicating allowed thread access to this class/method/member + class CountRequest extends Request { + CountRequest(DataRequestMonitor rm) { + super(rm); + } + } + + // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) + // indicating allowed thread access to this class/method/member + class ItemRequest extends Request { + final int fIndex; + ItemRequest(int index, DataRequestMonitor rm) { + super(rm); + fIndex = index; + } + } + + // The executor used to access all internal data of the generator. + // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) + // indicating allowed thread access to this class/method/member + // Hint: If a member does not have an annotation, the programmer can assume + // that the concurrency rule that applies to the class also applies to this + // member. + private DsfExecutor fExecutor; + + // Main request queue of the data generator. The getValue(), getCount(), + // and shutdown() methods write into the queue, while the serviceQueue() + // method reads from it. + // The executor used to access all internal data of the generator. + // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) + // indicating allowed thread access to this class/method/member + private List fQueue = new LinkedList(); + + // List of listeners is not synchronized, it also has to be accessed + // using the executor. + // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) + // indicating allowed thread access to this class/method/member + private List fListeners = new LinkedList(); + + // Current number of elements in this generator. + // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) + // indicating allowed thread access to this class/method/member + private int fCount = MIN_COUNT; + + // Counter used to determine when to reset the element count. + // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) + // indicating allowed thread access to this class/method/member + private int fCountResetTrigger = 0; + + // Elements which were modified since the last reset. + // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) + // indicating allowed thread access to this class/method/member + private Set fChangedIndexes = new HashSet(); + + // Flag used to ensure that requests are processed sequentially. + // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) + // indicating allowed thread access to this class/method/member + private boolean fServiceQueueInProgress = false; + + // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) + // indicating allowed thread access to this class/method/member + public DataGeneratorWithExecutor() { + // Create the executor + fExecutor = new DefaultDsfExecutor("Supplier Executor"); + + // Schedule a runnable to make the random changes. + fExecutor.scheduleAtFixedRate( + new DsfRunnable() { + public void run() { + randomChanges(); + } + }, + RANDOM_CHANGE_INTERVAL, + RANDOM_CHANGE_INTERVAL, + TimeUnit.MILLISECONDS); + } + + // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) + // indicating allowed thread access to this class/method/member + public void shutdown(final RequestMonitor rm) { + try { + fExecutor.execute( new DsfRunnable() { + public void run() { + // Empty the queue of requests and fail them. + for (Request request : fQueue) { + request.fRequestMonitor.setStatus( + new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, "Supplier shut down")); + request.fRequestMonitor.done(); + } + fQueue.clear(); + + // Kill executor. + fExecutor.shutdown(); + rm.done(); + } + }); + } catch (RejectedExecutionException e) { + rm.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, "Supplier shut down")); + rm.done(); + } + } + + // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) + // indicating allowed thread access to this class/method/member + public void getCount(final DataRequestMonitor rm) { + try { + fExecutor.execute( new DsfRunnable() { + public void run() { + fQueue.add(new CountRequest(rm)); + serviceQueue(); + } + }); + } catch (RejectedExecutionException e) { + rm.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, "Supplier shut down")); + rm.done(); + } + } + + // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) + // indicating allowed thread access to this class/method/member + public void getValue(final int index, final DataRequestMonitor rm) { + try { + fExecutor.execute( new DsfRunnable() { + public void run() { + fQueue.add(new ItemRequest(index, rm)); + serviceQueue(); + } + }); + } catch (RejectedExecutionException e) { + rm.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, "Supplier shut down")); + rm.done(); + } + } + + // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) + // indicating allowed thread access to this class/method/member + public void addListener(final Listener listener) { + try { + fExecutor.execute( new DsfRunnable() { + public void run() { + fListeners.add(listener); + } + }); + } catch (RejectedExecutionException e) {} + } + + // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) + // indicating allowed thread access to this class/method/member + public void removeListener(final Listener listener) { + try { + fExecutor.execute( new DsfRunnable() { + public void run() { + fListeners.remove(listener); + } + }); + } catch (RejectedExecutionException e) {} + } + + // Main processing function of this generator. + // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) + // indicating allowed thread access to this class/method/member + private void serviceQueue() { + + // TODO Exercise 3 - Add logic to discard cancelled requests from queue. + // Hint: Since serviceQueue() is called using the executor, and the + // fQueue list can only be modified when running in the executor + // thread. This method can safely iterate and modify fQueue without + // risk of race conditions or concurrent modification exceptions. + + // If a queue servicing is already scheduled, do nothing. + if (fServiceQueueInProgress) { + return; + } + + if (fQueue.size() != 0) { + // If there are requests to service, remove one from the queue and + // schedule a runnable to process the request after a processing + // delay. + fServiceQueueInProgress = true; + final Request request = fQueue.remove(0); + fExecutor.schedule( + new DsfRunnable() { + public void run() { + if (request instanceof CountRequest) { + processCountRequest((CountRequest)request); + } else if (request instanceof ItemRequest) { + processItemRequest((ItemRequest)request); + } + + // Reset the processing flag and process next + // request. + fServiceQueueInProgress = false; + serviceQueue(); + } + }, + PROCESSING_DELAY, TimeUnit.MILLISECONDS); + } + } + + // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) + // indicating allowed thread access to this class/method/member + private void processCountRequest(CountRequest request) { + @SuppressWarnings("unchecked") // Suppress warning about lost type info. + DataRequestMonitor rm = (DataRequestMonitor)request.fRequestMonitor; + + rm.setData(fCount); + rm.done(); + } + + // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) + // indicating allowed thread access to this class/method/member + private void processItemRequest(ItemRequest request) { + @SuppressWarnings("unchecked") // Suppress warning about lost type info. + DataRequestMonitor rm = (DataRequestMonitor)request.fRequestMonitor; + + if (fChangedIndexes.contains(request.fIndex)) { + rm.setData("Changed: " + request.fIndex); + } else { + rm.setData(Integer.toString(request.fIndex)); + } + rm.done(); + } + + /** + * This method simulates changes in the supplier's data set. + */ + // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) + // indicating allowed thread access to this class/method/member + private void randomChanges() { + // Once every number of changes, reset the count, the rest of the + // times just change certain values. + if (++fCountResetTrigger % RANDOM_COUNT_CHANGE_INTERVALS == 0){ + randomCountReset(); + } else { + randomDataChange(); + } + } + + /** + * Calculates new size for provider's data set. + */ + // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) + // indicating allowed thread access to this class/method/member + private void randomCountReset() { + // Calculate the new count. + Random random = new java.util.Random(); + fCount = MIN_COUNT + Math.abs(random.nextInt()) % (MAX_COUNT - MIN_COUNT); + + // Reset the changed values. + fChangedIndexes.clear(); + + // Notify listeners + for (Listener listener : fListeners) { + listener.countChanged(); + } + } + + /** + * Invalidates a random range of indexes. + */ + // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) + // indicating allowed thread access to this class/method/member + private void randomDataChange() { + // Calculate the indexes to change. + Random random = new java.util.Random(); + Set set = new HashSet(); + for (int i = 0; i < fCount * RANDOM_CHANGE_SET_PERCENTAGE / 100; i++) { + set.add( new Integer(Math.abs(random.nextInt()) % fCount) ); + } + + // Add the indexes to an overall set of changed indexes. + fChangedIndexes.addAll(set); + + // Notify listeners + for (Listener listener : fListeners) { + listener.valuesChanged(set); + } + } +} + diff --git a/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/dataviewer/DataGeneratorWithThread.java b/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/dataviewer/DataGeneratorWithThread.java new file mode 100644 index 00000000000..04fab7aaf2e --- /dev/null +++ b/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/dataviewer/DataGeneratorWithThread.java @@ -0,0 +1,238 @@ +/******************************************************************************* + * Copyright (c) 2006, 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.dd.examples.dsf.dataviewer; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Random; +import java.util.Set; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.ListenerList; +import org.eclipse.core.runtime.Status; +import org.eclipse.dd.dsf.concurrent.DataRequestMonitor; +import org.eclipse.dd.dsf.concurrent.RequestMonitor; +import org.eclipse.dd.examples.dsf.DsfExamplesPlugin; + +/** + * Thread-based implementation of the data generator. + *

+ * This generator is based around a queue of client requests and a thread which + * reads the requests from the queue and processes them. The distinguishing + * feature of this generator is that it uses a a blocking queue as the main + * synchronization object. However, fListeners, fShutdown, and fChangedIndexes + * fields also need to be thread-safe and so they implement their own + * synchronization. + *

+ */ +public class DataGeneratorWithThread extends Thread implements IDataGenerator { + + // Request objects are used to serialize the interface calls into objects + // which can then be pushed into a queue. + abstract class Request { + final RequestMonitor fRequestMonitor; + + Request(RequestMonitor rm) { + fRequestMonitor = rm; + } + } + + class CountRequest extends Request { + CountRequest(DataRequestMonitor rm) { + super(rm); + } + } + + class ItemRequest extends Request { + final int fIndex; + ItemRequest(int index, DataRequestMonitor rm) { + super(rm); + fIndex = index; + } + } + + class ShutdownRequest extends Request { + ShutdownRequest(RequestMonitor rm) { + super(rm); + } + } + + // Main request queue of the data generator. The getValue(), getCount(), + // and shutdown() methods write into the queue, while the run() method + // reads from it. + private final BlockingQueue fQueue = new LinkedBlockingQueue(); + + // ListenerList class provides thread safety. + private ListenerList fListeners = new ListenerList(); + + // Current number of elements in this generator. + private int fCount = MIN_COUNT; + + // Counter used to determine when to reset the element count. + private int fCountResetTrigger = 0; + + // Elements which were modified since the last reset. + private Set fChangedIndexes = Collections.synchronizedSet(new HashSet()); + + // Used to determine when to make changes in data. + private long fLastChangeTime = System.currentTimeMillis(); + + // Flag indicating when the generator has been shut down. + private AtomicBoolean fShutdown = new AtomicBoolean(false); + + public DataGeneratorWithThread() { + // Immediately kick off the request processing thread. + start(); + } + + public void shutdown(RequestMonitor rm) { + // Mark the generator as shut down. After the fShutdown flag is set, + // all new requests should be shut down. + if (!fShutdown.getAndSet(true)) { + fQueue.add(new ShutdownRequest(rm)); + } else { + // + rm.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, "Supplier shut down")); + rm.done(); + } + } + + public void getCount(DataRequestMonitor rm) { + if (!fShutdown.get()) { + fQueue.add(new CountRequest(rm)); + } else { + rm.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, "Supplier shut down")); + rm.done(); + } + } + + public void getValue(int index, DataRequestMonitor rm) { + if (!fShutdown.get()) { + fQueue.add(new ItemRequest(index, rm)); + } else { + rm.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, "Supplier shut down")); + rm.done(); + } + } + + public void addListener(Listener listener) { + fListeners.add(listener); + } + + public void removeListener(Listener listener) { + fListeners.remove(listener); + } + + @Override + public void run() { + try { + while(true) { + // Get the next request from the queue. The time-out + // ensures that that the random changes get processed. + final Request request = fQueue.poll(100, TimeUnit.MILLISECONDS); + + // If a request was dequeued, process it. + if (request != null) { + // Simulate a processing delay. + Thread.sleep(PROCESSING_DELAY); + + if (request instanceof CountRequest) { + processCountRequest((CountRequest)request); + } else if (request instanceof ItemRequest) { + processItemRequest((ItemRequest)request); + } else if (request instanceof ShutdownRequest) { + // If shutting down, just break out of the while(true) + // loop and thread will exit. + request.fRequestMonitor.done(); + break; + } + } + + // Simulate data changes. + randomChanges(); + } + } + catch (InterruptedException x) {} + } + + private void processCountRequest(CountRequest request) { + @SuppressWarnings("unchecked") // Suppress warning about lost type info. + DataRequestMonitor rm = (DataRequestMonitor)request.fRequestMonitor; + + rm.setData(fCount); + rm.done(); + } + + private void processItemRequest(ItemRequest request) { + @SuppressWarnings("unchecked") // Suppress warning about lost type info. + DataRequestMonitor rm = (DataRequestMonitor)request.fRequestMonitor; + + if (fChangedIndexes.contains(request.fIndex)) { + rm.setData("Changed: " + request.fIndex); + } else { + rm.setData(Integer.toString(request.fIndex)); + } + rm.done(); + } + + + private void randomChanges() { + // Check if enough time is elapsed. + if (System.currentTimeMillis() > fLastChangeTime + RANDOM_CHANGE_INTERVAL) { + fLastChangeTime = System.currentTimeMillis(); + + // Once every number of changes, reset the count, the rest of the + // times just change certain values. + if (++fCountResetTrigger % RANDOM_COUNT_CHANGE_INTERVALS == 0){ + randomCountReset(); + } else { + randomDataChange(); + } + } + } + + private void randomCountReset() { + // Calculate the new count. + Random random = new java.util.Random(); + fCount = MIN_COUNT + Math.abs(random.nextInt()) % (MAX_COUNT - MIN_COUNT); + + // Reset the changed values. + fChangedIndexes.clear(); + + // Notify listeners + for (Object listener : fListeners.getListeners()) { + ((Listener)listener).countChanged(); + } + } + + private void randomDataChange() { + // Calculate the indexes to change. + Random random = new java.util.Random(); + Set set = new HashSet(); + for (int i = 0; i < fCount * RANDOM_CHANGE_SET_PERCENTAGE / 100; i++) { + set.add( new Integer(Math.abs(random.nextInt()) % fCount) ); + } + + // Add the indexes to an overall set of changed indexes. + fChangedIndexes.addAll(set); + + // Notify listeners + for (Object listener : fListeners.getListeners()) { + ((Listener)listener).valuesChanged(set); + } + } +} + + diff --git a/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/dataviewer/IDataGenerator.java b/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/dataviewer/IDataGenerator.java new file mode 100644 index 00000000000..066eda82377 --- /dev/null +++ b/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/dataviewer/IDataGenerator.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (c) 2006, 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.dd.examples.dsf.dataviewer; + +import java.util.Set; + +import org.eclipse.dd.dsf.concurrent.DataRequestMonitor; +import org.eclipse.dd.dsf.concurrent.RequestMonitor; + +/** + * Data generator is simple source of data used to populate the example table + * view. It contains two asynchronous methods for retrieving the data + * parameters: the count and the value for a given index. It also allows the + * view to receive events indicating when the data supplied by the generator + * is changed. + */ +// TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) +// indicating allowed thread access to this class/method/member +public interface IDataGenerator { + + // Constants which control the data generator behavior. + // Changing the count range can stress the scalability of the system, while + // changing of the process delay and random change interval can stress + // its performance. + final static int MIN_COUNT = 100; + final static int MAX_COUNT = 200; + final static int PROCESSING_DELAY = 10; + final static int RANDOM_CHANGE_INTERVAL = 10000; + final static int RANDOM_COUNT_CHANGE_INTERVALS = 3; + final static int RANDOM_CHANGE_SET_PERCENTAGE = 10; + + + // Listener interface that the view needs to implement to react + // to the changes in data. + public interface Listener { + void countChanged(); + void valuesChanged(Set indexes); + } + + // Data access methods. + void getCount(DataRequestMonitor rm); + void getValue(int index, DataRequestMonitor rm); + + // Method used to shutdown the data generator including any threads that + // it may use. + void shutdown(RequestMonitor rm); + + // Methods for registering change listeners. + void addListener(Listener listener); + void removeListener(Listener listener); +} diff --git a/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/dataviewer/SyncDataViewer.java b/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/dataviewer/SyncDataViewer.java new file mode 100644 index 00000000000..d4d4bdc7fe7 --- /dev/null +++ b/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/dataviewer/SyncDataViewer.java @@ -0,0 +1,183 @@ +/******************************************************************************* + * 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.dd.examples.dsf.dataviewer; + +import java.util.Set; + +import org.eclipse.dd.dsf.concurrent.DataRequestMonitor; +import org.eclipse.dd.dsf.concurrent.ImmediateExecutor; +import org.eclipse.dd.dsf.concurrent.Query; +import org.eclipse.jface.viewers.IStructuredContentProvider; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; + +/** + * Data viewer based on a table, which reads data using synchronous methods. + *

+ * This viewer implements the {@link IStructuredContentProvider} interface + * which is used by the JFace TableViewer class to populate a Table. This + * interface contains one principal methods for reading data {@link #getElements(Object)}, + * which synchronously returns an array of elements. In order to implement this + * method using the asynchronous data generator, this provider uses the + * {@link Query} object. + *

+ */ +public class SyncDataViewer + implements IStructuredContentProvider, IDataGenerator.Listener +{ + // The viewer and generator that this content provider using. + final private TableViewer fViewer; + final private IDataGenerator fDataGenerator; + + public SyncDataViewer(TableViewer viewer, IDataGenerator generator) { + fViewer = viewer; + fDataGenerator = generator; + fDataGenerator.addListener(this); + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + // Not used + } + + + public Object[] getElements(Object inputElement) { + + // Create the query object for reading data count. + Query countQuery = new Query() { + @Override + protected void execute(DataRequestMonitor rm) { + fDataGenerator.getCount(rm); + } + }; + + // Submit the query to be executed. A query implements a runnable + // interface and it has to be executed in order to do its work. + ImmediateExecutor.getInstance().execute(countQuery); + int count = 0; + + // Block until the query completes, which will happen when the request + // monitor of the execute() method is marked done. + try { + count = countQuery.get(); + } catch (Exception e) { + // InterruptedException and ExecutionException can be thrown here. + // ExecutionException containing a CoreException will be thrown + // if an error status is set to the Query's request monitor. + return new Object[0]; + } + + // Create the array that will be filled with elements. + // For each index in the array execute a query to get the element at + // that index. + final Object[] elements = new Object[count]; + + for (int i = 0; i < count; i++) { + final int index = i; + Query valueQuery = new Query() { + @Override + protected void execute(DataRequestMonitor rm) { + fDataGenerator.getValue(index, rm); + } + }; + ImmediateExecutor.getInstance().execute(valueQuery); + try { + elements[i] = valueQuery.get(); + } catch (Exception e) { + elements[i] = "error"; + } + } + return elements; + } + + public void dispose() { + fDataGenerator.removeListener(this); + } + + public void countChanged() { + // For any event from the generator, refresh the whole viewer. + refreshViewer(); + } + + public void valuesChanged(Set indexes) { + // For any event from the generator, refresh the whole viewer. + refreshViewer(); + } + + private void refreshViewer() { + // TODO Exercise 5 - Add a call to getElements() to force a deadlock. + + // This method may be called on any thread, switch to the display + // thread before calling the viewer. + Display display = fViewer.getControl().getDisplay(); + display.asyncExec( new Runnable() { + public void run() { + if (!fViewer.getControl().isDisposed()) { + fViewer.refresh(); + } + } + }); + } + + public static void main(String[] args) { + // Create the shell to hold the viewer. + Display display = new Display(); + Shell shell = new Shell(display, SWT.SHELL_TRIM); + shell.setLayout(new GridLayout()); + GridData data = new GridData(GridData.FILL_BOTH); + shell.setLayoutData(data); + Font font = new Font(display, "Courier", 10, SWT.NORMAL); + + // Create the table viewer. + TableViewer tableViewer = new TableViewer(shell, SWT.BORDER); + tableViewer.getControl().setLayoutData(data); + + // Create the data generator. + // TODO Exercise 5 - Use the DataGeneratorWithExecutor() instead. + final IDataGenerator generator = new DataGeneratorWithThread(); + + // Create the content provider which will populate the viewer. + SyncDataViewer contentProvider = new SyncDataViewer(tableViewer, generator); + tableViewer.setContentProvider(contentProvider); + tableViewer.setInput(new Object()); + + // Open the shell and service the display dispatch loop until user + // closes the shell. + shell.open(); + while (!shell.isDisposed()) { + if (!display.readAndDispatch()) + display.sleep(); + } + + // The IDataGenerator.shutdown() method is asynchronous, this requires + // using a query again in order to wait for its completion. + Query shutdownQuery = new Query() { + @Override + protected void execute(DataRequestMonitor rm) { + generator.shutdown(rm); + } + }; + ImmediateExecutor.getInstance().execute(shutdownQuery); + try { + shutdownQuery.get(); + } catch (Exception e) {} + + // Shut down the display. + font.dispose(); + display.dispose(); + } + +} diff --git a/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/dataviewer/answers/AsyncDataViewer.java b/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/dataviewer/answers/AsyncDataViewer.java new file mode 100644 index 00000000000..8c8195595a9 --- /dev/null +++ b/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/dataviewer/answers/AsyncDataViewer.java @@ -0,0 +1,272 @@ +/******************************************************************************* + * Copyright (c) 2006, 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.dd.examples.dsf.dataviewer.answers; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import org.eclipse.dd.dsf.concurrent.ConfinedToDsfExecutor; +import org.eclipse.dd.dsf.concurrent.ThreadSafe; +import org.eclipse.dd.dsf.concurrent.DataRequestMonitor; +import org.eclipse.dd.dsf.concurrent.DsfExecutor; +import org.eclipse.dd.dsf.concurrent.ImmediateExecutor; +import org.eclipse.dd.dsf.concurrent.Query; +import org.eclipse.dd.dsf.ui.concurrent.DisplayDsfExecutor; +import org.eclipse.jface.viewers.ILazyContentProvider; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Table; + +/** + * Data viewer based on a table, which reads data using asynchronous methods. + *

+ * This viewer implements the {@link ILazyContentProvider} interface + * which is used by the JFace TableViewer class to populate a Table. This + * interface contains separate asynchronous methods for requesting the count + * and values for individual indexes, which neatly correspond to the methods + * in {@link IDataGenerator}. As an added optimization, this viewer + * implementation checks for the range of visible items in the view upon each + * request, and it cancels old requests which scroll out of view but have not + * been completed yet. However, it is up to the data generator implementation + * to check the canceled state of the requests and ignore them. + *

+ */ +@ConfinedToDsfExecutor("fDisplayExecutor") +public class AsyncDataViewer + implements ILazyContentProvider, IDataGenerator.Listener +{ + // Executor to use instead of Display.asyncExec(). + @ThreadSafe + final private DsfExecutor fDisplayExecutor; + + // The viewer and generator that this content provider using. + final private TableViewer fViewer; + final private IDataGenerator fDataGenerator; + + // Fields used in request cancellation logic. + private List fItemDataRequestMonitors = new LinkedList(); + private Set fIndexesToCancel = new HashSet(); + private int fCancelCallsPending = 0; + + public AsyncDataViewer(TableViewer viewer, IDataGenerator generator) { + fViewer = viewer; + fDisplayExecutor = DisplayDsfExecutor.getDisplayDsfExecutor(fViewer.getTable().getDisplay()); + fDataGenerator = generator; + fDataGenerator.addListener(this); + } + + public void dispose() { + fDataGenerator.removeListener(this); + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + // Set the initial count to the viewer after the input is set. + queryItemCount(); + } + + public void updateElement(final int index) { + // Calculate the visible index range. + final int topIdx = fViewer.getTable().getTopIndex(); + final int botIdx = topIdx + getVisibleItemCount(topIdx); + + // Request the item for the given index. + queryValue(index); + + // Invoke a cancel task with a delay. The delay allows multiple cancel + // calls to be combined together improving performance of the viewer. + fCancelCallsPending++; + fDisplayExecutor.schedule( + new Runnable() { public void run() { + cancelStaleRequests(topIdx, botIdx); + }}, + 1, TimeUnit.MILLISECONDS); + } + + private int getVisibleItemCount(int top) { + Table table = fViewer.getTable(); + int itemCount = table.getItemCount(); + return Math.min((table.getBounds().height / table.getItemHeight()) + 2, itemCount - top); + } + + @ThreadSafe + public void countChanged() { + queryItemCount(); + } + + @ThreadSafe + public void valuesChanged(final Set indexes) { + // Mark the changed items in table viewer as dirty, this will + // trigger update requests for these indexes if they are + // visible in the viewer. + final TableViewer tableViewer = fViewer; + fDisplayExecutor.execute( new Runnable() { + public void run() { + if (!fViewer.getTable().isDisposed()) { + for (Integer index : indexes) { + tableViewer.clear(index); + } + } + }}); + } + + + private void queryItemCount() { + // Request count from data provider. When the count is returned, we + // have to re-dispatch into the display thread to avoid calling + // the table widget on the DSF dispatch thread. + fIndexesToCancel.clear(); + fDataGenerator.getCount( + // Use the display executor to construct the request monitor, this + // will cause the handleCompleted() method to be automatically + // called on the display thread. + new DataRequestMonitor(fDisplayExecutor, null) { + @Override + protected void handleCompleted() { + if (!fViewer.getTable().isDisposed()) { + fViewer.setItemCount(getData()); + fViewer.getTable().clearAll(); + } + } + }); + + } + + + // Dedicated class for data item requests. This class holds the index + // argument so it can be examined when canceling stale requests. + private class ValueDataRequestMonitor extends DataRequestMonitor { + + /** Index is used when canceling stale requests. */ + int fIndex; + + ValueDataRequestMonitor(int index) { + super(fDisplayExecutor, null); + fIndex = index; + } + + @Override + protected void handleCompleted() { + fItemDataRequestMonitors.remove(this); + + // Check if the request completed successfully, otherwise ignore it. + if (isSuccess()) { + if (!fViewer.getTable().isDisposed()) { + fViewer.replace(getData(), fIndex); + } + } + } + } + + private void queryValue(final int index) { + ValueDataRequestMonitor rm = new ValueDataRequestMonitor(index); + fItemDataRequestMonitors.add(rm); + fDataGenerator.getValue(index, rm); + } + + private void cancelStaleRequests(int topIdx, int botIdx) { + // Decrement the count of outstanding cancel calls. + fCancelCallsPending--; + + // Must check again, in case disposed while re-dispatching. + if (fDataGenerator == null || fViewer.getTable().isDisposed()) return; + + // Go through the outstanding requests and cancel any that + // are not visible anymore. + for (Iterator itr = fItemDataRequestMonitors.iterator(); itr.hasNext();) { + ValueDataRequestMonitor item = itr.next(); + if (item.fIndex < topIdx || item.fIndex > botIdx) { + // Set the item to canceled status, so that the data provider + // will ignore it. + item.cancel(); + + // Add the item index to list of indexes that were canceled, + // which will be sent to the table widget. + fIndexesToCancel.add(item.fIndex); + + // Remove the item from the outstanding cancel requests. + itr.remove(); + } + } + if (!fIndexesToCancel.isEmpty() && fCancelCallsPending == 0) { + Set canceledIdxs = fIndexesToCancel; + fIndexesToCancel = new HashSet(); + + // Clear the indexes of the canceled request, so that the + // viewer knows to request them again when needed. + // Note: clearing using TableViewer.clear(int) seems very + // inefficient, it's better to use Table.clear(int[]). + int[] canceledIdxsArray = new int[canceledIdxs.size()]; + int i = 0; + for (Integer index : canceledIdxs) { + canceledIdxsArray[i++] = index; + } + fViewer.getTable().clear(canceledIdxsArray); + } + } + + + public static void main(String[] args) { + // Create the shell to hold the viewer. + Display display = new Display(); + Shell shell = new Shell(display, SWT.SHELL_TRIM); + shell.setLayout(new GridLayout()); + GridData data = new GridData(GridData.FILL_BOTH); + shell.setLayoutData(data); + Font font = new Font(display, "Courier", 10, SWT.NORMAL); + + // Create the table viewer. + TableViewer tableViewer = new TableViewer(shell, SWT.BORDER | SWT.VIRTUAL); + tableViewer.getControl().setLayoutData(data); + + // Create the data generator. + final IDataGenerator generator = new DataGeneratorWithExecutor(); + + // Create the content provider which will populate the viewer. + AsyncDataViewer contentProvider = new AsyncDataViewer(tableViewer, generator); + tableViewer.setContentProvider(contentProvider); + tableViewer.setInput(new Object()); + + // Open the shell and service the display dispatch loop until user + // closes the shell. + shell.open(); + while (!shell.isDisposed()) { + if (!display.readAndDispatch()) + display.sleep(); + } + + // The IDataGenerator.shutdown() method is asynchronous, this requires + // using a query again in order to wait for its completion. + Query shutdownQuery = new Query() { + @Override + protected void execute(DataRequestMonitor rm) { + generator.shutdown(rm); + } + }; + ImmediateExecutor.getInstance().execute(shutdownQuery); + try { + shutdownQuery.get(); + } catch (Exception e) {} + + // Shut down the display. + font.dispose(); + display.dispose(); + } +} diff --git a/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/dataviewer/answers/DataGeneratorWithExecutor.java b/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/dataviewer/answers/DataGeneratorWithExecutor.java new file mode 100644 index 00000000000..9d84110d1a5 --- /dev/null +++ b/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/dataviewer/answers/DataGeneratorWithExecutor.java @@ -0,0 +1,311 @@ +/******************************************************************************* + * Copyright (c) 2006 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.dd.examples.dsf.dataviewer.answers; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Random; +import java.util.Set; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.TimeUnit; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.dd.dsf.concurrent.ConfinedToDsfExecutor; +import org.eclipse.dd.dsf.concurrent.Immutable; +import org.eclipse.dd.dsf.concurrent.ThreadSafe; +import org.eclipse.dd.dsf.concurrent.DataRequestMonitor; +import org.eclipse.dd.dsf.concurrent.DefaultDsfExecutor; +import org.eclipse.dd.dsf.concurrent.DsfExecutor; +import org.eclipse.dd.dsf.concurrent.DsfRunnable; +import org.eclipse.dd.dsf.concurrent.RequestMonitor; +import org.eclipse.dd.examples.dsf.DsfExamplesPlugin; + +/** + * DSF Executor-based implementation of the data generator. + *

+ * This generator uses a queue of client requests and processes these + * requests periodically using a DSF executor. The main feature of this + * generator is that it uses the executor as its only synchronization object. + * This means that all the fields with the exception of the executor can only + * be accessed while running in the executor thread. + *

+ */ +@ThreadSafe +public class DataGeneratorWithExecutor implements IDataGenerator { + + // Request objects are used to serialize the interface calls into objects + // which can then be pushed into a queue. + @Immutable + abstract class Request { + final RequestMonitor fRequestMonitor; + + Request(RequestMonitor rm) { + fRequestMonitor = rm; + } + } + + @Immutable + class CountRequest extends Request { + CountRequest(DataRequestMonitor rm) { + super(rm); + } + } + + @Immutable + class ItemRequest extends Request { + final int fIndex; + ItemRequest(int index, DataRequestMonitor rm) { + super(rm); + fIndex = index; + } + } + + // The executor used to access all internal data of the generator. + private DsfExecutor fExecutor; + + // Main request queue of the data generator. The getValue(), getCount(), + // and shutdown() methods write into the queue, while the serviceQueue() + // method reads from it. + // The executor used to access all internal data of the generator. + @ConfinedToDsfExecutor("fExecutor") + private List fQueue = new LinkedList(); + + // List of listeners is not synchronized, it also has to be accessed + // using the executor. + @ConfinedToDsfExecutor("fExecutor") + private List fListeners = new LinkedList(); + + // Current number of elements in this generator. + @ConfinedToDsfExecutor("fExecutor") + private int fCount = MIN_COUNT; + + // Counter used to determine when to reset the element count. + @ConfinedToDsfExecutor("fExecutor") + private int fCountResetTrigger = 0; + + // Elements which were modified since the last reset. + @ConfinedToDsfExecutor("fExecutor") + private Set fChangedIndexes = new HashSet(); + + // Flag used to ensure that requests are processed sequentially. + @ConfinedToDsfExecutor("fExecutor") + private boolean fServiceQueueInProgress = false; + + public DataGeneratorWithExecutor() { + // Create the executor + fExecutor = new DefaultDsfExecutor("Supplier Executor"); + + // Schedule a runnable to make the random changes. + fExecutor.scheduleAtFixedRate( + new DsfRunnable() { + public void run() { + randomChanges(); + } + }, + RANDOM_CHANGE_INTERVAL, + RANDOM_CHANGE_INTERVAL, + TimeUnit.MILLISECONDS); + } + + public void shutdown(final RequestMonitor rm) { + try { + fExecutor.execute( new DsfRunnable() { + public void run() { + // Empty the queue of requests and fail them. + for (Request request : fQueue) { + request.fRequestMonitor.setStatus( + new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, "Supplier shut down")); + request.fRequestMonitor.done(); + } + fQueue.clear(); + + // Kill executor. + fExecutor.shutdown(); + rm.done(); + } + }); + } catch (RejectedExecutionException e) { + rm.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, "Supplier shut down")); + rm.done(); + } + } + + public void getCount(final DataRequestMonitor rm) { + try { + fExecutor.execute( new DsfRunnable() { + public void run() { + fQueue.add(new CountRequest(rm)); + serviceQueue(); + } + }); + } catch (RejectedExecutionException e) { + rm.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, "Supplier shut down")); + rm.done(); + } + } + + public void getValue(final int index, final DataRequestMonitor rm) { + try { + fExecutor.execute( new DsfRunnable() { + public void run() { + fQueue.add(new ItemRequest(index, rm)); + serviceQueue(); + } + }); + } catch (RejectedExecutionException e) { + rm.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, "Supplier shut down")); + rm.done(); + } + } + + public void addListener(final Listener listener) { + try { + fExecutor.execute( new DsfRunnable() { + public void run() { + fListeners.add(listener); + } + }); + } catch (RejectedExecutionException e) {} + } + + public void removeListener(final Listener listener) { + try { + fExecutor.execute( new DsfRunnable() { + public void run() { + fListeners.remove(listener); + } + }); + } catch (RejectedExecutionException e) {} + } + + // Main processing function of this generator. + @ConfinedToDsfExecutor("fExecutor") + private void serviceQueue() { + + for (Iterator requestItr = fQueue.iterator(); requestItr.hasNext();) { + Request request = requestItr.next(); + if (request.fRequestMonitor.isCanceled()) { + request.fRequestMonitor.setStatus( + new Status(IStatus.CANCEL, DsfExamplesPlugin.PLUGIN_ID, "Request canceled")); + request.fRequestMonitor.done(); + requestItr.remove(); + } + } + + // If a queue servicing is already scheduled, do nothing. + if (fServiceQueueInProgress) { + return; + } + + if (fQueue.size() != 0) { + // If there are requests to service, remove one from the queue and + // schedule a runnable to process the request after a processing + // delay. + fServiceQueueInProgress = true; + final Request request = fQueue.remove(0); + fExecutor.schedule( + new DsfRunnable() { + public void run() { + if (request instanceof CountRequest) { + processCountRequest((CountRequest)request); + } else if (request instanceof ItemRequest) { + processItemRequest((ItemRequest)request); + } + + // Reset the processing flag and process next + // request. + fServiceQueueInProgress = false; + serviceQueue(); + } + }, + PROCESSING_DELAY, TimeUnit.MILLISECONDS); + } + } + + @ConfinedToDsfExecutor("fExecutor") + private void processCountRequest(CountRequest request) { + @SuppressWarnings("unchecked") // Suppress warning about lost type info. + DataRequestMonitor rm = (DataRequestMonitor)request.fRequestMonitor; + + rm.setData(fCount); + rm.done(); + } + + @ConfinedToDsfExecutor("fExecutor") + private void processItemRequest(ItemRequest request) { + @SuppressWarnings("unchecked") // Suppress warning about lost type info. + DataRequestMonitor rm = (DataRequestMonitor)request.fRequestMonitor; + + if (fChangedIndexes.contains(request.fIndex)) { + rm.setData("Changed: " + request.fIndex); + } else { + rm.setData(Integer.toString(request.fIndex)); + } + rm.done(); + } + + /** + * This method simulates changes in the supplier's data set. + */ + @ConfinedToDsfExecutor("fExecutor") + private void randomChanges() { + // Once every number of changes, reset the count, the rest of the + // times just change certain values. + if (++fCountResetTrigger % RANDOM_COUNT_CHANGE_INTERVALS == 0){ + randomCountReset(); + } else { + randomDataChange(); + } + } + + /** + * Calculates new size for provider's data set. + */ + @ConfinedToDsfExecutor("fExecutor") + private void randomCountReset() { + // Calculate the new count. + Random random = new java.util.Random(); + fCount = MIN_COUNT + Math.abs(random.nextInt()) % (MAX_COUNT - MIN_COUNT); + + // Reset the changed values. + fChangedIndexes.clear(); + + // Notify listeners + for (Listener listener : fListeners) { + listener.countChanged(); + } + } + + /** + * Invalidates a random range of indexes. + */ + @ConfinedToDsfExecutor("fExecutor") + private void randomDataChange() { + // Calculate the indexes to change. + Random random = new java.util.Random(); + Set set = new HashSet(); + for (int i = 0; i < fCount * RANDOM_CHANGE_SET_PERCENTAGE / 100; i++) { + set.add( new Integer(Math.abs(random.nextInt()) % fCount) ); + } + + // Add the indexes to an overall set of changed indexes. + fChangedIndexes.addAll(set); + + // Notify listeners + for (Listener listener : fListeners) { + listener.valuesChanged(set); + } + } +} + diff --git a/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/dataviewer/answers/DataGeneratorWithThread.java b/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/dataviewer/answers/DataGeneratorWithThread.java new file mode 100644 index 00000000000..25bab2d5bc9 --- /dev/null +++ b/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/dataviewer/answers/DataGeneratorWithThread.java @@ -0,0 +1,238 @@ +/******************************************************************************* + * Copyright (c) 2006, 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.dd.examples.dsf.dataviewer.answers; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Random; +import java.util.Set; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.ListenerList; +import org.eclipse.core.runtime.Status; +import org.eclipse.dd.dsf.concurrent.DataRequestMonitor; +import org.eclipse.dd.dsf.concurrent.RequestMonitor; +import org.eclipse.dd.examples.dsf.DsfExamplesPlugin; + +/** + * Thread-based implementation of the data generator. + *

+ * This generator is based around a queue of client requests and a thread which + * reads the requests from the queue and processes them. The distinguishing + * feature of this generator is that it uses a a blocking queue as the main + * synchronization object. However, fListeners, fShutdown, and fChangedIndexes + * fields also need to be thread-safe and so they implement their own + * synchronization. + *

+ */ +public class DataGeneratorWithThread extends Thread implements IDataGenerator { + + // Request objects are used to serialize the interface calls into objects + // which can then be pushed into a queue. + abstract class Request { + final RequestMonitor fRequestMonitor; + + Request(RequestMonitor rm) { + fRequestMonitor = rm; + } + } + + class CountRequest extends Request { + CountRequest(DataRequestMonitor rm) { + super(rm); + } + } + + class ItemRequest extends Request { + final int fIndex; + ItemRequest(int index, DataRequestMonitor rm) { + super(rm); + fIndex = index; + } + } + + class ShutdownRequest extends Request { + ShutdownRequest(RequestMonitor rm) { + super(rm); + } + } + + // Main request queue of the data generator. The getValue(), getCount(), + // and shutdown() methods write into the queue, while the run() method + // reads from it. + private final BlockingQueue fQueue = new LinkedBlockingQueue(); + + // ListenerList class provides thread safety. + private ListenerList fListeners = new ListenerList(); + + // Current number of elements in this generator. + private int fCount = MIN_COUNT; + + // Counter used to determine when to reset the element count. + private int fCountResetTrigger = 0; + + // Elements which were modified since the last reset. + private Set fChangedIndexes = Collections.synchronizedSet(new HashSet()); + + // Used to determine when to make changes in data. + private long fLastChangeTime = System.currentTimeMillis(); + + // Flag indicating when the generator has been shut down. + private AtomicBoolean fShutdown = new AtomicBoolean(false); + + public DataGeneratorWithThread() { + // Immediately kick off the request processing thread. + start(); + } + + public void shutdown(RequestMonitor rm) { + // Mark the generator as shut down. After the fShutdown flag is set, + // all new requests should be shut down. + if (!fShutdown.getAndSet(true)) { + fQueue.add(new ShutdownRequest(rm)); + } else { + // + rm.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, "Supplier shut down")); + rm.done(); + } + } + + public void getCount(DataRequestMonitor rm) { + if (!fShutdown.get()) { + fQueue.add(new CountRequest(rm)); + } else { + rm.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, "Supplier shut down")); + rm.done(); + } + } + + public void getValue(int index, DataRequestMonitor rm) { + if (!fShutdown.get()) { + fQueue.add(new ItemRequest(index, rm)); + } else { + rm.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, "Supplier shut down")); + rm.done(); + } + } + + public void addListener(Listener listener) { + fListeners.add(listener); + } + + public void removeListener(Listener listener) { + fListeners.remove(listener); + } + + @Override + public void run() { + try { + while(true) { + // Get the next request from the queue. The time-out + // ensures that that the random changes get processed. + final Request request = fQueue.poll(100, TimeUnit.MILLISECONDS); + + // If a request was dequeued, process it. + if (request != null) { + // Simulate a processing delay. + Thread.sleep(PROCESSING_DELAY); + + if (request instanceof CountRequest) { + processCountRequest((CountRequest)request); + } else if (request instanceof ItemRequest) { + processItemRequest((ItemRequest)request); + } else if (request instanceof ShutdownRequest) { + // If shutting down, just break out of the while(true) + // loop and thread will exit. + request.fRequestMonitor.done(); + break; + } + } + + // Simulate data changes. + randomChanges(); + } + } + catch (InterruptedException x) {} + } + + private void processCountRequest(CountRequest request) { + @SuppressWarnings("unchecked") // Suppress warning about lost type info. + DataRequestMonitor rm = (DataRequestMonitor)request.fRequestMonitor; + + rm.setData(fCount); + rm.done(); + } + + private void processItemRequest(ItemRequest request) { + @SuppressWarnings("unchecked") // Suppress warning about lost type info. + DataRequestMonitor rm = (DataRequestMonitor)request.fRequestMonitor; + + if (fChangedIndexes.contains(request.fIndex)) { + rm.setData("Changed: " + request.fIndex); + } else { + rm.setData(Integer.toString(request.fIndex)); + } + rm.done(); + } + + + private void randomChanges() { + // Check if enough time is elapsed. + if (System.currentTimeMillis() > fLastChangeTime + RANDOM_CHANGE_INTERVAL) { + fLastChangeTime = System.currentTimeMillis(); + + // Once every number of changes, reset the count, the rest of the + // times just change certain values. + if (++fCountResetTrigger % RANDOM_COUNT_CHANGE_INTERVALS == 0){ + randomCountReset(); + } else { + randomDataChange(); + } + } + } + + private void randomCountReset() { + // Calculate the new count. + Random random = new java.util.Random(); + fCount = MIN_COUNT + Math.abs(random.nextInt()) % (MAX_COUNT - MIN_COUNT); + + // Reset the changed values. + fChangedIndexes.clear(); + + // Notify listeners + for (Object listener : fListeners.getListeners()) { + ((Listener)listener).countChanged(); + } + } + + private void randomDataChange() { + // Calculate the indexes to change. + Random random = new java.util.Random(); + Set set = new HashSet(); + for (int i = 0; i < fCount * RANDOM_CHANGE_SET_PERCENTAGE / 100; i++) { + set.add( new Integer(Math.abs(random.nextInt()) % fCount) ); + } + + // Add the indexes to an overall set of changed indexes. + fChangedIndexes.addAll(set); + + // Notify listeners + for (Object listener : fListeners.getListeners()) { + ((Listener)listener).valuesChanged(set); + } + } +} + + diff --git a/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/dataviewer/answers/IDataGenerator.java b/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/dataviewer/answers/IDataGenerator.java new file mode 100644 index 00000000000..eb8348f8848 --- /dev/null +++ b/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/dataviewer/answers/IDataGenerator.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (c) 2006, 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.dd.examples.dsf.dataviewer.answers; + +import java.util.Set; + +import org.eclipse.dd.dsf.concurrent.DataRequestMonitor; +import org.eclipse.dd.dsf.concurrent.RequestMonitor; +import org.eclipse.dd.dsf.concurrent.ThreadSafe; + +/** + * Data generator is simple source of data used to populate the example table + * view. It contains two asynchronous methods for retrieving the data + * parameters: the count and the value for a given index. It also allows the + * view to receive events indicating when the data supplied by the generator + * is changed. + */ +@ThreadSafe +public interface IDataGenerator { + + // Constants which control the data generator behavior. + // Changing the count range can stress the scalability of the system, while + // changing of the process delay and random change interval can stress + // its performance. + final static int MIN_COUNT = 100; + final static int MAX_COUNT = 200; + final static int PROCESSING_DELAY = 10; + final static int RANDOM_CHANGE_INTERVAL = 10000; + final static int RANDOM_COUNT_CHANGE_INTERVALS = 3; + final static int RANDOM_CHANGE_SET_PERCENTAGE = 10; + + + // Listener interface that the view needs to implement to react + // to the changes in data. + public interface Listener { + void countChanged(); + void valuesChanged(Set indexes); + } + + // Data access methods. + void getCount(DataRequestMonitor rm); + void getValue(int index, DataRequestMonitor rm); + + // Method used to shutdown the data generator including any threads that + // it may use. + void shutdown(RequestMonitor rm); + + // Methods for registering change listeners. + void addListener(Listener listener); + void removeListener(Listener listener); +} diff --git a/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/dataviewer/answers/SyncDataViewer.java b/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/dataviewer/answers/SyncDataViewer.java new file mode 100644 index 00000000000..ef442cb2a9d --- /dev/null +++ b/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/dataviewer/answers/SyncDataViewer.java @@ -0,0 +1,182 @@ +/******************************************************************************* + * 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.dd.examples.dsf.dataviewer.answers; + +import java.util.Set; + +import org.eclipse.dd.dsf.concurrent.DataRequestMonitor; +import org.eclipse.dd.dsf.concurrent.ImmediateExecutor; +import org.eclipse.dd.dsf.concurrent.Query; +import org.eclipse.jface.viewers.IStructuredContentProvider; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; + +/** + * Data viewer based on a table, which reads data using synchronous methods. + *

+ * This viewer implements the {@link IStructuredContentProvider} interface + * which is used by the JFace TableViewer class to populate a Table. This + * interface contains one principal methods for reading data {@link #getElements(Object)}, + * which synchronously returns an array of elements. In order to implement this + * method using the asynchronous data generator, this provider uses the + * {@link Query} object. + *

+ */ +public class SyncDataViewer + implements IStructuredContentProvider, IDataGenerator.Listener +{ + // The viewer and generator that this content provider using. + final private TableViewer fViewer; + final private IDataGenerator fDataGenerator; + + public SyncDataViewer(TableViewer viewer, IDataGenerator generator) { + fViewer = viewer; + fDataGenerator = generator; + fDataGenerator.addListener(this); + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + // Not used + } + + + public Object[] getElements(Object inputElement) { + + // Create the query object for reading data count. + Query countQuery = new Query() { + @Override + protected void execute(DataRequestMonitor rm) { + fDataGenerator.getCount(rm); + } + }; + + // Submit the query to be executed. A query implements a runnable + // interface and it has to be executed in order to do its work. + ImmediateExecutor.getInstance().execute(countQuery); + int count = 0; + + // Block until the query completes, which will happen when the request + // monitor of the execute() method is marked done. + try { + count = countQuery.get(); + } catch (Exception e) { + // InterruptedException and ExecutionException can be thrown here. + // ExecutionException containing a CoreException will be thrown + // if an error status is set to the Query's request monitor. + return new Object[0]; + } + + // Create the array that will be filled with elements. + // For each index in the array execute a query to get the element at + // that index. + final Object[] elements = new Object[count]; + + for (int i = 0; i < count; i++) { + final int index = i; + Query valueQuery = new Query() { + @Override + protected void execute(DataRequestMonitor rm) { + fDataGenerator.getValue(index, rm); + } + }; + ImmediateExecutor.getInstance().execute(valueQuery); + try { + elements[i] = valueQuery.get(); + } catch (Exception e) { + elements[i] = "error"; + } + } + return elements; + } + + public void dispose() { + fDataGenerator.removeListener(this); + } + + public void countChanged() { + // For any event from the generator, refresh the whole viewer. + refreshViewer(); + } + + public void valuesChanged(Set indexes) { + // For any event from the generator, refresh the whole viewer. + refreshViewer(); + } + + private void refreshViewer() { + getElements(null); + + // This method may be called on any thread, switch to the display + // thread before calling the viewer. + Display display = fViewer.getControl().getDisplay(); + display.asyncExec( new Runnable() { + public void run() { + if (!fViewer.getControl().isDisposed()) { + fViewer.refresh(); + } + } + }); + } + + public static void main(String[] args) { + // Create the shell to hold the viewer. + Display display = new Display(); + Shell shell = new Shell(display, SWT.SHELL_TRIM); + shell.setLayout(new GridLayout()); + GridData data = new GridData(GridData.FILL_BOTH); + shell.setLayoutData(data); + Font font = new Font(display, "Courier", 10, SWT.NORMAL); + + // Create the table viewer. + TableViewer tableViewer = new TableViewer(shell, SWT.BORDER); + tableViewer.getControl().setLayoutData(data); + + // Create the data generator. + final IDataGenerator generator = new DataGeneratorWithExecutor(); + + // Create the content provider which will populate the viewer. + SyncDataViewer contentProvider = new SyncDataViewer(tableViewer, generator); + tableViewer.setContentProvider(contentProvider); + tableViewer.setInput(new Object()); + + // Open the shell and service the display dispatch loop until user + // closes the shell. + shell.open(); + while (!shell.isDisposed()) { + if (!display.readAndDispatch()) + display.sleep(); + } + + // The IDataGenerator.shutdown() method is asynchronous, this requires + // using a query again in order to wait for its completion. + Query shutdownQuery = new Query() { + @Override + protected void execute(DataRequestMonitor rm) { + generator.shutdown(rm); + } + }; + ImmediateExecutor.getInstance().execute(shutdownQuery); + try { + shutdownQuery.get(); + } catch (Exception e) {} + + // Shut down the display. + font.dispose(); + display.dispose(); + } + +} diff --git a/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/requestmonitor/Async2Plus2.java b/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/requestmonitor/Async2Plus2.java new file mode 100644 index 00000000000..6fa56cb19c4 --- /dev/null +++ b/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/requestmonitor/Async2Plus2.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * 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.dd.examples.dsf.requestmonitor; + +import java.util.concurrent.Executor; + +import org.eclipse.dd.dsf.concurrent.DataRequestMonitor; +import org.eclipse.dd.dsf.concurrent.ImmediateExecutor; + +/** + * Example of using a DataRequestMonitor to retrieve a result from an + * asynchronous method. + */ +public class Async2Plus2 { + + public static void main(String[] args) { + Executor executor = ImmediateExecutor.getInstance(); + DataRequestMonitor rm = + new DataRequestMonitor(executor, null) { + @Override + protected void handleCompleted() { + System.out.println("2 + 2 = " + getData()); + } + }; + asyncAdd(2, 2, rm); + } + + static void asyncAdd(int value1, int value2, DataRequestMonitor rm) { + rm.setData(value1 + value2); + rm.done(); + } +} diff --git a/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/requestmonitor/AsyncHelloWorld.java b/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/requestmonitor/AsyncHelloWorld.java new file mode 100644 index 00000000000..293c0b399f8 --- /dev/null +++ b/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/requestmonitor/AsyncHelloWorld.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * 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.dd.examples.dsf.requestmonitor; + +import java.util.concurrent.Executor; + +import org.eclipse.dd.dsf.concurrent.ImmediateExecutor; +import org.eclipse.dd.dsf.concurrent.RequestMonitor; + +/** + * "Hello world" example which uses an asynchronous method to print out + * the result. + *

+ * The main method uses an immediate executor, which executes runnables + * as soon as they are submitted, in creating its request monitor. + * + */ +public class AsyncHelloWorld { + + public static void main(String[] args) { + Executor executor = ImmediateExecutor.getInstance(); + RequestMonitor rm = new RequestMonitor(executor, null); + asyncHelloWorld(rm); + } + + static void asyncHelloWorld(RequestMonitor rm) { + System.out.println("Hello world"); + // TODO Exercise 1: - Call the second async. "Hello world 2" method. + // Hint: Calling an asynchronous method requires passing to it a + // request monitor. A new request monitor can be constructed with + // a parent RequestMonitor as an argument argument. The parent gets + // completed automatically when the lower level request monitor is + // completed. + rm.done(); + } + + // TODO: Exercise 1 - Add a second async. "Hello world 2" method. +} diff --git a/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/requestmonitor/AsyncQuicksort.java b/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/requestmonitor/AsyncQuicksort.java new file mode 100644 index 00000000000..f6b6a16e86b --- /dev/null +++ b/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/requestmonitor/AsyncQuicksort.java @@ -0,0 +1,111 @@ +/******************************************************************************* + * 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.dd.examples.dsf.requestmonitor; + +import java.util.Arrays; +import java.util.concurrent.Executor; + +import org.eclipse.dd.dsf.concurrent.CountingRequestMonitor; +import org.eclipse.dd.dsf.concurrent.ImmediateExecutor; +import org.eclipse.dd.dsf.concurrent.RequestMonitor; + +/** + * Example of using a CountingRequestMonitor to wait for multiple + * asynchronous calls to complete. + */ +public class AsyncQuicksort { + + static Executor fgExecutor = ImmediateExecutor.getInstance(); + + public static void main(String[] args) { + final int[] array = {5, 7, 8, 3, 2, 1, 9, 5, 4}; + + System.out.println("To sort: " + Arrays.toString(array)); + asyncQuicksort( + array, 0, array.length - 1, + new RequestMonitor(fgExecutor, null) { + @Override + protected void handleCompleted() { + System.out.println("Sorted: " + Arrays.toString(array)); + } + }); + } + + static void asyncQuicksort(final int[] array, final int left, + final int right, final RequestMonitor rm) + { + if (right > left) { + int pivot = left; + // TODO: Exercise 2 - Convert the call to partition into an + // asynchronous call to asyncPartition(). + // Hint: The rest of the code below should be executed inside + // the DataRequestMonitor.handleCompleted() overriding method. + int newPivot = partition(array, left, right, pivot); + printArray(array, left, right, newPivot); + + CountingRequestMonitor countingRm = new CountingRequestMonitor(fgExecutor, rm); + asyncQuicksort(array, left, newPivot - 1, countingRm); + asyncQuicksort(array, newPivot + 1, right, countingRm); + countingRm.setDoneCount(2); + } else { + rm.done(); + } + } + + // TODO Exercise 2 - Convert partition to an asynchronous method. + // Hint: a DataRequestMonitor should be used to carry the + // return value to the caller. + static int partition(int[] array, int left, int right, int pivot) + { + int pivotValue = array[pivot]; + array[pivot] = array[right]; + array[right] = pivotValue; + int store = left; + for (int i = left; i < right; i++) { + if (array[i] <= pivotValue) { + int tmp = array[store]; + array[store] = array[i]; + array[i] = tmp; + store++; + } + } + array[right] = array[store]; + array[store] = pivotValue; + + // TODO: Request Monitors Exercise 2 - Return the data to caller using + // a request monitor. + return store; + } + + static void printArray(int[] array, int left, int right, int pivot) { + StringBuffer buffer = new StringBuffer(); + for (int i = 0; i < array.length; i++ ) { + if (i == left) { + buffer.append('>'); + } else if (i == pivot) { + buffer.append('-'); + } else { + buffer.append(' '); + } + buffer.append(array[i]); + + if (i == right) { + buffer.append('<'); + } else if (i == pivot) { + buffer.append('-'); + } else { + buffer.append(' '); + } + } + + System.out.println(buffer); + } +} diff --git a/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/requestmonitor/answers/Async2Plus2.java b/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/requestmonitor/answers/Async2Plus2.java new file mode 100644 index 00000000000..d775c0c1e1e --- /dev/null +++ b/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/requestmonitor/answers/Async2Plus2.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * 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.dd.examples.dsf.requestmonitor.answers; + +import java.util.concurrent.Executor; + +import org.eclipse.dd.dsf.concurrent.DataRequestMonitor; +import org.eclipse.dd.dsf.concurrent.ImmediateExecutor; + +/** + * Example of using a DataRequestMonitor to retrieve a result from an + * asynchronous method. + */ +public class Async2Plus2 { + + public static void main(String[] args) { + Executor executor = ImmediateExecutor.getInstance(); + DataRequestMonitor rm = + new DataRequestMonitor(executor, null) { + @Override + protected void handleCompleted() { + System.out.println("2 + 2 = " + getData()); + } + }; + asyncAdd(2, 2, rm); + } + + static void asyncAdd(int value1, int value2, DataRequestMonitor rm) { + rm.setData(value1 + value2); + rm.done(); + } +} diff --git a/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/requestmonitor/answers/AsyncHelloWorld.java b/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/requestmonitor/answers/AsyncHelloWorld.java new file mode 100644 index 00000000000..89061748a35 --- /dev/null +++ b/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/requestmonitor/answers/AsyncHelloWorld.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * 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.dd.examples.dsf.requestmonitor.answers; + +import java.util.concurrent.Executor; + +import org.eclipse.dd.dsf.concurrent.ImmediateExecutor; +import org.eclipse.dd.dsf.concurrent.RequestMonitor; + +/** + * "Hello world" example which uses an asynchronous method to print out + * the result. + *

+ * The main method uses an immediate executor, which executes runnables + * as soon as they are submitted, in creating its request monitor. + * + */ +public class AsyncHelloWorld { + + public static void main(String[] args) { + Executor executor = ImmediateExecutor.getInstance(); + RequestMonitor rm = new RequestMonitor(executor, null); + asyncHelloWorld(rm); + } + + static void asyncHelloWorld(RequestMonitor rm) { + System.out.println("Hello world"); + RequestMonitor rm2 = new RequestMonitor(ImmediateExecutor.getInstance(), rm); + asyncHelloWorld2(rm2); + } + + static void asyncHelloWorld2(RequestMonitor rm) { + System.out.println("Hello world 2"); + rm.done(); + } +} diff --git a/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/requestmonitor/answers/AsyncQuicksort.java b/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/requestmonitor/answers/AsyncQuicksort.java new file mode 100644 index 00000000000..10b510d389d --- /dev/null +++ b/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/requestmonitor/answers/AsyncQuicksort.java @@ -0,0 +1,113 @@ +/******************************************************************************* + * 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.dd.examples.dsf.requestmonitor.answers; + +import java.util.Arrays; +import java.util.concurrent.Executor; + +import org.eclipse.dd.dsf.concurrent.CountingRequestMonitor; +import org.eclipse.dd.dsf.concurrent.DataRequestMonitor; +import org.eclipse.dd.dsf.concurrent.ImmediateExecutor; +import org.eclipse.dd.dsf.concurrent.RequestMonitor; + +/** + * Example of using a CountingRequestMonitor to wait for multiple + * asynchronous calls to complete. + */ +public class AsyncQuicksort { + + static Executor fgExecutor = ImmediateExecutor.getInstance(); + + public static void main(String[] args) { + final int[] array = {5, 7, 8, 3, 2, 1, 9, 5, 4}; + + System.out.println("To sort: " + Arrays.toString(array)); + asyncQuicksort( + array, 0, array.length - 1, + new RequestMonitor(fgExecutor, null) { + @Override + protected void handleCompleted() { + System.out.println("Sorted: " + Arrays.toString(array)); + } + }); + } + + static void asyncQuicksort(final int[] array, final int left, + final int right, final RequestMonitor rm) + { + if (right > left) { + int pivot = left; + asyncPartition( + array, left, right, pivot, + new DataRequestMonitor(fgExecutor, rm) { + @Override + protected void handleCompleted() { + int newPivot = getData(); + printArray(array, left, right, newPivot); + + CountingRequestMonitor countingRm = new CountingRequestMonitor(fgExecutor, rm); + asyncQuicksort(array, left, newPivot - 1, countingRm); + asyncQuicksort(array, newPivot + 1, right, countingRm); + countingRm.setDoneCount(2); + } + }); + } else { + rm.done(); + } + } + + static void asyncPartition(int[] array, int left, int right, int pivot, DataRequestMonitor rm) + { + int pivotValue = array[pivot]; + array[pivot] = array[right]; + array[right] = pivotValue; + int store = left; + for (int i = left; i < right; i++) { + if (array[i] <= pivotValue) { + int tmp = array[store]; + array[store] = array[i]; + array[i] = tmp; + store++; + } + } + array[right] = array[store]; + array[store] = pivotValue; + + // Java 5 automatically converts the int type of the store variable + // to an Integer object. + rm.setData(store); + rm.done(); + } + + static void printArray(int[] array, int left, int right, int pivot) { + StringBuffer buffer = new StringBuffer(); + for (int i = 0; i < array.length; i++ ) { + if (i == left) { + buffer.append('>'); + } else if (i == pivot) { + buffer.append('-'); + } else { + buffer.append(' '); + } + buffer.append(array[i]); + + if (i == right) { + buffer.append('<'); + } else if (i == pivot) { + buffer.append('-'); + } else { + buffer.append(' '); + } + } + + System.out.println(buffer); + } +} diff --git a/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/timers/AlarmsVMNode.java b/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/timers/AlarmsVMNode.java index 3cc08f4ce3c..4d67b00ac01 100644 --- a/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/timers/AlarmsVMNode.java +++ b/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/timers/AlarmsVMNode.java @@ -44,8 +44,14 @@ class AlarmsVMNode extends AbstractDMVMNode @Override protected void updateElementsInSessionThread(final IChildrenUpdate update) { // Check that the services are available - if (!checkService(AlarmService.class, null, update)) return; - if (!checkService(TimerService.class, null, update)) return; + if ( getServicesTracker().getService(AlarmService.class) == null ) { + handleFailedUpdate(update); + return; + } + if ( getServicesTracker().getService(TimerService.class) == null ) { + handleFailedUpdate(update); + return; + } // Find the trigger and timer contexts. If not found, fail. TriggerDMContext alarmDmc = findDmcInPath( diff --git a/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/timers/TimersVMNode.java b/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/timers/TimersVMNode.java index d4e6f456893..d0714e4fe74 100644 --- a/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/timers/TimersVMNode.java +++ b/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/timers/TimersVMNode.java @@ -84,12 +84,14 @@ class TimersVMNode extends AbstractDMVMNode @Override protected void updateElementsInSessionThread(final IChildrenUpdate update) { - if (!checkService(AlarmService.class, null, update)) return; + if ( getServicesTracker().getService(TimerService.class) == null ) { + handleFailedUpdate(update); + return; + } // Retrieve the timer DMContexts, create the corresponding VMCs array, and // set them as result. - TimerDMContext[] timers = - getServicesTracker().getService(TimerService.class).getTimers(); + TimerDMContext[] timers = getServicesTracker().getService(TimerService.class).getTimers(); fillUpdateWithVMCs(update, timers); update.done(); } @@ -118,14 +120,17 @@ class TimersVMNode extends AbstractDMVMNode update.getViewerInput(), update.getElementPath(), TimerDMContext.class); // If either update or service are not valid, fail the update and exit. - if (!checkDmc(dmc, update) || - !checkService(TimerService.class, null, update)) - { - return; + if ( dmc == null ) { + handleFailedUpdate(update); + return; } - TimerService timerService = - getServicesTracker().getService(TimerService.class, null); + TimerService timerService = getServicesTracker().getService(TimerService.class, null); + if ( timerService == null ) { + handleFailedUpdate(update); + return; + } + int value = timerService.getTimerValue(dmc); if (value == -1) { diff --git a/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/timers/TriggersVMNode.java b/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/timers/TriggersVMNode.java index 952b1778aed..78f29ad4937 100644 --- a/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/timers/TriggersVMNode.java +++ b/plugins/org.eclipse.dd.examples.dsf/src/org/eclipse/dd/examples/dsf/timers/TriggersVMNode.java @@ -85,10 +85,12 @@ class TriggersVMNode extends AbstractDMVMNode @Override protected void updateElementsInSessionThread(final IChildrenUpdate update) { - if (!checkService(AlarmService.class, null, update)) return; + if ( getServicesTracker().getService(AlarmService.class) == null ) { + handleFailedUpdate(update); + return; + } - TriggerDMContext[] triggers = - getServicesTracker().getService(AlarmService.class).getTriggers(); + TriggerDMContext[] triggers = getServicesTracker().getService(AlarmService.class).getTriggers(); fillUpdateWithVMCs(update, triggers); update.done(); } @@ -119,16 +121,19 @@ class TriggersVMNode extends AbstractDMVMNode TriggerDMContext triggerCtx = findDmcInPath( update.getViewerInput(), update.getElementPath(), TriggerDMContext.class); - // If either update or service are not valid, fail the update and exit. - if (!checkDmc(triggerCtx, update) || - !checkService(AlarmService.class, null, update)) - { + // If either update or service are not valid, fail the update and return. + if ( triggerCtx == null ) { + handleFailedUpdate(update); return; } + AlarmService alarmService = getServicesTracker().getService(AlarmService.class, null); + if ( alarmService == null ) { + handleFailedUpdate(update); + return; + } + // Calculate and set the update properties. - AlarmService alarmService = - getServicesTracker().getService(AlarmService.class, null); int value = alarmService.getTriggerValue(triggerCtx); if (value == -1) { diff --git a/plugins/org.eclipse.dd.gdb.ui/src/org/eclipse/dd/gdb/internal/ui/viewmodel/launch/ContainerVMNode.java b/plugins/org.eclipse.dd.gdb.ui/src/org/eclipse/dd/gdb/internal/ui/viewmodel/launch/ContainerVMNode.java index ac0ee1e86b1..f5de55bb1e4 100644 --- a/plugins/org.eclipse.dd.gdb.ui/src/org/eclipse/dd/gdb/internal/ui/viewmodel/launch/ContainerVMNode.java +++ b/plugins/org.eclipse.dd.gdb.ui/src/org/eclipse/dd/gdb/internal/ui/viewmodel/launch/ContainerVMNode.java @@ -54,9 +54,14 @@ public class ContainerVMNode extends AbstractDMVMNode @Override protected void updateElementsInSessionThread(IChildrenUpdate update) { - if (!checkService(AbstractMIControl.class, null, update)) return; - MIControlDMContext containerCtx = getServicesTracker().getService(AbstractMIControl.class).getControlDMContext(); + AbstractMIControl controlService = getServicesTracker().getService(AbstractMIControl.class); + if ( controlService == null ) { + handleFailedUpdate(update); + return; + } + + MIControlDMContext containerCtx = controlService.getControlDMContext(); update.setChild(createVMContext(containerCtx), 0); update.done(); } @@ -77,9 +82,12 @@ public class ContainerVMNode extends AbstractDMVMNode protected void updateLabelInSessionThread(ILabelUpdate[] updates) { for (final ILabelUpdate update : updates) { - if (!checkService(GDBRunControl.class, null, update)) continue; - final GDBRunControl runControl = getServicesTracker().getService(GDBRunControl.class); - + final GDBRunControl runControl = getServicesTracker().getService(GDBRunControl.class); + if ( runControl == null ) { + handleFailedUpdate(update); + continue; + } + final GDBControlDMContext dmc = findDmcInPath(update.getViewerInput(), update.getElementPath(), GDBControlDMContext.class); String imageKey = null; diff --git a/plugins/org.eclipse.dd.gdb.ui/src/org/eclipse/dd/gdb/internal/ui/viewmodel/launch/ThreadVMNode.java b/plugins/org.eclipse.dd.gdb.ui/src/org/eclipse/dd/gdb/internal/ui/viewmodel/launch/ThreadVMNode.java index 1ab9ccd43be..f96aa01b4a0 100644 --- a/plugins/org.eclipse.dd.gdb.ui/src/org/eclipse/dd/gdb/internal/ui/viewmodel/launch/ThreadVMNode.java +++ b/plugins/org.eclipse.dd.gdb.ui/src/org/eclipse/dd/gdb/internal/ui/viewmodel/launch/ThreadVMNode.java @@ -61,26 +61,30 @@ public class ThreadVMNode extends AbstractDMVMNode @Override protected void updateElementsInSessionThread(final IChildrenUpdate update) { - if (!checkService(IRunControl.class, null, update)) return; - final IContainerDMContext contDmc = findDmcInPath(update.getViewerInput(), update.getElementPath(), IContainerDMContext.class); + IRunControl runControl = getServicesTracker().getService(IRunControl.class); + if ( runControl == null ) { + handleFailedUpdate(update); + return; + } - if (contDmc == null) { - handleFailedUpdate(update); - return; - } - - getServicesTracker().getService(IRunControl.class).getExecutionContexts(contDmc, - new DataRequestMonitor(getSession().getExecutor(), null){ - @Override - public void handleCompleted() { - if (!isSuccess()) { - handleFailedUpdate(update); - return; - } - fillUpdateWithVMCs(update, getData()); - update.done(); - } - }); + final IContainerDMContext contDmc = findDmcInPath(update.getViewerInput(), update.getElementPath(), IContainerDMContext.class); + if (contDmc == null) { + handleFailedUpdate(update); + return; + } + + runControl.getExecutionContexts(contDmc, + new DataRequestMonitor(getSession().getExecutor(), null){ + @Override + public void handleCompleted() { + if (!isSuccess()) { + handleFailedUpdate(update); + return; + } + fillUpdateWithVMCs(update, getData()); + update.done(); + } + }); } @@ -195,8 +199,11 @@ public class ThreadVMNode extends AbstractDMVMNode protected void updateLabelInSessionThread(ILabelUpdate[] updates) { for (final ILabelUpdate update : updates) { - if (!checkService(GDBRunControl.class, null, update)) continue; - final GDBRunControl runControl = getServicesTracker().getService(GDBRunControl.class); + final GDBRunControl runControl = getServicesTracker().getService(GDBRunControl.class); + if ( runControl == null ) { + handleFailedUpdate(update); + continue; + } final IMIExecutionDMContext dmc = findDmcInPath(update.getViewerInput(), update.getElementPath(), IMIExecutionDMContext.class); @@ -220,7 +227,11 @@ public class ThreadVMNode extends AbstractDMVMNode // We're in a new dispatch cycle, and we have to check whether the // service reference is still valid. - if (!checkService(GDBRunControl.class, null, update)) return; + final GDBRunControl runControl = getServicesTracker().getService(GDBRunControl.class); + if ( runControl == null ) { + handleFailedUpdate(update); + return; + } final StateChangeReason reason = getData().getStateChangeReason();