diff options
17 files changed, 1328 insertions, 215 deletions
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/RangeCacheTests.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/RangeCacheTests.java new file mode 100644 index 000000000..ed2386468 --- /dev/null +++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/RangeCacheTests.java @@ -0,0 +1,477 @@ +/******************************************************************************* + * 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.tcf.debug.test; + +import java.util.ArrayList; +import java.util.List; +import java.util.SortedSet; +import java.util.TreeSet; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; + +import junit.framework.Assert; +import junit.framework.TestCase; + +import org.eclipse.tcf.debug.test.util.Callback; +import org.eclipse.tcf.debug.test.util.DataCallback; +import org.eclipse.tcf.debug.test.util.ICache; +import org.eclipse.tcf.debug.test.util.Query; +import org.eclipse.tcf.debug.test.util.RangeCache; +import org.eclipse.tcf.protocol.Protocol; + +/** + * Tests that exercise the DataCache object. + */ +public class RangeCacheTests extends TestCase { + + class TestRangeCache extends RangeCache<Integer> { + + @Override + protected void retrieve(long offset, int count, DataCallback<List<Integer>> rm) { + fRetrieveInfos.add(new RetrieveInfo(offset, count, rm)); + } + + @Override + public void reset() { + super.reset(); + } + + } + + class TestQuery extends Query<List<Integer>> { + long fOffset; + int fCount; + TestQuery(long offset, int count) { + fOffset = offset; + fCount = count; + } + + @Override + protected void execute(final DataCallback<List<Integer>> rm) { + fRangeCache = fTestCache.getRange(fOffset, fCount); + if (fRangeCache.isValid()) { + rm.setData(fRangeCache.getData()); + rm.setError(fRangeCache.getError()); + rm.done(); + } else { + fRangeCache.update(new Callback(rm) { + @Override + protected void handleSuccess() { + rm.setData(fRangeCache.getData()); + rm.done(); + } + }); + } + } + } + + class RetrieveInfo implements Comparable<RetrieveInfo> { + long fOffset; + int fCount; + DataCallback<List<Integer>> fRm; + RetrieveInfo(long offset, int count, DataCallback<List<Integer>> rm) { + fOffset = offset; + fCount = count; + fRm = rm; + } + + public int compareTo(RetrieveInfo o) { + if (fOffset > o.fOffset) { + return 1; + } else if (fOffset == o.fOffset) { + return 0; + } else /*if (fOffset < o.fOffset)*/ { + return -1; + } + } + } + + TestRangeCache fTestCache; + SortedSet<RetrieveInfo> fRetrieveInfos; + ICache<List<Integer>> fRangeCache; + + private List<Integer> makeList(long offset, int count) { + List<Integer> list = new ArrayList<Integer>(count); + for (int i = 0; i < count; i++) { + list.add((int)(i + offset)); + } + return list; + } + + /** + * There's no rule on how quickly the cache has to start data retrieval + * after it has been requested. It could do it immediately, or it could + * wait a dispatch cycle, etc.. + */ + private void waitForRetrieveRm(int size) { + synchronized(this) { + while (fRetrieveInfos.size() < size) { + try { + wait(100); + } catch (InterruptedException e) { + return; + } + } + } + } + + protected void setUp() throws Exception { + fTestCache = new TestRangeCache(); + fRetrieveInfos = new TreeSet<RetrieveInfo>(); + fRangeCache = null; + } + + protected void tearDown() throws ExecutionException, InterruptedException { + fTestCache = null; + } + + private void assertCacheValidWithData(ICache<List<Integer>> cache, long offset, int count) { + Assert.assertTrue(cache.isValid()); + Assert.assertEquals(makeList(offset, count), cache.getData()); + Assert.assertTrue(cache.getError() == null); + } + + private void assertCacheValidWithError(ICache<List<Integer>> cache) { + Assert.assertTrue(cache.isValid()); + Assert.assertFalse(cache.getError() == null); + } + + private void assertCacheWaiting(ICache<List<Integer>> cache) { + Assert.assertFalse(cache.isValid()); + try { + cache.getData(); + Assert.fail("Expected an IllegalStateException"); + } catch (IllegalStateException e) {} + try { + cache.getError(); + Assert.fail("Expected an IllegalStateException"); + } catch (IllegalStateException e) {} + } + + private void completeInfo(final RetrieveInfo info, final long offset, final int count) { + Protocol.invokeAndWait(new Runnable() { + public void run() { + info.fRm.setData(makeList(offset, count)); + info.fRm.done(); + } + }); + } + + private void getRange(long queryOffset, int queryCount, long[] retrieveOffsets, int retrieveCounts[]) throws InterruptedException, ExecutionException { + assert retrieveOffsets.length == retrieveCounts.length; + int retrieveCount = retrieveOffsets.length; + + // Request data from cache + TestQuery q = new TestQuery(queryOffset, queryCount); + + fRangeCache = null; + fRetrieveInfos.clear(); + + q.invoke(); + + // Wait until the cache requests the data. + waitForRetrieveRm(retrieveOffsets.length); + + if (retrieveCount != 0) { + assertCacheWaiting(fRangeCache); + + // Set the data + Assert.assertEquals(retrieveCount, fRetrieveInfos.size()); + int i = 0; + for (RetrieveInfo info : fRetrieveInfos) { + completeInfo(info, retrieveOffsets[i], retrieveCounts[i]); + i++; + } + } + + // Wait for data. + Assert.assertEquals(makeList(queryOffset, queryCount), q.get()); + + // Check state while waiting for data + assertCacheValidWithData(fRangeCache, queryOffset, queryCount); + } + + public void testGetOneRangeTest() throws InterruptedException, ExecutionException { + getRange(0, 100, new long[] { 0 }, new int[] { 100 }); + } + + public void testGetMultipleRangesTest() throws InterruptedException, ExecutionException { + // Retrieve a range in-between two cached ranges + getRange(0, 100, new long[] { 0 }, new int[] { 100 }); + getRange(200, 100, new long[] { 200 }, new int[] { 100 }); + getRange(0, 300, new long[] { 100 }, new int[] { 100 }); + + // Retrieve a range overlapping two cached ranges + getRange(1000, 100, new long[] { 1000 }, new int[] { 100 }); + getRange(1200, 100, new long[] { 1200 }, new int[] { 100 }); + getRange(900, 500, new long[] { 900, 1100, 1300 }, new int[] { 100, 100, 100 }); + + // Retrieve a range that's a subset of a cached range. + getRange(2000, 100, new long[] { 2000 }, new int[] { 100 }); + getRange(2000, 50, new long[] {}, new int[] {}); + getRange(2025, 50, new long[] {}, new int[] {}); + getRange(2050, 50, new long[] {}, new int[] {}); + } + + public void testGetIncompleteRangeTest() throws InterruptedException, ExecutionException { + long queryOffset = 0; + int queryCount = 100; + long[] retrieveOffsets = new long[] { 0 }; + int retrieveCounts[] = new int[] { 50 }; + + assert retrieveOffsets.length == retrieveCounts.length; + int retrieveCount = retrieveOffsets.length; + + // Request data from cache + TestQuery q = new TestQuery(queryOffset, queryCount); + + fRangeCache = null; + fRetrieveInfos.clear(); + + q.invoke(); + + // Wait until the cache requests the data. + waitForRetrieveRm(retrieveOffsets.length); + + if (retrieveCount != 0) { + assertCacheWaiting(fRangeCache); + + // Set the data + Assert.assertEquals(retrieveCount, fRetrieveInfos.size()); + int i = 0; + for (RetrieveInfo info : fRetrieveInfos) { + completeInfo(info, retrieveOffsets[i], retrieveCounts[i]); + i++; + } + } + + // Wait for data. + List<Integer> data = q.get(); + Assert.assertEquals(100, data.size()); + for (int i = 50; i < 100; i++) { + Assert.assertEquals(null, data.get(i)); + } + + // Check state while waiting for data + assertCacheValidWithData(fRangeCache, 0, 50); + } + + + private void cancelRange(long queryOffset, int queryCount, long[] retrieveOffsets, int retrieveCounts[]) throws Exception { + int retrieveCount = retrieveOffsets.length; + + // Request data from cache + TestQuery q = new TestQuery(queryOffset, queryCount); + + fRangeCache = null; + fRetrieveInfos.clear(); + + q.invoke(); + + // Wait until the cache requests the data. + waitForRetrieveRm(retrieveCount); + + assertCacheWaiting(fRangeCache); + + // Set the data without using an executor. + Assert.assertEquals(retrieveCount, fRetrieveInfos.size()); + int i = 0; + for (RetrieveInfo info : fRetrieveInfos) { + Assert.assertEquals(retrieveOffsets[i], info.fOffset); + Assert.assertEquals(retrieveCounts[i], info.fCount); + Assert.assertFalse(info.fRm.isCanceled()); + i++; + } + + q.cancel(true); + try { + q.get(); + Assert.fail("Expected a cancellation exception"); + } catch (CancellationException e) {} // Expected exception; + + for (RetrieveInfo info : fRetrieveInfos) { + Assert.assertTrue(info.fRm.isCanceled()); + } + } + + public void testCancelOneRangeTest() throws Exception { + cancelRange(0, 100, new long[] { 0 }, new int[] { 100 }); + } + + public void testCancelMultipleRangesTest() throws Exception { + // Cancel a couple of ranges. + cancelRange(0, 100, new long[] { 0 }, new int[] { 100 }); + cancelRange(200, 100, new long[] { 200 }, new int[] { 100 }); + + // Cancel a range overlapping two previously canceled ranges. + cancelRange(0, 300, new long[] { 0 }, new int[] { 300 }); + } + + public void testGetAndCancelMultipleRangesTest() throws Exception { + // Cancel a range, then retrieve the same range + cancelRange(0, 100, new long[] { 0 }, new int[] { 100 }); + getRange(0, 100, new long[] { 0 }, new int[] { 100 }); + + // Cancel a range overlapping a cached range. + cancelRange(0, 200, new long[] { 100 }, new int[] { 100 }); + } + + public void testResetOneRangeTest() throws InterruptedException, ExecutionException { + getRange(0, 100, new long[] { 0 }, new int[] { 100 }); + + Protocol.invokeAndWait(new Runnable() { + public void run() { + fTestCache.reset(); + }; + }); + + getRange(0, 100, new long[] { 0 }, new int[] { 100 }); + } + + public void testResetMultipleRangesTest() throws InterruptedException, ExecutionException { + // Retrieve a range in-between two cached ranges + getRange(0, 100, new long[] { 0 }, new int[] { 100 }); + getRange(200, 100, new long[] { 200 }, new int[] { 100 }); + getRange(0, 300, new long[] { 100 }, new int[] { 100 }); + + // Retrieve a range overlapping two cached ranges + getRange(1000, 100, new long[] { 1000 }, new int[] { 100 }); + getRange(1200, 100, new long[] { 1200 }, new int[] { 100 }); + getRange(900, 500, new long[] { 900, 1100, 1300 }, new int[] { 100, 100, 100 }); + + // Retrieve a range that's a subset of a cached range. + getRange(2000, 100, new long[] { 2000 }, new int[] { 100 }); + getRange(2000, 50, new long[] {}, new int[] {}); + getRange(2025, 50, new long[] {}, new int[] {}); + getRange(2050, 50, new long[] {}, new int[] {}); + + Protocol.invokeAndWait(new Runnable() { + public void run() { + fTestCache.reset(); + }; + }); + + // Retrieve a range in-between two cached ranges + getRange(0, 100, new long[] { 0 }, new int[] { 100 }); + getRange(200, 100, new long[] { 200 }, new int[] { 100 }); + getRange(0, 300, new long[] { 100 }, new int[] { 100 }); + + // Retrieve a range overlapping two cached ranges + getRange(1000, 100, new long[] { 1000 }, new int[] { 100 }); + getRange(1200, 100, new long[] { 1200 }, new int[] { 100 }); + getRange(900, 500, new long[] { 900, 1100, 1300 }, new int[] { 100, 100, 100 }); + + // Retrieve a range that's a subset of a cached range. + getRange(2000, 100, new long[] { 2000 }, new int[] { 100 }); + getRange(2000, 50, new long[] {}, new int[] {}); + getRange(2025, 50, new long[] {}, new int[] {}); + getRange(2050, 50, new long[] {}, new int[] {}); + } + + public void testResetWhileInvalidTest() throws InterruptedException, ExecutionException { + // Request data from cache + TestQuery q = new TestQuery(10, 100); + + fRangeCache = null; + fRetrieveInfos.clear(); + + q.invoke(); + + // Wait until the cache requests the data. + waitForRetrieveRm(1); + + assertCacheWaiting(fRangeCache); + + Protocol.invokeAndWait(new Runnable() { + public void run() { + fTestCache.reset(); + }; + }); + + // Set the data without using an executor. + Assert.assertEquals(1, fRetrieveInfos.size()); + completeInfo(fRetrieveInfos.first(), 10, 100); + + // Wait for data. + Assert.assertEquals(makeList(10, 100), q.get()); + + // Check state while waiting for data + assertCacheValidWithData(fRangeCache, 10, 100); + } + + public void testSetRangeErrorTest() throws InterruptedException, ExecutionException { + + // Retrieve a range of data. + getRange(0, 100, new long[] { 0 }, new int[] { 100 }); + + // Force an error status into the range cache. + Protocol.invokeAndWait(new Runnable() { + public void run() { + fTestCache.set(0, 100, null, new Throwable( "Cache invalid" )); + }; + }); + + // Retrieve a range cache and check that it immediately contains the error status in it. + fRangeCache = null; + Protocol.invokeAndWait(new Runnable() { + public void run() { + fRangeCache = fTestCache.getRange(0, 100); + } + }); + + assertCacheValidWithError(fRangeCache); + + // Request an update from the range and check for exception. + TestQuery q = new TestQuery(10, 90); + + fRangeCache = null; + fRetrieveInfos.clear(); + + q.invoke(); + + try { + q.get(); + Assert.fail("Expected an ExecutionException"); + } catch (ExecutionException e) {} + } + + public void testGetOneRangeUsingDifferentRangeInstanceTest() throws InterruptedException, ExecutionException { + // Request data from cache + TestQuery q = new TestQuery(0, 100); + + fRangeCache = null; + fRetrieveInfos.clear(); + + q.invoke(); + + // Wait until the cache requests the data. + waitForRetrieveRm(1); + + assertCacheWaiting(fRangeCache); + + // Set the data without using an executor. + Assert.assertEquals(1, fRetrieveInfos.size()); + RetrieveInfo info = fRetrieveInfos.iterator().next(); + completeInfo(info, 0, 100); + + Protocol.invokeAndWait(new Runnable() { + public void run() { + fRangeCache = fTestCache.getRange(0, 100); + } + }); + + // Check state while waiting for data + assertCacheValidWithData(fRangeCache, 0, 100); + } + + + +} diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/SampleTest.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/SampleTest.java index 4c49aa480..7ab497620 100644 --- a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/SampleTest.java +++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/SampleTest.java @@ -10,10 +10,13 @@ import java.util.regex.Pattern; import org.eclipse.debug.internal.ui.viewers.model.provisional.VirtualItem; import org.eclipse.tcf.debug.test.services.IWaitForEventCache; import org.eclipse.tcf.debug.test.services.RunControlCM; +import org.eclipse.tcf.debug.test.util.ICache; +import org.eclipse.tcf.debug.test.util.RangeCache; import org.eclipse.tcf.debug.test.util.Transaction; import org.eclipse.tcf.services.ILineNumbers.CodeArea; import org.eclipse.tcf.services.IRunControl; import org.eclipse.tcf.services.IRunControl.RunControlContext; +import org.eclipse.tcf.services.IStackTrace.StackTraceContext; import org.eclipse.tcf.services.ISymbols; import org.eclipse.tcf.services.ISymbols.Symbol; import org.junit.Assert; @@ -269,6 +272,59 @@ public class SampleTest extends AbstractTcfUITest { } }.get(); } + + public void testStackTraceCMResetOnContextStateChange() throws Exception { + initProcessModel("tcf_test_func2"); + + // Retrieve the current PC and top frame for use later + final String pc = new Transaction<String>() { + @Override + protected String process() throws InvalidCacheException, ExecutionException { + return validate(fRunControlCM.getState(fThreadId)).pc; + } + }.get(); + + // Retrieve data from caches (make them valid). + new Transaction<Object>() { + @Override + protected String process() throws InvalidCacheException, ExecutionException { + String[] frameIds = validate( fStackTraceCM.getChildren(fThreadId) ); + validate (fStackTraceCM.getContexts(frameIds)); + RangeCache<StackTraceContext> framesRange = fStackTraceCM.getContextRange(fThreadId); + validate( framesRange.getRange(0, frameIds.length) ); + return null; + } + }.get(); + + + // Execute a step. + resumeAndWaitForSuspend(fThreadCtx, IRunControl.RM_STEP_OUT); + + // End test, check that all caches were reset and now return an error. + new Transaction<Object>() { + @Override + protected Object process() throws InvalidCacheException, ExecutionException { + ICache<String[]> frameIdsCache = fStackTraceCM.getChildren(fThreadId); + Assert.assertFalse("Expected cache to be reset", frameIdsCache.isValid()); + return null; + } + }.get(); + + new Transaction<Object>() { + @Override + protected Object process() throws InvalidCacheException, ExecutionException { + String[] frameIds = validate( fStackTraceCM.getChildren(fThreadId) ); + ICache<StackTraceContext[]> cache = fStackTraceCM.getContexts(frameIds); + Assert.assertFalse("Expected cache to be reset", cache.isValid()); + + RangeCache<StackTraceContext> framesRange = fStackTraceCM.getContextRange(fThreadId); + ICache<List<StackTraceContext>> framesRangeCache = framesRange.getRange(0, frameIds.length); + Assert.assertFalse("Expected cache to be reset", framesRangeCache.isValid()); + + return null; + } + }.get(); + } public void testRunControlCMChildrenInvalidation() throws Exception { initProcessModel("tcf_test_func0"); @@ -323,12 +379,14 @@ public class SampleTest extends AbstractTcfUITest { new Transaction<String>() { @Override protected String process() throws InvalidCacheException, ExecutionException { - validate( fDiagnosticsCM.cancelTest(fTestId, this) ); // Create wait caches + fRunControlCM.waitForContextRemoved(fProcessId, this); IWaitForEventCache<?>[] waitCaches = new IWaitForEventCache<?>[threads.length]; for (int i = 0; i < threads.length; i++) { waitCaches[i] = fRunControlCM.waitForContextRemoved(threads[i], this); } + validate( fDiagnosticsCM.cancelTest(fTestId, this) ); + validate(waitCaches); validate(fRunControlCM.waitForContextRemoved(fProcessId, this)); try { diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/TransactionTests.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/TransactionTests.java index f1d0d1932..101f9a44f 100644 --- a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/TransactionTests.java +++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/TransactionTests.java @@ -10,7 +10,9 @@ *******************************************************************************/ package org.eclipse.tcf.debug.test; -import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; import java.util.concurrent.ExecutionException; import junit.framework.Assert; @@ -29,7 +31,7 @@ public class TransactionTests extends TestCase { final static private int NUM_CACHES = 5; TestCache[] fTestCaches = new TestCache[NUM_CACHES]; - DataCallback<?>[] fRetrieveRms = new DataCallback<?>[NUM_CACHES]; + Map<DataCallback<?>, Boolean> fRetrieveRms; class TestCache extends CallbackCache<Integer> { @@ -42,7 +44,7 @@ public class TransactionTests extends TestCase { @Override protected void retrieve(DataCallback<Integer> rm) { synchronized(TransactionTests.this) { - fRetrieveRms[fIndex] = rm; + fRetrieveRms.put(rm, true); TransactionTests.this.notifyAll(); } } @@ -70,47 +72,95 @@ public class TransactionTests extends TestCase { return sum; } } + + class TestSingleTransactionUnchecked extends Transaction<Integer> { + + @Override + protected boolean processUnchecked() { + if (!validateUnchecked(fTestCaches[0])) { + return false; + } + assert fTestCaches[0].getError() == null; + setData(fTestCaches[0].getData()); + return true; + } + } + + class TestSumTransactionUnchecked extends Transaction<Integer> { + @Override + protected boolean processUnchecked() { + if (!validateUnchecked(fTestCaches)) { + return false; + } + + int sum = 0; + for (CallbackCache<Integer> cache : fTestCaches) { + assert cache.getError() == null; + sum += cache.getData(); + } + setData(sum); + return true; + } + } + + class TestSumTransactionIterative extends Transaction<Integer> { + @Override + protected boolean processUnchecked() { + int sum = 0; + for (CallbackCache<Integer> cache : fTestCaches) { + if (!validateUnchecked(cache)) { + return false; + } + assert cache.getError() == null; + sum += cache.getData(); + } + setData(sum); + return true; + } + } /** * There's no rule on how quickly the cache has to start data retrieval * after it has been requested. It could do it immediately, or it could * wait a dispatch cycle, etc.. */ - private void waitForRetrieveRm(boolean all) { + private int waitForRetrieveRm() { synchronized(this) { - if (all) { - while (Arrays.asList(fRetrieveRms).contains(null)) { - try { - wait(); - } catch (InterruptedException e) { - return; - } - } - } else { - while (fRetrieveRms[0] == null) { - try { - wait(); - } catch (InterruptedException e) { - return; - } + while (!checkRetrieveRms()) { + try { + wait(100); + } catch (InterruptedException e) { + return NUM_CACHES; } } + return fRetrieveRms.size(); } } + private boolean checkRetrieveRms() { + if (fRetrieveRms.size() == NUM_CACHES) return true; + + boolean retVal = false; + for (DataCallback<?> rm : fRetrieveRms.keySet()) { + if (fRetrieveRms.get(rm)) { + retVal = true; + } + } + return retVal; + } + public void setUp() throws ExecutionException, InterruptedException { + fRetrieveRms = new HashMap<DataCallback<?>, Boolean>(); for (int i = 0; i < fTestCaches.length; i++) { fTestCaches[i] = new TestCache(i); } } public void tearDown() throws ExecutionException, InterruptedException { - fRetrieveRms = new DataCallback<?>[NUM_CACHES]; fTestCaches = new TestCache[NUM_CACHES]; } - public void testSingleTransaction() throws InterruptedException, ExecutionException { - final TestSingleTransaction testTransaction = new TestSingleTransaction(); + private void doTestSingleTransaction(final Transaction<Integer> testTransaction) throws InterruptedException, ExecutionException { // Request data from cache Query<Integer> q = new Query<Integer>() { @Override @@ -121,22 +171,27 @@ public class TransactionTests extends TestCase { q.invoke(); // Wait until the cache starts data retrieval. - waitForRetrieveRm(false); + waitForRetrieveRm(); // Set the data to caches. Protocol.invokeAndWait(new Runnable() { public void run() { - ((DataCallback<Integer>)fRetrieveRms[0]).setData(1); - fRetrieveRms[0].done(); + @SuppressWarnings("unchecked") + DataCallback<Integer> cb = ((DataCallback<Integer>)fRetrieveRms.keySet().iterator().next()); + cb.setData(1); + cb.done(); } }); Assert.assertEquals(1, (int)q.get()); } - - public void testSumTransaction() throws InterruptedException, ExecutionException { - final TestSumTransaction testTransaction = new TestSumTransaction(); + public void testSingleTransaction() throws InterruptedException, ExecutionException { + doTestSingleTransaction(new TestSingleTransaction()); + } + + private void doTestSumTransaction(final Transaction<Integer> testTransaction) throws InterruptedException, ExecutionException { + // Request data from cache Query<Integer> q = new Query<Integer>() { @Override @@ -146,22 +201,50 @@ public class TransactionTests extends TestCase { }; q.invoke(); - // Wait until the cache starts data retrieval. - waitForRetrieveRm(true); - - // Set the data to caches. - Protocol.invokeAndWait(new Runnable() { - public void run() { - for (DataCallback<?> rm : fRetrieveRms) { - ((DataCallback<Integer>)rm).setData(1); - rm.done(); + int numRms = 0; + while (numRms != NUM_CACHES) { + // Wait until the cache starts data retrieval. + numRms = waitForRetrieveRm(); + + // Set the data to caches. + Protocol.invokeAndWait(new Runnable() { + public void run() { + for (Iterator<DataCallback<?>> itr = fRetrieveRms.keySet().iterator(); itr.hasNext();) { + @SuppressWarnings("unchecked") + DataCallback<Integer> rm =((DataCallback<Integer>)itr.next()); + if (fRetrieveRms.get(rm)) { + rm.setData(1); + rm.done(); + fRetrieveRms.put(rm, false); + itr = fRetrieveRms.keySet().iterator(); + } + } } - } - }); + }); + } q.invoke(); Assert.assertEquals(NUM_CACHES, (int)q.get()); } + public void testSumTransaction() throws InterruptedException, ExecutionException { + doTestSumTransaction(new TestSumTransaction()); + } + + public void testSingleTransactionUnchecked() throws InterruptedException, ExecutionException { + doTestSingleTransaction(new TestSingleTransactionUnchecked()); + } + + public void testSumTransactionUnchecked() throws InterruptedException, ExecutionException { + doTestSumTransaction(new TestSumTransactionUnchecked()); + } + + + public void testSumTransactionIterative() throws InterruptedException, ExecutionException { + + doTestSumTransaction(new TestSumTransactionIterative()); + + } + } diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/UtilsSuite.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/UtilsSuite.java new file mode 100644 index 000000000..99a51adbb --- /dev/null +++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/UtilsSuite.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2012 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.tcf.debug.test; + +import junit.framework.Test; +import junit.framework.TestSuite; + +/** + * + */ +public class UtilsSuite extends TestSuite { + + public UtilsSuite() throws Exception { + addTest(new TestSuite(CacheTests.class)); + addTest(new TestSuite(TransactionTests.class)); + addTest(new TestSuite(QueryTests.class)); + addTest(new TestSuite(RangeCacheTests.class)); + } + + /** + * Returns the suite. This is required to + * use the JUnit Launcher. + */ + public static Test suite() throws Exception { + return new UtilsSuite(); + } + +} diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/AbstractCacheManager.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/AbstractCacheManager.java index 909483446..b3d15cd08 100644 --- a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/AbstractCacheManager.java +++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/AbstractCacheManager.java @@ -3,11 +3,9 @@ package org.eclipse.tcf.debug.test.services; import java.util.LinkedHashMap; import java.util.Map; -import org.eclipse.tcf.debug.test.util.ICache; - public class AbstractCacheManager { - protected Map<Key<?>, ICache<?>> fMap = new LinkedHashMap<Key<?>, ICache<?>>(); + protected Map<Key<?>, Object> fMap = new LinkedHashMap<Key<?>, Object>(); public AbstractCacheManager() { super(); @@ -27,7 +25,7 @@ public class AbstractCacheManager { V cache = (V)fMap.get(key); if (cache != null) return cache; cache = key.createCache(); - fMap.put(key, (ICache<?>)cache); + fMap.put(key, cache); return cache; } diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/BreakpointsCM.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/BreakpointsCM.java index b4f1798cb..1a728664d 100644 --- a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/BreakpointsCM.java +++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/BreakpointsCM.java @@ -244,7 +244,7 @@ public class BreakpointsCM extends AbstractCacheManager implements IBreakpoints. } // TODO: avoid iterating over all entries, use separate list for events. - for (Map.Entry<Key<?>, ICache<?>> entry: fMap.entrySet()) { + for (Map.Entry<Key<?>, Object> entry: fMap.entrySet()) { if (entry.getKey() instanceof IdEventKey) { IdEventKey<?> eventKey = (IdEventKey<?>)entry.getKey(); if ( StatusChangedCache.class.equals( eventKey.getCacheClass() ) && @@ -286,7 +286,7 @@ public class BreakpointsCM extends AbstractCacheManager implements IBreakpoints. setBreakpointsProperties(bps); // TODO: avoid iterating over all entries, use separate list for events. - for (Map.Entry<Key<?>, ICache<?>> entry: fMap.entrySet()) { + for (Map.Entry<Key<?>, Object> entry: fMap.entrySet()) { if (entry.getKey() instanceof EventKey) { EventKey<?> eventKey = (EventKey<?>)entry.getKey(); if ( ContextAddedCache.class.equals( eventKey.getCacheClass() ) ) { @@ -311,7 +311,7 @@ public class BreakpointsCM extends AbstractCacheManager implements IBreakpoints. setBreakpointsProperties(bps); // TODO: avoid iterating over all entries, use separate list for events. - for (Map.Entry<Key<?>, ICache<?>> entry: fMap.entrySet()) { + for (Map.Entry<Key<?>, Object> entry: fMap.entrySet()) { if (entry.getKey() instanceof EventKey) { EventKey<?> eventKey = (EventKey<?>)entry.getKey(); if ( ContextChangedCache.class.equals( eventKey.getCacheClass() ) ) { @@ -344,7 +344,7 @@ public class BreakpointsCM extends AbstractCacheManager implements IBreakpoints. } // TODO: avoid iterating over all entries, use separate list for events. - for (Map.Entry<Key<?>, ICache<?>> entry: fMap.entrySet()) { + for (Map.Entry<Key<?>, Object> entry: fMap.entrySet()) { if (entry.getKey() instanceof EventKey) { EventKey<?> eventKey = (EventKey<?>)entry.getKey(); if ( ContextRemovedCache.class.equals( eventKey.getCacheClass() ) ) { diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/ResetMap.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/ResetMap.java index 56bfc2820..ac9ff8822 100644 --- a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/ResetMap.java +++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/ResetMap.java @@ -27,14 +27,18 @@ import org.eclipse.tcf.protocol.Protocol; */ public class ResetMap { + public interface IResettable { + public void reset(); + } + public static final String ANY_ID = ""; - private Map<String, List<AbstractCache<?>>> fValid = new TreeMap<String, List<AbstractCache<?>>>(); + private Map<String, List<IResettable>> fValid = new TreeMap<String, List<IResettable>>(); private Map<String, Set<String>> fChildren = new TreeMap<String, Set<String>>(); private Map<String, String> fParents = new TreeMap<String, String>(); - private Map<AbstractCache<?>, Set<String>> fPending = new LinkedHashMap<AbstractCache<?>, Set<String>>(); + private Map<IResettable, Set<String>> fPending = new LinkedHashMap<IResettable, Set<String>>(); - public synchronized Set<String> removePending(AbstractCache<?> cache) { + public synchronized Set<String> removePending(IResettable cache) { Set<String> pendingIds = fPending.remove(cache); if (pendingIds == null) { pendingIds = Collections.emptySet(); @@ -42,23 +46,23 @@ public class ResetMap { return pendingIds; } - public synchronized void addValid(String id, AbstractCache<?> cache) { + public synchronized void addValid(String id, IResettable cache) { assert !fPending.containsKey(cache); - List<AbstractCache<?>> list = fValid.get(id); + List<IResettable> list = fValid.get(id); if (list == null) { - list = new ArrayList<AbstractCache<?>>(); + list = new ArrayList<IResettable>(); fValid.put(id, list); } list.add(cache); } - public synchronized void addValid(String id, String[] childrenIds, AbstractCache<?> cache) { + public synchronized void addValid(String id, String[] childrenIds, IResettable cache) { assert !fPending.containsKey(cache); - List<AbstractCache<?>> list = fValid.get(id); + List<IResettable> list = fValid.get(id); if (list == null) { - list = new ArrayList<AbstractCache<?>>(); + list = new ArrayList<IResettable>(); fValid.put(id, list); } list.add(cache); @@ -67,13 +71,13 @@ public class ResetMap { } } - public synchronized void addValid(List<String> ids, AbstractCache<?> cache) { + public synchronized void addValid(List<String> ids, IResettable cache) { assert !fPending.containsKey(cache); String id = ids.get(0); - List<AbstractCache<?>> list = fValid.get(id); + List<IResettable> list = fValid.get(id); if (list == null) { - list = new ArrayList<AbstractCache<?>>(); + list = new ArrayList<IResettable>(); fValid.put(id, list); } list.add(cache); @@ -88,8 +92,8 @@ public class ResetMap { } } - public synchronized List<AbstractCache<?>> getCaches(String id) { - List<AbstractCache<?>> list = fValid.get(id); + public synchronized List<IResettable> getCaches(String id) { + List<IResettable> list = fValid.get(id); if (list == null) { list = Collections.emptyList(); } @@ -105,9 +109,9 @@ public class ResetMap { // Do not call reset while holding lock to reset map. Instead collect // caches to reset and reset them outside the lock. - List<AbstractCache<?>> anyList = Collections.emptyList(); - List<AbstractCache<?>> idList = Collections.emptyList(); - List<AbstractCache<?>> parentList = Collections.emptyList(); + List<IResettable> anyList = Collections.emptyList(); + List<IResettable> idList = Collections.emptyList(); + List<IResettable> parentList = Collections.emptyList(); synchronized (this) { for (Set<String> pendingIds : fPending.values()) { pendingIds.add(id); @@ -115,7 +119,7 @@ public class ResetMap { anyList = fValid.remove(ANY_ID); if (resetChildren && fChildren.containsKey(id)) { - idList = new ArrayList<AbstractCache<?>>(); + idList = new ArrayList<IResettable>(); collectChildren(id, idList); } else { idList = fValid.remove(id); @@ -133,7 +137,7 @@ public class ResetMap { resetList(parentList); } - private void collectChildren(String id, List<AbstractCache<?>> caches) { + private void collectChildren(String id, List<IResettable> caches) { caches.addAll( fValid.remove(id) ); Set<String> children = fChildren.remove(id); if (children != null) { @@ -143,17 +147,15 @@ public class ResetMap { } } - private void resetList(List<AbstractCache<?>> list) { + private void resetList(List<IResettable> list) { if (list != null) { - for (AbstractCache<?> cache : list) { - if (cache.isValid()) { - cache.reset(); - } + for (IResettable cache : list) { + cache.reset(); } } } - public synchronized void addPending(AbstractCache<?> cache) { + public synchronized void addPending(IResettable cache) { fPending.put(cache, new TreeSet<String>()); } } diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/RunControlCM.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/RunControlCM.java index ca4b8194e..803102717 100644 --- a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/RunControlCM.java +++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/RunControlCM.java @@ -17,6 +17,7 @@ import java.util.Set; import java.util.TreeSet; import java.util.concurrent.ExecutionException; +import org.eclipse.tcf.debug.test.services.ResetMap.IResettable; import org.eclipse.tcf.debug.test.util.CallbackCache; import org.eclipse.tcf.debug.test.util.DataCallback; import org.eclipse.tcf.debug.test.util.ICache; @@ -154,7 +155,7 @@ public class RunControlCM extends AbstractCacheManager implements RunControlList } - private class ContextStateCache extends CallbackCache<ContextState> { + private class ContextStateCache extends CallbackCache<ContextState> implements IResettable { private class InnerContextStateCache extends TokenCache<ContextState> implements IRunControl.DoneGetState { private final RunControlContext fContext; @@ -327,7 +328,7 @@ public class RunControlCM extends AbstractCacheManager implements RunControlList listener.containerResumed(context_ids); } - for (Map.Entry<Key<?>, ICache<?>> entry: fMap.entrySet()) { + for (Map.Entry<Key<?>, Object> entry: fMap.entrySet()) { if (entry.getKey() instanceof IdEventKey) { IdEventKey<?> eventKey = (IdEventKey<?>)entry.getKey(); if ( WaitForContainerResumedCache.class.equals(eventKey.getCacheClass()) && @@ -361,7 +362,7 @@ public class RunControlCM extends AbstractCacheManager implements RunControlList listener.containerSuspended(context, pc, reason, params, suspended_ids); } - for (Map.Entry<Key<?>, ICache<?>> entry: fMap.entrySet()) { + for (Map.Entry<Key<?>, Object> entry: fMap.entrySet()) { if (entry.getKey() instanceof IdEventKey) { IdEventKey<?> eventKey = (IdEventKey<?>)entry.getKey(); if ( WaitForContainerSuspendedCache.class.equals( eventKey.getCacheClass() ) && @@ -413,7 +414,7 @@ public class RunControlCM extends AbstractCacheManager implements RunControlList } // TODO: avoid iterating over all entries, use separate list for events. - for (Map.Entry<Key<?>, ICache<?>> entry: fMap.entrySet()) { + for (Map.Entry<Key<?>, Object> entry: fMap.entrySet()) { if (entry.getKey() instanceof IdEventKey) { IdEventKey<?> eventKey = (IdEventKey<?>)entry.getKey(); if ( WaitForContextSuspendedCache.class.equals( eventKey.getCacheClass() ) && @@ -456,7 +457,7 @@ public class RunControlCM extends AbstractCacheManager implements RunControlList } // TODO: avoid iterating over all entries, use separate list for events. - for (Map.Entry<Key<?>, ICache<?>> entry: fMap.entrySet()) { + for (Map.Entry<Key<?>, Object> entry: fMap.entrySet()) { if (entry.getKey() instanceof IdEventKey) { IdEventKey<?> eventKey = (IdEventKey<?>)entry.getKey(); if ( WaitForContextResumedCache.class.equals( eventKey.getCacheClass() ) && @@ -483,7 +484,7 @@ public class RunControlCM extends AbstractCacheManager implements RunControlList fStateResetMap.reset(id); // TODO: avoid iterating over all entries, use separate list for events. - for (Map.Entry<Key<?>, ICache<?>> entry: fMap.entrySet()) { + for (Map.Entry<Key<?>, Object> entry: fMap.entrySet()) { if (entry.getKey() instanceof IdEventKey) { IdEventKey<?> eventKey = (IdEventKey<?>)entry.getKey(); if ( WaitForContextExceptionCache.class.equals( eventKey.getCacheClass() ) && @@ -547,7 +548,7 @@ public class RunControlCM extends AbstractCacheManager implements RunControlList fChildrenResetMap.reset(parent, false, false); } - for (Map.Entry<Key<?>, ICache<?>> entry: fMap.entrySet()) { + for (Map.Entry<Key<?>, Object> entry: fMap.entrySet()) { if (entry.getKey() instanceof ContextEventKey) { ContextEventKey<?> eventKey = (ContextEventKey<?>)entry.getKey(); if ( WaitForContextAddedCache.class.equals( eventKey.getCacheClass()) && @@ -580,7 +581,7 @@ public class RunControlCM extends AbstractCacheManager implements RunControlList fChildrenResetMap.reset(context.getID(), true, false); } - for (Map.Entry<Key<?>, ICache<?>> entry: fMap.entrySet()) { + for (Map.Entry<Key<?>, Object> entry: fMap.entrySet()) { if (entry.getKey() instanceof ContextEventKey) { ContextEventKey<?> eventKey = (ContextEventKey<?>)entry.getKey(); if ( WaitForContextChangedCache.class.equals( eventKey.getCacheClass()) && @@ -614,7 +615,7 @@ public class RunControlCM extends AbstractCacheManager implements RunControlList fStateResetMap.reset(context_id); } - for (Map.Entry<Key<?>, ICache<?>> entry: fMap.entrySet()) { + for (Map.Entry<Key<?>, Object> entry: fMap.entrySet()) { if (entry.getKey() instanceof IdEventKey) { IdEventKey<?> eventKey = (IdEventKey<?>)entry.getKey(); if ( WaitForContextRemovedCache.class.equals( eventKey.getCacheClass()) && diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/StackTraceCM.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/StackTraceCM.java index 5f8b582db..e913a26b4 100644 --- a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/StackTraceCM.java +++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/StackTraceCM.java @@ -10,10 +10,18 @@ *******************************************************************************/ package org.eclipse.tcf.debug.test.services; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; import java.util.Map; +import java.util.concurrent.ExecutionException; +import org.eclipse.tcf.debug.test.services.ResetMap.IResettable; +import org.eclipse.tcf.debug.test.util.DataCallback; import org.eclipse.tcf.debug.test.util.ICache; +import org.eclipse.tcf.debug.test.util.RangeCache; import org.eclipse.tcf.debug.test.util.TokenCache; +import org.eclipse.tcf.debug.test.util.Transaction; import org.eclipse.tcf.protocol.IToken; import org.eclipse.tcf.services.IRunControl; import org.eclipse.tcf.services.IRunControl.RunControlContext; @@ -26,6 +34,7 @@ import org.eclipse.tcf.services.IStackTrace.StackTraceContext; public class StackTraceCM extends AbstractCacheManager implements IRunControl.RunControlListener { private IStackTrace fService; private IRunControl fRunControl; + private final ResetMap fRunControlStateResetMap = new ResetMap(); public StackTraceCM(IStackTrace service, IRunControl runControl) { fService = service; @@ -39,70 +48,90 @@ public class StackTraceCM extends AbstractCacheManager implements IRunControl.Ru super.dispose(); } - private class ChildrenCache extends TokenCache<String[]> implements IStackTrace.DoneGetChildren { - private final String fId; - public ChildrenCache(String id) { - fId = id; - } - - @Override - protected IToken retrieveToken() { - return fService.getChildren(fId, this); - } - public void doneGetChildren(IToken token, Exception error, String[] context_ids) { - set(token, context_ids, error); - } - public void resetChildren() { - if (isValid()) reset(); - } - }; + public ICache<String[]> getChildren(final String id) { + class MyCache extends TokenCache<String[]> implements IStackTrace.DoneGetChildren { + @Override + protected IToken retrieveToken() { + fRunControlStateResetMap.addPending(this); + return fService.getChildren(id, this); + } + + public void doneGetChildren(IToken token, Exception error, String[] context_ids) { + fRunControlStateResetMap.removePending(this); + fRunControlStateResetMap.addValid(id, this); + set(token, context_ids, error); + } + }; - private class ChildrenCacheKey extends IdKey<ChildrenCache> { - public ChildrenCacheKey(String id) { - super(ChildrenCache.class, id); - } - @Override ChildrenCache createCache() { return new ChildrenCache(fId); } - } - - public ICache<String[]> getChildren(String id) { - return mapCache(new ChildrenCacheKey(id)); + return mapCache(new IdKey<MyCache>(MyCache.class, id) { + @Override MyCache createCache() { return new MyCache(); } + }); } - class ContextCache extends TokenCache<StackTraceContext> implements IStackTrace.DoneGetContext { - private final String fId; + public RangeCache<StackTraceContext> getContextRange(final String parentId) { - public ContextCache(String id) { - fId = id; - } - @Override - protected IToken retrieveToken() { - return fService.getContext(new String[] { fId }, this); - } - public void doneGetContext(IToken token, Exception error, StackTraceContext[] contexts) { - StackTraceContext context = contexts != null && contexts.length > 0 ? contexts[0] : null; - set(token, context, error); - } - public void resetContext() { - if (isValid()) reset(); - } - } - - private class ContextCacheKey extends IdKey<ContextCache> { - public ContextCacheKey(String id) { - super(ContextCache.class, id); - } - @Override ContextCache createCache() { return new ContextCache(fId); } + class MyCache extends RangeCache<StackTraceContext> implements IResettable { + boolean fIsValid = false; + boolean fIsPending = false; + @Override + protected void retrieve(final long offset, final int count, DataCallback<List<StackTraceContext>> rm) { + if (!fIsPending) fRunControlStateResetMap.addPending(MyCache.this); + new Transaction<List<StackTraceContext>>() { + @Override + protected List<StackTraceContext> process() throws InvalidCacheException, ExecutionException { + String[] ids = validate(getChildren(parentId)); + int adjustedCount = Math.min(count, ids.length + (int)offset); + String[] subIds = new String[adjustedCount]; + System.arraycopy(ids, (int)offset, subIds, 0, adjustedCount); + StackTraceContext[] contexts = validate(getContexts(subIds)); + if (!fIsValid) { + fRunControlStateResetMap.removePending(MyCache.this); + fRunControlStateResetMap.addValid(parentId, MyCache.this); + } + return Arrays.asList(contexts); + + } + }.request(rm); + } + + public void reset() { + fIsValid = false; + fIsPending = false; + @SuppressWarnings("unchecked") + List<StackTraceContext> emptyData = (List<StackTraceContext>)Collections.EMPTY_LIST; + set(0, 0, emptyData, new Throwable("Cache invalid") ); + } + + }; + + return mapCache(new IdKey<MyCache>(MyCache.class, parentId) { + @Override MyCache createCache() { return new MyCache(); } + }); + } - public ICache<StackTraceContext>[] getContext(final String[] ids) { - @SuppressWarnings("unchecked") - ICache<StackTraceContext>[] caches = (ICache<StackTraceContext>[])new ICache[ids.length]; - for (int i = 0; i < ids.length; i++) { - caches[i] = mapCache(new ContextCacheKey(ids[i])); + public ICache<StackTraceContext[]> getContexts(final String[] ids) { + class MyCache extends TokenCache<StackTraceContext[]> implements IStackTrace.DoneGetContext { + @Override + protected IToken retrieveToken() { + fRunControlStateResetMap.addPending(this); + return fService.getContext(ids, this); + } + + public void doneGetContext(IToken token, Exception error, StackTraceContext[] contexts) { + fRunControlStateResetMap.removePending(this); + fRunControlStateResetMap.addValid(contexts[0].getParentID(), this); + set(token, contexts, error); + } } - return caches; + + return mapCache(new IdKey<MyCache>(MyCache.class, Arrays.toString(ids)) { + @Override MyCache createCache() { return new MyCache(); } + }); } + + public void contextAdded(RunControlContext[] contexts) { // TODO Auto-generated method stub @@ -110,53 +139,39 @@ public class StackTraceCM extends AbstractCacheManager implements IRunControl.Ru public void contextChanged(RunControlContext[] contexts) { for (RunControlContext context : contexts) { - resetRunControlContext(context.getID()); + fRunControlStateResetMap.reset(context.getID()); } } public void contextRemoved(String[] context_ids) { for (String id : context_ids) { - resetRunControlContext(id); + fRunControlStateResetMap.reset(id); } } public void contextSuspended(String context, String pc, String reason, Map<String, Object> params) { - resetRunControlContext(context); + fRunControlStateResetMap.reset(context); } public void contextResumed(String context) { - resetRunControlContext(context); + fRunControlStateResetMap.reset(context); } public void containerSuspended(String context, String pc, String reason, Map<String, Object> params, String[] suspended_ids) { for (String id : suspended_ids) { - resetRunControlContext(id); + fRunControlStateResetMap.reset(id); } } public void containerResumed(String[] context_ids) { for (String id : context_ids) { - resetRunControlContext(id); + fRunControlStateResetMap.reset(id); } } public void contextException(String context, String msg) { - resetRunControlContext(context); - } - - private void resetRunControlContext(String id) { - ChildrenCache childrenCache = getCache(new ChildrenCacheKey(id)); - if (childrenCache != null && childrenCache.isValid() && childrenCache.getData() != null) { - String[] frameIds = childrenCache.getData(); - for (String frameId : frameIds) { - ContextCache contextCache = getCache(new ContextCacheKey(frameId)); - if (contextCache != null) { - contextCache.resetContext(); - } - } - childrenCache.resetChildren(); - } + fRunControlStateResetMap.reset(context); } } diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/SymbolsCM.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/SymbolsCM.java index 3a1ce110c..b0c276c2f 100644 --- a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/SymbolsCM.java +++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/SymbolsCM.java @@ -16,6 +16,7 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ExecutionException; +import org.eclipse.tcf.debug.test.services.ResetMap.IResettable; import org.eclipse.tcf.debug.test.util.AbstractCache; import org.eclipse.tcf.debug.test.util.CallbackCache; import org.eclipse.tcf.debug.test.util.DataCallback; @@ -63,7 +64,7 @@ public class SymbolsCM extends AbstractCacheManager { ANY_ID_PARENTS.add(ResetMap.ANY_ID); } - abstract private class SymbolCache<V> extends CallbackCache<V> { + abstract private class SymbolCache<V> extends CallbackCache<V> implements IResettable { protected final AbstractCache<V> fInner; private Symbol fSymbol; private List<String> fParents = new ArrayList<String>(4); diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/AbstractCache.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/AbstractCache.java index fcb737215..0b74b1bd7 100644 --- a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/AbstractCache.java +++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/AbstractCache.java @@ -221,37 +221,45 @@ public abstract class AbstractCache<V> implements ICache<V> { * canceled, or if there are no clients waiting at all. */ protected boolean isCanceled() { + Object waitingList = null; + synchronized (this) { + if (fWaitingList instanceof Callback[]) { + waitingList = new Callback[((Callback[])fWaitingList).length]; + System.arraycopy(fWaitingList, 0, waitingList, 0, ((Callback[]) fWaitingList).length); + } else { + waitingList = fWaitingList; + } + } boolean canceled; List<Callback> canceledRms = null; - synchronized (this) { - if (fWaitingList instanceof Callback) { - if ( ((Callback)fWaitingList).isCanceled() ) { - canceledRms = new ArrayList<Callback>(1); - canceledRms.add((Callback)fWaitingList); - canceled = true; - } else { - canceled = false; - } - } else if(fWaitingList instanceof Callback[]) { + if (waitingList instanceof Callback) { + if ( ((Callback)waitingList).isCanceled() ) { + canceledRms = new ArrayList<Callback>(1); + canceledRms.add((Callback)waitingList); canceled = true; - Callback[] waitingList = (Callback[])fWaitingList; - for (int i = 0; i < waitingList.length; i++) { - if (waitingList[i] != null) { - if (waitingList[i].isCanceled()) { - if (canceledRms == null) { - canceledRms = new ArrayList<Callback>(1); - } - canceledRms.add( waitingList[i] ); - } else { - canceled = false; + } else { + canceled = false; + } + } else if(waitingList instanceof Callback[]) { + canceled = true; + Callback[] waitingListArray = (Callback[])waitingList; + for (int i = 0; i < waitingListArray.length; i++) { + if (waitingListArray[i] != null) { + if (waitingListArray[i].isCanceled()) { + if (canceledRms == null) { + canceledRms = new ArrayList<Callback>(1); } + canceledRms.add( waitingListArray[i] ); + } else { + canceled = false; } } - } else { - assert fWaitingList == null; - canceled = true; } + } else { + assert waitingList == null; + canceled = true; } + if (canceledRms != null) { final List<Callback> _canceledRms = canceledRms; Protocol.invokeLater(new Runnable() { diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/Callback.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/Callback.java index 227112fbe..dadc2bce7 100644 --- a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/Callback.java +++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/Callback.java @@ -265,22 +265,24 @@ public class Callback { * issued. Even if the request was canceled. * </p> */ - public synchronized void done() { - assert Protocol.getEventQueue().isDispatchThread(); - - if (fDone) { - throw new IllegalStateException("Callback: " + this + ", done() method called more than once"); //$NON-NLS-1$//$NON-NLS-2$ - } - fDone = true; - - // This Callback is done, it can no longer be canceled. - // We must clear the list of cancelListeners because it causes a - // circular reference between parent and child Callback, which - // causes a memory leak. - fCancelListeners = null; - - if (fParentCallback != null) { - fParentCallback.removeCancelListener(fCanceledListener); + public void done() { + synchronized(this) { + assert Protocol.getEventQueue().isDispatchThread(); + + if (fDone) { + throw new IllegalStateException("Callback: " + this + ", done() method called more than once"); //$NON-NLS-1$//$NON-NLS-2$ + } + fDone = true; + + // This Callback is done, it can no longer be canceled. + // We must clear the list of cancelListeners because it causes a + // circular reference between parent and child Callback, which + // causes a memory leak. + fCancelListeners = null; + + if (fParentCallback != null) { + fParentCallback.removeCancelListener(fCanceledListener); + } } handleCompleted(); diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/CallbackCache.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/CallbackCache.java index 9275118c7..4929a4d31 100644 --- a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/CallbackCache.java +++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/CallbackCache.java @@ -48,7 +48,7 @@ public abstract class CallbackCache<V> extends AbstractCache<V> { @Override public boolean isCanceled() { return super.isCanceled() || CallbackCache.this.isCanceled(); - }; + } }; retrieve(fRm); } diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/RangeCache.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/RangeCache.java new file mode 100644 index 000000000..3ef3f4b27 --- /dev/null +++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/RangeCache.java @@ -0,0 +1,329 @@ +/******************************************************************************* + * Copyright (c) 2010 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.tcf.debug.test.util; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.SortedSet; +import java.util.TreeSet; +import java.util.concurrent.ExecutionException; + +import org.eclipse.tcf.protocol.Protocol; + +/** + * Cache for efficiently retrieving overlapping ranges of elements from an + * asynchronous data source. The results of a range request (see + * {@link #getRange(long, int)}) are kept around and reused in subsequent + * requests. E.g., two individual, initial requests for 4 bytes at offsets 0 and + * 4 will relieve a subsequent request for 4 bytes at offset 2 from having to + * asynchronously retrieve the data from the source. The results from the first + * two range requests are used to service the third. + * + * <p> + * Clients of this cache should call {@link #getRange(long, int)} to get a cache + * for that given range of elements. Sub-classes must implement + * {@link #retrieve(long, int, DataRequestMonitor)} to retrieve data from the + * asynchronous data source. + * + * + * + * @since 2.2 + */ +abstract public class RangeCache<V> { + + private class Request extends CallbackCache<List<V>> implements Comparable<Request> { + long fOffset; + int fCount; + @Override + protected void retrieve(DataCallback<java.util.List<V>> rm) { + RangeCache.this.retrieve(fOffset, fCount, rm); + } + + Request(long offset, int count) { + fOffset = offset; + fCount = count; + } + + public int compareTo(RangeCache<V>.Request o) { + if (fOffset > o.fOffset) { + return 1; + } else if (fOffset == o.fOffset) { + return 0; + } else /*if (fOffset < o.fOffset)*/ { + return -1; + } + } + + @Override + public boolean equals(Object _o) { + if (_o instanceof RangeCache<?>.Request) { + RangeCache<?>.Request o = (RangeCache<?>.Request)_o; + return fOffset == o.fOffset && fCount == o.fCount; + } + return false; + } + + @Override + public int hashCode() { + return (int)fOffset^fCount; + } + + @Override + public String toString() { + return "" + fOffset + "(" + fCount + ") -> " + (fOffset + fCount); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + } + + /** + * This transaction class implements the main logic of the range cache. + * It examines the current requests held by the cache and and creates + * requests ones as needed. Once the requests are all valid it returns + * the completed data to the client. + */ + private class RangeTransaction extends Transaction<List<V>> { + + long fOffset; + int fCount; + + RangeTransaction(long offset, int count) { + fOffset = offset; + fCount = count; + } + @Override + protected List<V> process() throws InvalidCacheException, ExecutionException { + clearCanceledRequests(); + + List<Request> transactionRequests = getRequests(fOffset, fCount); + validate(transactionRequests); + + return makeElementsListFromRequests(transactionRequests, fOffset, fCount); + } + + private void clearCanceledRequests() { + for (Iterator<Request> itr = fRequests.iterator(); itr.hasNext();) { + Request request = itr.next(); + if (!request.isValid() && request.isCanceled()) { + itr.remove(); + } + } + } + } + + /** + * Requests currently held by this cache. The requests should be for + * non-overlapping ranges of elements. + */ + + private SortedSet<Request> fRequests = new TreeSet<Request>(); + + /** + * Retrieves data from the data source. + * + * @param offset Offset in data range where the requested list of data should start. + * @param count Number of elements requests. + * @param rm Callback for the data. + */ + protected abstract void retrieve(long offset, int count, DataCallback<List<V>> rm); + + /** + * Returns a cache for the range of requested data. The cache object needs + * to be retrieved from range cache every time that the cache is a + * + * @param offset Offset in data range where the requested list of data should start. + * @param count Number of elements requests. + * @return Cache object for the requested data. + */ + public ICache<List<V>> getRange(final long offset, final int count) { + assert Protocol.isDispatchThread(); + + List<Request> requests = getRequests(offset, count); + + CallbackCache<List<V>> range = new CallbackCache<List<V>>() { + @Override + protected void retrieve(DataCallback<List<V>> rm) { + new RangeTransaction(offset, count).request(rm); + } + }; + + boolean valid = true; + List<Throwable> errors = null; + for (ICache<?> request : requests) { + if (request.isValid()) { + if (request.getError() != null) { + errors = errors != null ? errors : new ArrayList<Throwable>(requests.size()); + errors.add(request.getError()); + } + } else { + valid = false; + break; + } + } + + if (valid) { + if (errors == null) { + range.set(makeElementsListFromRequests(requests, offset, count), null, true); + } else { + if (errors.size() == 1) { + range.set(null, errors.get(0), true); + } else { + AggregateError aggregateError = new AggregateError("Multiple Errors"); + for (Throwable error : errors) aggregateError.add(error); + } + } + } + + return range; + } + + /** + * Sets the given list and status to the cache. Subsequent range requests + * that fall in its the range will return the given data. Requests outside + * of its range will trigger a call to {@link #retrieve(long, int, DataRequestMonitor)}.<br> + * The given data parameter can be <code>null</code> if the given status + * parameter contains an error. In this case all requests in the given + * range will return the error. + * + * @param offset Offset of the given data to set to cache. + * @param count Count of the given data to set to cache. + * @param data List of elements to set to cache. Can be <code>null</code>. + * @param error Error object to set to cache or <code>null</code> if none. + */ + public void set(long offset, int count, List<V> data, Throwable error) { + for (Request request : fRequests) { + if (!request.isValid()) { + request.set(null, null, false); + } + } + fRequests.clear(); + Request request = new Request(offset, count); + request.set(data, error, true); + fRequests.add(request); + } + + /** + * Forces the cache into an invalid state. If there are any pending + * requests, their will continue and their results will be cached. + */ + protected void reset() { + for (Iterator<Request> itr = fRequests.iterator(); itr.hasNext();) { + Request request = itr.next(); + if (request.isValid()) { + request.reset(); + itr.remove(); + } + } + } + + private List<Request> getRequests(long fOffset, int fCount) { + List<Request> requests = new ArrayList<Request>(1); + + // Create a new request for the data to retrieve. + Request current = new Request(fOffset, fCount); + + current = adjustRequestHead(current, requests, fOffset, fCount); + if (current != null) { + current = adjustRequestTail(current, requests, fOffset, fCount); + } + if (current != null) { + requests.add(current); + fRequests.add(current); + } + return requests; + } + + // Adjust the beginning of the requested range of data. If there + // is already an overlapping range in front of the requested range, + // then use it. + private Request adjustRequestHead(Request request, List<Request> transactionRequests, long offset, int count) { + SortedSet<Request> headRequests = fRequests.headSet(request); + if (!headRequests.isEmpty()) { + Request headRequest = headRequests.last(); + long headEndOffset = headRequest.fOffset + headRequest.fCount; + if (headEndOffset > offset) { + transactionRequests.add(headRequest); + request.fCount = (int)(request.fCount - (headEndOffset - offset)); + request.fOffset = headEndOffset; + } + } + if (request.fCount > 0) { + return request; + } else { + return null; + } + } + + /** + * Adjust the end of the requested range of data. + * @param current + * @param transactionRequests + * @return + */ + private Request adjustRequestTail(Request current, List<Request> transactionRequests, long offset, int count) { + // Create a duplicate of the tailSet, in order to avoid a concurrent modification exception. + List<Request> tailSet = new ArrayList<Request>(fRequests.tailSet(current)); + + // Iterate through the matching requests and add them to the requests list. + for (Request tailRequest : tailSet) { + if (tailRequest.fOffset < current.fOffset + count) { + // found overlapping request add it to list + if (tailRequest.fOffset <= current.fOffset) { + // next request starts off at the beginning of current request + transactionRequests.add(tailRequest); + current.fOffset = tailRequest.fOffset + tailRequest.fCount; + current.fCount = ((int)(offset - current.fOffset)) + count ; + if (current.fCount <= 0) { + return null; + } + } else { + current.fCount = (int)(tailRequest.fOffset - current.fOffset); + transactionRequests.add(current); + fRequests.add(current); + current = null; + transactionRequests.add(tailRequest); + long tailEndOffset = tailRequest.fOffset + tailRequest.fCount; + long rangeEndOffset = offset + count; + if (tailEndOffset >= rangeEndOffset) { + return null; + } else { + current = new Request(tailEndOffset, (int)(rangeEndOffset - tailEndOffset)); + } + } + } else { + break; + } + } + return current; + } + + private List<V> makeElementsListFromRequests(List<Request> requests, long offset, int count) { + List<V> retVal = new ArrayList<V>(count); + long index = offset; + long end = offset + count; + int requestIdx = 0; + while (index < end ) { + Request request = requests.get(requestIdx); + if (index < request.fOffset + request.fCount) { + int dataIndex = (int)(index - request.fOffset); + if (dataIndex < request.getData().size()) { + retVal.add( request.getData().get(dataIndex) ); + } else { + // If request didnt' return enough data, pad range with nulls. + retVal.add(null); + } + index ++; + } else { + requestIdx++; + } + } + return retVal; + } +} diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/TokenCache.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/TokenCache.java index e19b73457..1edb92a1f 100644 --- a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/TokenCache.java +++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/TokenCache.java @@ -12,12 +12,13 @@ package org.eclipse.tcf.debug.test.util; import java.util.concurrent.atomic.AtomicReference; +import org.eclipse.tcf.debug.test.services.ResetMap.IResettable; import org.eclipse.tcf.protocol.IToken; /** * */ -public abstract class TokenCache<V> extends AbstractCache<V> { +public abstract class TokenCache<V> extends AbstractCache<V> implements IResettable { private AtomicReference<IToken> fToken = new AtomicReference<IToken>(); diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/Transaction.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/Transaction.java index 7729ad1a0..26101a021 100644 --- a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/Transaction.java +++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/Transaction.java @@ -22,7 +22,7 @@ import org.eclipse.tcf.protocol.Protocol; /** * @since 2.2 */ -public abstract class Transaction<V> implements Future<V> { +public abstract class Transaction<V> implements Future<V>, Runnable { /** * The exception we throw when the client transaction logic asks us to @@ -60,7 +60,7 @@ public abstract class Transaction<V> implements Future<V> { } fRm = rm; assert fRm != null; - execute(); + run(); } protected void preProcess() {} @@ -112,7 +112,9 @@ public abstract class Transaction<V> implements Future<V> { * @throws CoreException Exception indicating that one of the caches is * in error state and transaction cannot be processed. */ - abstract protected V process() throws InvalidCacheException, ExecutionException; + protected V process() throws InvalidCacheException, ExecutionException { + return null; + } /** * Can be called only while in process(). @@ -135,10 +137,16 @@ public abstract class Transaction<V> implements Future<V> { /** * Method which invokes the transaction logic and handles any exception that * may result. If that logic encounters a stale/unset cache object, then we - * simply do nothing. This method will be called again once the cache - * objects tell us it has obtained an updated value form the source. + * simply do nothing. This method can be invoked by transaction logic when + * caches have become valid, thus unblocking transaction processing. */ - private void execute() { + public void run() { + // If execute is called after transaction completes (as a result of a + // cancelled request completing for example), ignore it. + if (fRm == null) { + return; + } + if (fRm.isCanceled()) { fRm.done(); fRm = null; @@ -156,8 +164,8 @@ public abstract class Transaction<V> implements Future<V> { } /** - * Clients must call one of our validate methods prior to using (calling - * getData()) on data cache object. + * Clients must call one of the validate methods prior to using (calling + * getData()) on data cache object. * * @param cache * the object being validated @@ -180,7 +188,7 @@ public abstract class Transaction<V> implements Future<V> { cache.update(new Callback(fRm) { @Override protected void handleCompleted() { - execute(); + run(); } }); throw INVALID_CACHE_EXCEPTION; @@ -188,15 +196,15 @@ public abstract class Transaction<V> implements Future<V> { } /** - * See {@link #validate(RequestCache)}. This variant simply validates + * See {@link #validate(ICache)}. This variant simply validates * multiple cache objects. */ - public <T> void validate(ICache<?> ... caches) throws InvalidCacheException, ExecutionException { + public void validate(ICache<?> ... caches) throws InvalidCacheException, ExecutionException { validate(Arrays.asList(caches)); } /** - * See {@link #validate(RequestCache)}. This variant simply validates + * See {@link #validate(ICache)}. This variant validates * multiple cache objects. */ public void validate(@SuppressWarnings("rawtypes") Iterable caches) throws InvalidCacheException, ExecutionException { @@ -220,7 +228,7 @@ public abstract class Transaction<V> implements Future<V> { AggregateCallback countringRm = new AggregateCallback(fRm) { @Override protected void handleCompleted() { - execute(); + run(); } }; int count = 0; @@ -236,6 +244,81 @@ public abstract class Transaction<V> implements Future<V> { } } + /** + * See {@link #validate(ICache)}. This variant does not throw exceptions, + * instead it returns <code>false</code> if the cache is not valid. If the + * given cache is valid, and this method returns <code>true</code>, clients + * must still check if the cache contains an error before retrieving its + * data through {@link ICache#getData()}. + * + * @param cache the object being validated + * @return returns <code>false</code> if the cache is not yet valid and + * transaction processing should be interrupted. + */ + public boolean validateUnchecked(ICache<?> cache) { + if (cache.isValid()) { + return true; + } else { + // Just sk the cache to update itself from its source, and schedule a + // re-attempt of the transaction logic to occur when the stale/unset + // cache has been updated + cache.update(new Callback(fRm) { + @Override + protected void handleCompleted() { + run(); + } + }); + return false; + } + } + + /** + * See {@link #validate(ICache)}. This variant validates + * multiple cache objects. + */ + public boolean validateUnchecked(ICache<?> ... caches) { + return validateUnchecked(Arrays.asList(caches)); + } + + + /** + * See {@link #validate(ICache)}. This variant validates + * multiple cache objects. + */ + public boolean validateUnchecked(@SuppressWarnings("rawtypes") Iterable caches) { + // Check if all caches are valid + boolean allValid = true; + + for (Object cacheObj : caches) { + ICache<?> cache = (ICache<?>)cacheObj; + if (!cache.isValid()) { + allValid = false; + } + } + if (allValid) { + return true; + } + + // Just schedule a re-attempt of the transaction logic, to occur + // when the stale/unset cache objects have been updated + AggregateCallback countringRm = new AggregateCallback(fRm) { + @Override + protected void handleCompleted() { + run(); + } + }; + int count = 0; + for (Object cacheObj : caches) { + ICache<?> cache = (ICache<?>)cacheObj; + if (!cache.isValid()) { + cache.update(countringRm); + count++; + } + } + countringRm.setDoneCount(count); + return false; + } + private synchronized Query<V> getQuery(boolean create) { if (fQuery == null && create) { fQuery = new Query<V>() { diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/TransactionCache.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/TransactionCache.java index 874945331..f862c650c 100644 --- a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/TransactionCache.java +++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/TransactionCache.java @@ -87,7 +87,7 @@ public abstract class TransactionCache<V> extends Transaction<V> implements ICac * Can be called while in {@link #process()} * @param cache */ - public void addDependsOn(ICache cache) { + public void addDependsOn(ICache<?> cache) { fDependsOn.add(cache); } @@ -99,7 +99,7 @@ public abstract class TransactionCache<V> extends Transaction<V> implements ICac } @Override - public void validate(Iterable caches) throws InvalidCacheException, ExecutionException { + public void validate(@SuppressWarnings("rawtypes") Iterable caches) throws InvalidCacheException, ExecutionException { for (Object cacheObj : caches) { ICache<?> cache = (ICache<?>)cacheObj; if (cache.isValid()) { @@ -108,4 +108,23 @@ public abstract class TransactionCache<V> extends Transaction<V> implements ICac } super.validate(caches); } + + @Override + public boolean validateUnchecked(ICache<?> cache) { + if (cache.isValid()) { + addDependsOn(cache); + } + return super.validateUnchecked(cache); + } + + @Override + public boolean validateUnchecked(@SuppressWarnings("rawtypes") Iterable caches) { + for (Object cacheObj : caches) { + ICache<?> cache = (ICache<?>)cacheObj; + if (cache.isValid()) { + addDependsOn(cache); + } + } + return super.validateUnchecked(caches); + } } |