mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-08-04 06:45:43 +02:00
Bug 310345: [concurrent] Asynchronous Cache Programming Model (ACPM) utilities for DSF (committing my commented version of Pawel's file. Pawel will be adding the others).
This commit is contained in:
parent
42197fda9c
commit
400a4127fd
2 changed files with 306 additions and 0 deletions
|
@ -0,0 +1,123 @@
|
|||
package org.eclipse.cdt.dsf.concurrent;
|
||||
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2008 Wind River Systems, Inc. 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
|
||||
*******************************************************************************/
|
||||
|
||||
import org.eclipse.core.runtime.IStatus;
|
||||
import org.eclipse.core.runtime.Status;
|
||||
|
||||
/**
|
||||
* A general purpose cache, which caches the result of a single request.
|
||||
* Sub classes need to implement {@link #retrieve(DataRequestMonitor)} to fetch
|
||||
* data from the data source. Clients are responsible for calling
|
||||
* {@link #disable()} and {@link #reset()} to manage the state of the cache in
|
||||
* response to events from the data source.
|
||||
* <p>
|
||||
* This cache requires an executor to use. The executor is used to synchronize
|
||||
* access to the cache state and data.
|
||||
* </p>
|
||||
* @since 2.1
|
||||
*/
|
||||
@ConfinedToDsfExecutor("fExecutor")
|
||||
public abstract class RequestCache<V> extends AbstractCache<V> {
|
||||
|
||||
protected DataRequestMonitor<V> fRm;
|
||||
|
||||
|
||||
public RequestCache(ImmediateInDsfExecutor executor) {
|
||||
super(executor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sub-classes should override this method to retrieve the cache data
|
||||
* from its source.
|
||||
*
|
||||
* @param rm Request monitor for completion of data retrieval.
|
||||
*/
|
||||
@Override
|
||||
protected void retrieve() {
|
||||
// Make sure to cancel the previous rm. This may lead to the rm being
|
||||
// canceled twice, but that's not harmful.
|
||||
if (fRm != null) {
|
||||
fRm.cancel();
|
||||
}
|
||||
|
||||
fRm = new DataRequestMonitor<V>(getExecutor(), null) {
|
||||
|
||||
private IStatus fRawStatus = Status.OK_STATUS;
|
||||
|
||||
@Override
|
||||
protected void handleCompleted() {
|
||||
if (this == fRm) {
|
||||
fRm = null;
|
||||
IStatus status;
|
||||
synchronized (this) {
|
||||
status = fRawStatus;
|
||||
}
|
||||
set(getData(), status);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setStatus(IStatus status) {
|
||||
fRawStatus = status;
|
||||
};
|
||||
|
||||
@Override
|
||||
public boolean isCanceled() {
|
||||
return super.isCanceled() || RequestCache.this.isCanceled();
|
||||
};
|
||||
};
|
||||
retrieve(fRm);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sub-classes should override this method to retrieve the cache data
|
||||
* from its source.
|
||||
*
|
||||
* @param rm Request monitor for completion of data retrieval.
|
||||
*/
|
||||
protected abstract void retrieve(DataRequestMonitor<V> rm);
|
||||
|
||||
@Override
|
||||
protected synchronized void canceled() {
|
||||
if (fRm != null) {
|
||||
fRm.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset(V data, IStatus status) {
|
||||
if (fRm != null) {
|
||||
fRm.cancel();
|
||||
fRm = null;
|
||||
}
|
||||
super.reset(data, status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disable() {
|
||||
if (fRm != null) {
|
||||
fRm.cancel();
|
||||
fRm = null;
|
||||
}
|
||||
super.disable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(V data, IStatus status) {
|
||||
if (fRm != null) {
|
||||
fRm.cancel();
|
||||
fRm = null;
|
||||
}
|
||||
super.set(data, status);
|
||||
}
|
||||
}
|
|
@ -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.cdt.dsf.concurrent;
|
||||
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
|
||||
/**
|
||||
* @since 2.2
|
||||
*/
|
||||
public abstract class Transaction<V> {
|
||||
|
||||
/**
|
||||
* The exception we throw when the client transaction logic asks us to
|
||||
* validate a cache object that is stale (or has never obtained a value from
|
||||
* the source)
|
||||
*/
|
||||
private static final InvalidCacheException INVALID_CACHE_EXCEPTION = new InvalidCacheException();
|
||||
|
||||
/** The request object we've been given to set the transaction results in */
|
||||
private DataRequestMonitor<V> fRm;
|
||||
|
||||
public static class InvalidCacheException extends Exception {
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
||||
|
||||
/**
|
||||
* Kicks off the transaction. We'll either complete the request monitor
|
||||
* immediately if all the data points the transaction needs are cached and
|
||||
* valid, or we'll end up asynchronously completing the monitor if and when
|
||||
* either (a) all the data points are available and up-to-date, or (b)
|
||||
* obtaining them from the source encountered an error. Note that there is
|
||||
* potential in (b) for us to never complete the monitor. If one or more
|
||||
* data points are perpetually becoming stale, then we'll indefinitely wait
|
||||
* for them to stabilize. The caller should cancel its request monitor in
|
||||
* order to get us to stop waiting.
|
||||
*
|
||||
* @param rm Request completion monitor.
|
||||
*/
|
||||
public void request(DataRequestMonitor<V> rm) {
|
||||
if (fRm != null) {
|
||||
assert fRm.isCanceled();
|
||||
fRm.done();
|
||||
}
|
||||
fRm = rm;
|
||||
execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* The transaction logic--code that tries to synchronously make use of,
|
||||
* usually, multiple data points that are normally obtained asynchronously.
|
||||
* Each data point is represented by a cache object. The transaction logic
|
||||
* must check the validity of each cache object just prior to using it
|
||||
* (calling its getData()). It should do that check by calling one of our
|
||||
* validate() methods. Those methods will throw InvalidCacheException if the
|
||||
* cached data is invalid (stale, e.g.,) or CoreException if an error was
|
||||
* encountered the last time it got data form the source. The exception will
|
||||
* abort the transaction, but in the case of InvalidCacheException, we
|
||||
* schedule an asynchronous call that will re-invoke the transaction
|
||||
* logic once the cache object has been updated from the source.
|
||||
*
|
||||
* @return the cached data if it's valid, otherwise an exception is thrown
|
||||
* @throws InvalidCacheException
|
||||
* @throws CoreException
|
||||
*/
|
||||
abstract protected V process() throws InvalidCacheException, CoreException;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
private void execute() {
|
||||
if (fRm.isCanceled()) {
|
||||
fRm.done();
|
||||
fRm = null;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Execute the transaction logic
|
||||
V data = process();
|
||||
|
||||
// No exception means all cache objects used by the transaction
|
||||
// were valid and up to date. Complete the request
|
||||
fRm.setData(data);
|
||||
fRm.done();
|
||||
fRm = null;
|
||||
}
|
||||
catch (CoreException e) {
|
||||
// At least one of the cache objects encountered a failure obtaining
|
||||
// the data from the source. Complete the request.
|
||||
fRm.setStatus(e.getStatus());
|
||||
fRm.done();
|
||||
fRm = null;
|
||||
}
|
||||
catch (InvalidCacheException e) {
|
||||
// At least one of the cache objects was stale/unset. Keep the
|
||||
// request monitor in the incomplete state, thus leaving our client
|
||||
// "waiting" (asynchronously). We'll get called again once the cache
|
||||
// objects are updated, thus re-starting the whole transaction
|
||||
// attempt.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clients must call one of our validate methods prior to using (calling
|
||||
* getData()) on data cache object.
|
||||
*
|
||||
* @param cache
|
||||
* the object being validated
|
||||
* @throws InvalidCacheException
|
||||
* if the data is stale/unset
|
||||
* @throws CoreException
|
||||
* if an error was encountered getting the data from the source
|
||||
*/
|
||||
protected void validate(RequestCache<?> cache) throws InvalidCacheException, CoreException {
|
||||
if (cache.isValid()) {
|
||||
if (!cache.getStatus().isOK()) {
|
||||
throw new CoreException(cache.getStatus());
|
||||
}
|
||||
} else {
|
||||
// Throw the invalid cache exception, but first schedule a
|
||||
// re-attempt of the transaction logic, to occur when the
|
||||
// stale/unset cache object has been updated
|
||||
cache.wait(new RequestMonitor(ImmediateExecutor.getInstance(), fRm) {
|
||||
@Override
|
||||
protected void handleCompleted() {
|
||||
execute();
|
||||
}
|
||||
});
|
||||
throw INVALID_CACHE_EXCEPTION;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link #validate(RequestCache)}. This variant simply validates
|
||||
* multiple cache objects.
|
||||
*/
|
||||
protected void validate(RequestCache<?> ... caches) throws InvalidCacheException, CoreException {
|
||||
// Check if any of the caches have errors:
|
||||
boolean allValid = true;
|
||||
|
||||
for (RequestCache<?> cache : caches) {
|
||||
if (cache.isValid()) {
|
||||
if (!cache.getStatus().isOK()) {
|
||||
throw new CoreException(cache.getStatus());
|
||||
}
|
||||
} else {
|
||||
allValid = false;
|
||||
}
|
||||
}
|
||||
if (!allValid) {
|
||||
// Throw the invalid cache exception, but first schedule a
|
||||
// re-attempt of the transaction logic, to occur when the
|
||||
// stale/unset cache objects have been updated
|
||||
CountingRequestMonitor countringRm = new CountingRequestMonitor(ImmediateExecutor.getInstance(), fRm) {
|
||||
@Override
|
||||
protected void handleCompleted() {
|
||||
execute();
|
||||
}
|
||||
};
|
||||
int count = 0;
|
||||
for (RequestCache<?> cache : caches) {
|
||||
if (!cache.isValid()) {
|
||||
cache.wait(countringRm);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
countringRm.setDoneCount(count);
|
||||
throw INVALID_CACHE_EXCEPTION;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Reference in a new issue