mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-07-01 06:05:24 +02:00
Bug 329488 - DSF ACPM deadlocks
This commit is contained in:
parent
b0f3b7b768
commit
8378c53a18
3 changed files with 70 additions and 24 deletions
|
@ -195,45 +195,67 @@ public abstract class AbstractCache<V> implements ICache<V> {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if there are no clients waiting for this cache or if the
|
||||||
|
* clients that are waiting, have already canceled their requests.
|
||||||
|
* <p>
|
||||||
|
* Note: Calling this method may cause the client request monitors that were
|
||||||
|
* canceled to be completed with a cancel status. If all the client request
|
||||||
|
* monitors were canceled, this method will also cause the {@link #canceled()}
|
||||||
|
* method to be called. Both of these side effects will only happen
|
||||||
|
* asynchronously after <code>isCanceled()</code> returns.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return <code>true</code> if all clients waiting on this cache have been
|
||||||
|
* canceled, or if there are no clients waiting at all.
|
||||||
|
*/
|
||||||
@ThreadSafe
|
@ThreadSafe
|
||||||
protected boolean isCanceled() {
|
protected boolean isCanceled() {
|
||||||
boolean canceled;
|
boolean canceled;
|
||||||
List<RequestMonitor> canceledRms = null;
|
List<RequestMonitor> canceledRms = null;
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
if (fWaitingList instanceof RequestMonitor && ((RequestMonitor)fWaitingList).isCanceled()) {
|
if (fWaitingList instanceof RequestMonitor) {
|
||||||
canceledRms = new ArrayList<RequestMonitor>(1);
|
if ( ((RequestMonitor)fWaitingList).isCanceled() ) {
|
||||||
canceledRms.add((RequestMonitor)fWaitingList);
|
canceledRms = new ArrayList<RequestMonitor>(1);
|
||||||
fWaitingList = null;
|
canceledRms.add((RequestMonitor)fWaitingList);
|
||||||
|
canceled = true;
|
||||||
|
} else {
|
||||||
|
canceled = false;
|
||||||
|
}
|
||||||
} else if(fWaitingList instanceof RequestMonitor[]) {
|
} else if(fWaitingList instanceof RequestMonitor[]) {
|
||||||
boolean waiting = false;
|
canceled = true;
|
||||||
RequestMonitor[] waitingList = (RequestMonitor[])fWaitingList;
|
RequestMonitor[] waitingList = (RequestMonitor[])fWaitingList;
|
||||||
for (int i = 0; i < waitingList.length; i++) {
|
for (int i = 0; i < waitingList.length; i++) {
|
||||||
if (waitingList[i] != null && waitingList[i].isCanceled()) {
|
if (waitingList[i] != null) {
|
||||||
if (canceledRms == null) {
|
if (waitingList[i].isCanceled()) {
|
||||||
canceledRms = new ArrayList<RequestMonitor>(1);
|
if (canceledRms == null) {
|
||||||
|
canceledRms = new ArrayList<RequestMonitor>(1);
|
||||||
|
}
|
||||||
|
canceledRms.add( waitingList[i] );
|
||||||
|
} else {
|
||||||
|
canceled = false;
|
||||||
}
|
}
|
||||||
canceledRms.add( waitingList[i] );
|
|
||||||
waitingList[i] = null;
|
|
||||||
}
|
}
|
||||||
waiting = waiting || waitingList[i] != null;
|
|
||||||
}
|
}
|
||||||
if (!waiting) {
|
} else {
|
||||||
fWaitingList = null;
|
assert fWaitingList == null;
|
||||||
}
|
canceled = true;
|
||||||
}
|
}
|
||||||
canceled = fWaitingList == null;
|
|
||||||
}
|
}
|
||||||
if (canceledRms != null) {
|
if (canceledRms != null) {
|
||||||
for (RequestMonitor canceledRm : canceledRms) {
|
final List<RequestMonitor> _canceledRms = canceledRms;
|
||||||
canceledRm.setStatus(Status.CANCEL_STATUS);
|
fExecutor.getDsfExecutor().execute(new DsfRunnable() {
|
||||||
canceledRm.removeCancelListener(fRequestCanceledListener);
|
public void run() {
|
||||||
canceledRm.done();
|
for (RequestMonitor canceledRm : _canceledRms) {
|
||||||
}
|
handleCanceledRm(canceledRm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return canceled;
|
return canceled;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resets the cache, setting the data to null and the status to
|
* Resets the cache, setting the data to null and the status to
|
||||||
* INVALID_STATUS. When in the invalid state, neither the data nor the
|
* INVALID_STATUS. When in the invalid state, neither the data nor the
|
||||||
|
|
|
@ -233,8 +233,15 @@ public class RequestMonitor extends DsfExecutable {
|
||||||
* A request monitor is considered canceled if either it or its parent was canceled.
|
* A request monitor is considered canceled if either it or its parent was canceled.
|
||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
public synchronized boolean isCanceled() {
|
public boolean isCanceled() {
|
||||||
return fCanceled || (fParentRequestMonitor != null && fParentRequestMonitor.isCanceled());
|
boolean canceled = false;
|
||||||
|
|
||||||
|
// Avoid holding onto this lock while calling parent RM, which may
|
||||||
|
// acquire other locks (bug 329488).
|
||||||
|
synchronized(this) {
|
||||||
|
canceled = fCanceled;
|
||||||
|
}
|
||||||
|
return canceled || (fParentRequestMonitor != null && fParentRequestMonitor.isCanceled());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -521,6 +521,14 @@ public class CacheTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void cancelWhilePendingWithoutClientNotificationTest() throws InterruptedException, ExecutionException {
|
public void cancelWhilePendingWithoutClientNotificationTest() throws InterruptedException, ExecutionException {
|
||||||
|
final boolean canceledCalled[] = new boolean[] { false };
|
||||||
|
|
||||||
|
fTestCache = new TestCache() {
|
||||||
|
protected synchronized void canceled() {
|
||||||
|
canceledCalled[0] = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
// Request data from cache
|
// Request data from cache
|
||||||
Query<Integer> q = new Query<Integer>() {
|
Query<Integer> q = new Query<Integer>() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -550,12 +558,21 @@ public class CacheTests {
|
||||||
q.cancel(true);
|
q.cancel(true);
|
||||||
|
|
||||||
assertCacheInvalidAndWithCanceledRM();
|
assertCacheInvalidAndWithCanceledRM();
|
||||||
|
|
||||||
|
// AbstractCache.canceled() should be called after isCanceled()
|
||||||
|
// discovers that the client has canceled its request. The canceled() method is
|
||||||
|
// called in a separate dispatch cycle, so we have to wait one cycle of the executor
|
||||||
|
// after is canceled is called.
|
||||||
|
fRetrieveRm.isCanceled();
|
||||||
|
fExecutor.submit(new Runnable() { public void run() {} }).get();
|
||||||
|
Assert.assertTrue(canceledCalled[0]);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
q.get();
|
q.get();
|
||||||
Assert.fail("Expected a cancellation exception");
|
Assert.fail("Expected a cancellation exception");
|
||||||
} catch (CancellationException e) {} // Expected exception;
|
} catch (CancellationException e) {} // Expected exception;
|
||||||
|
|
||||||
|
|
||||||
// Completed the retrieve RM
|
// Completed the retrieve RM
|
||||||
fExecutor.submit(new DsfRunnable() {
|
fExecutor.submit(new DsfRunnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
|
|
Loading…
Add table
Reference in a new issue