{@code
*rcent= ReferenceCache.getInstance().getDataSetOrLock( this.tsb.getURI(), mon);
*if ( !rcent.shouldILoad( Thread.currentThread() ) ) {
* QDataSet result= rcent.park( mon );
*}
*
*
* Be sure to use try/finally when using this cache!
*
* @param uri the URI to load.
* @param monitor to monitor the load.
* @return the ReferenceCacheEntry
*/
public ReferenceCacheEntry getDataSetOrLock( String uri, ProgressMonitor monitor ) {
tidy();
if ( monitor.isFinished() ) {
throw new IllegalStateException("Finished monitor was sent to reference cache getDataSetOrLock");
}
logger.log( Level.FINE, "getDataSetOrLock on thread {0} {1}", new Object[]{Thread.currentThread(), uri});
ReferenceCacheEntry result;
synchronized (this) {
result= uris.get(uri);
if ( result!=null ) {
if ( result.wasGarbageCollected() ) { // it was garbage collected.
result= new ReferenceCacheEntry(uri,monitor);
result.status= ReferenceCacheEntryStatus.LOADING; // TODO: study Why shouldn't we just set the loading status here?
result.loadThread= Thread.currentThread();
uris.put( uri, result );
logger.log( Level.FINEST, "this thread must reload garbage-collected uri" );
} else {
logger.log( Level.FINEST, "wait for another thread which is loading uri" );
if ( result.monitor.isFinished() && result.status!=ReferenceCacheEntryStatus.DONE) {
logger.log(Level.WARNING, "cache entry was never cleared: {0}", result.uri);
}
}
} else {
result= new ReferenceCacheEntry(uri,monitor);
result.status= ReferenceCacheEntryStatus.LOADING;
result.loadThread= Thread.currentThread();
uris.put( uri, result );
logger.log( Level.FINEST, "this thread will load uri" );
}
}
return result;
}
/**
* park this thread until the other guy has finished loading.
* @param ent
* @param monitor the monitor of the load.
*/
public void park( ReferenceCacheEntry ent, ProgressMonitor monitor ) {
if ( ent.loadThread==Thread.currentThread() && ent.status!=ReferenceCacheEntryStatus.DONE ) {
throw new IllegalStateException("This thread was supposed to load the data");
}
if ( monitor.isFinished() ) {
throw new IllegalStateException("Finished monitor was sent to reference cache park");
}
monitor.started();
monitor.setProgressMessage("waiting for load");
int warn1095Count=0;
while ( true ) {
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
logger.log(Level.SEVERE, ex.getMessage(), ex);
}
if ( !( ent.monitor.isFinished() || ent.monitor.isCancelled() ) ) {
monitor.setTaskSize( ent.monitor.getTaskSize());
monitor.setTaskProgress( ent.monitor.getTaskProgress());
}
if ( ent.monitor.isFinished() && ent.status!=ReferenceCacheEntryStatus.DONE ) {
if ( warn1095Count>100 ) { // give it 10 seconds to resolve this.
logger.warning("bug 1095: there is a monitor that is finished, but the reference cache entry is not marked as done.");
}
warn1095Count++;
//ent.status= ReferenceCacheEntryStatus.DONE;
}
if ( ent.status==ReferenceCacheEntryStatus.DONE ) break;
}
monitor.finished();
}
/**
* put the dataset into the ReferenceCache. If it is mutable, then a copy is
* made.
* @param uri
* @param ds
*/
private synchronized void putDataSet( String uri, QDataSet ds ) {
ReferenceCacheEntry result= uris.get(uri);
if ( result==null ) throw new IllegalStateException("nobody asked for this dataset, use offerDataSet");
logger.log( Level.FINEST, "putDataSet on thread {0} {1}", new Object[]{Thread.currentThread(), uri});
if ( ds instanceof MutablePropertyDataSet ) {
MutablePropertyDataSet mpds= (MutablePropertyDataSet)ds;
if ( !mpds.isImmutable() ) {
ds= Ops.copy(mpds);
((MutablePropertyDataSet)ds).makeImmutable();
}
} else if ( ds==null ) {
ds= NULL;
}
result.qds= new WeakReference<>(ds);
result.status= ReferenceCacheEntryStatus.DONE;
}
//experiments to see if Jython Caching works if the garbage collector unable to clean weak references.
Map