1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-07-24 09:25:31 +02:00

Bug 329481: Inconsistency in DSF ACPM cancel behavior.

This commit is contained in:
John Cortell 2010-11-10 16:47:55 +00:00
parent 85b40d1d99
commit 5fa5ff3a81
3 changed files with 104 additions and 34 deletions

View file

@ -37,7 +37,7 @@ public abstract class AbstractCache<V> implements ICache<V> {
private class RequestCanceledListener implements RequestMonitor.ICanceledListener {
public void requestCanceled(final RequestMonitor canceledRm) {
fExecutor.execute(new Runnable() {
fExecutor.getDsfExecutor().execute(new Runnable() {
public void run() {
handleCanceledRm(canceledRm);
}

View file

@ -12,7 +12,6 @@ package org.eclipse.cdt.dsf.concurrent;
*******************************************************************************/
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
/**
* A general purpose cache, which caches the result of a single request.
@ -46,25 +45,24 @@ public abstract class RequestCache<V> extends AbstractCache<V> {
fRm = new DataRequestMonitor<V>(getImmediateInDsfExecutor(), null) {
private IStatus fRawStatus = Status.OK_STATUS;
@Override
protected void handleCompleted() {
if (this == fRm) {
fRm = null;
IStatus status;
synchronized (this) {
status = fRawStatus;
// If the requestor canceled the request, then leave the
// cache as is, regardless of how the retrieval completes.
// We want the cache to stay in the invalid state so that
// it remains functional. The request may have completed
// successfully, and it may be tempting to use the result in
// that case, but that opens up a can of worms. We'll follow
// the behavior in RequestMonitor: when an RM is canceled,
// it's canceled; period.
if (!isCanceled()) {
set(getData(), getStatus());
}
set(getData(), status);
}
}
@Override
public synchronized void setStatus(IStatus status) {
fRawStatus = status;
};
@Override
public boolean isCanceled() {
return super.isCanceled() || RequestCache.this.isCanceled();

View file

@ -156,7 +156,7 @@ public class CacheTests {
Assert.assertFalse(fRetrieveRm.isCanceled());
}
private void assertCacheCanceled() {
private void assertCacheInvalidAndWithCanceledRM() {
Assert.assertFalse(fTestCache.isValid());
try {
fTestCache.getData();
@ -435,9 +435,11 @@ public class CacheTests {
Assert.fail("Expected a cancellation exception");
} catch (CancellationException e) {} // Expected exception;
assertCacheCanceled();
assertCacheInvalidAndWithCanceledRM();
// Completed the retrieve RM
// Simulate the retrieval completing successfully despite the cancel
// request. Perhaps the retrieval logic isn't checking the RM status.
// Even if it is checking, it may have gotten passed its last checkpoint
fExecutor.submit(new DsfRunnable() {
public void run() {
fRetrieveRm.setData(1);
@ -445,11 +447,79 @@ public class CacheTests {
}
}).get();
// Validate that cache accepts the canceled request data
assertCacheValidWithData(1);
// Validate that cache didn't accept the result after its RM was canceled
assertCacheInvalidAndWithCanceledRM();
}
@Test
public void cancelWhilePendingTest2() throws InterruptedException, ExecutionException {
// Request data from cache
Query<Integer> q = new TestQuery();
fExecutor.execute(q);
// Wait until the cache starts data retrieval.
waitForRetrieveRm();
// Cancel the client request
q.cancel(true);
try {
q.get();
Assert.fail("Expected a cancellation exception");
} catch (CancellationException e) {} // Expected exception;
assertCacheInvalidAndWithCanceledRM();
// Simulate retrieval logic that is regularly checking the RM's cancel
// status and has discovered that the request has been canceled. It
// technically does not need to explicitly set a cancel status object in
// the RM, thanks to RequestMonitor.getStatus() automatically returning
// Status.CANCEL_STATUS when its in the cancel state. So here we
// simulate the retrieval logic just aborting its operations and
// completing the RM. Note that it hasn't provided the data to the
// cache.
fExecutor.submit(new DsfRunnable() {
public void run() {
fRetrieveRm.done();
}
}).get();
assertCacheInvalidAndWithCanceledRM();
}
@Test
public void cancelWhilePendingTest3() throws InterruptedException, ExecutionException {
// Request data from cache
Query<Integer> q = new TestQuery();
fExecutor.execute(q);
// Wait until the cache starts data retrieval.
waitForRetrieveRm();
// Cancel the client request
q.cancel(true);
try {
q.get();
Assert.fail("Expected a cancellation exception");
} catch (CancellationException e) {} // Expected exception;
assertCacheInvalidAndWithCanceledRM();
// Simulate retrieval logic that is regularly checking the RM's cancel
// status and has discovered that the request has been canceled. It
// aborts its processing, sets STATUS.CANCEL_STATUS in the RM and
// completes it.
fExecutor.submit(new DsfRunnable() {
public void run() {
fRetrieveRm.setStatus(Status.CANCEL_STATUS);
fRetrieveRm.done();
}
}).get();
// Validate that cache didn't accept the result after its RM was canceled
assertCacheInvalidAndWithCanceledRM();
}
@Test
public void cancelWhilePendingWithoutClientNotificationTest() throws InterruptedException, ExecutionException {
// Request data from cache
Query<Integer> q = new Query<Integer>() {
@ -479,7 +549,7 @@ public class CacheTests {
// Cancel the client request
q.cancel(true);
assertCacheCanceled();
assertCacheInvalidAndWithCanceledRM();
try {
q.get();
@ -494,8 +564,8 @@ public class CacheTests {
}
}).get();
// Validate that cache accepts the canceled request data
assertCacheValidWithData(1);
// Validate that cache didn't accept the result after its RM was canceled
assertCacheInvalidAndWithCanceledRM();
}
/**
@ -508,7 +578,6 @@ public class CacheTests {
public void cancelAfterCompletedRaceCondition() throws InterruptedException, ExecutionException {
// Create a client request with a badly behaved cancel implementation.
@SuppressWarnings("unchecked")
final RequestMonitor[] rmBad = new RequestMonitor[1] ;
final boolean qBadCanceled[] = new boolean[] { false };
Query<Integer> qBad = new Query<Integer>() {
@ -538,7 +607,8 @@ public class CacheTests {
// Avoid clearing cancel listeners list
};
protected void handleSuccess() {
@Override
protected void handleSuccess() {
rm.setData(fTestCache.getData());
rm.done();
};
@ -587,13 +657,15 @@ public class CacheTests {
@Test
public void cancelWhilePendingWithTwoClientsTest() throws InterruptedException, ExecutionException {
// Request data from cache
// Request data from cache. Use submit/get to ensure both update
// requests are initiated before we wait for retrieval to start
Query<Integer> q1 = new TestQuery();
fExecutor.execute(q1);
fExecutor.submit(q1).get();
// Request data from cache again
Query<Integer> q2 = new TestQuery();
fExecutor.execute(q2);
fExecutor.submit(q2).get();
// Wait until the cache starts data retrieval.
@ -614,7 +686,7 @@ public class CacheTests {
Assert.fail("Expected a cancellation exception");
} catch (CancellationException e) {} // Expected exception;
assertCacheCanceled();
assertCacheInvalidAndWithCanceledRM();
// Completed the retrieve RM
fExecutor.submit(new DsfRunnable() {
@ -624,8 +696,8 @@ public class CacheTests {
}
}).get();
// Validate that cache accepts the canceled request data
assertCacheValidWithData(1);
// Validate that cache didn't accept the result after its RM was canceled
assertCacheInvalidAndWithCanceledRM();
}
@Test
@ -634,7 +706,7 @@ public class CacheTests {
List<Query<Integer>> qList = new ArrayList<Query<Integer>>();
for (int i = 0; i < 10; i++) {
Query<Integer> q = new TestQuery();
fExecutor.execute(q);
fExecutor.submit(q).get();
qList.add(q);
}
@ -660,7 +732,7 @@ public class CacheTests {
// Replace canceled requests with new ones
for (int i = 0; i < toCancel.length; i++) {
Query<Integer> q = new TestQuery();
fExecutor.execute(q);
fExecutor.submit(q).get();
qList.set(toCancel[i], q);
assertCacheWaiting();
}
@ -672,7 +744,7 @@ public class CacheTests {
qList.get(i).cancel(true);
}
qList.get(qList.size() - 1).cancel(true);
assertCacheCanceled();
assertCacheInvalidAndWithCanceledRM();
// Completed the retrieve RM
fExecutor.submit(new DsfRunnable() {
@ -682,8 +754,8 @@ public class CacheTests {
}
}).get();
// Validate that cache accepts the canceled request data
assertCacheValidWithData(1);
// Validate that cache didn't accept the result after its RM was canceled
assertCacheInvalidAndWithCanceledRM();
}
@Test