Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package org.intermine.objectstore.query;
/*
* Copyright (C) 2002-2022 FlyMine
*
* This code may be freely distributed and modified under the
* terms of the GNU Lesser General Public Licence. This should
* be distributed with the code. See the LICENSE file for more
* information or http://www.gnu.org/copyleft/lesser.html.
*
*/
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.log4j.Logger;
import org.intermine.objectstore.ObjectStoreException;
/**
* A manager for the prefetch mechanism for the Results object.
*
* @author Matthew Wakeling
*/
public final class PrefetchManager
{
private PrefetchManager() {
}
private static final Logger LOG = Logger.getLogger(PrefetchManager.class);
/** Pending set of requests - always accessed inside a synchronise on sync. */
protected static Set pending = new HashSet();
/** Set of requests currently being serviced. This Set is not accessed inside a block
* synchronised on any global object, so it must be able to handle concurrent access. */
protected static Set serviced = Collections.synchronizedSet(new HashSet());
protected static int serviceThreads = 0;
private static Object sync = new Object();
protected static final int LOADING = 3;
/*
* This class provides methods for cancelling requests, so here is an explanation of how this
* magic works.
*
* The ObjectStore provides a query cancellation system, where you register your thread as
* having a request ID, which is any object you choose, and then you run your query as normal.
* Another thread can come along and pass that request ID to the objectstore, telling it to
* cancel the associated query. The Thread making the request may throw an exception, and
* all subsequent actions will throw exceptions until the request ID is deregistered from the
* ObjectStore.
*
* This PrefetchManager messes all of this up, because it uses extra threads to perform
* queries, so a thread can be waiting in the PrefetchManager for another thread to finish
* a query. The PrefetchManager maps user threads onto action threads - sometimes they will
* be the same, but other times they will not.
*
* The PrefetchManager works with PrefetchManager.Request objects, which represent a unit of
* work to perform. These can be used as request ID objects to hand to the ObjectStore. So,
* Threads should register request ID objects with this PrefetchManager, in the same way as
* with the ObjectStore. When a cancel request comes in, the PrefetchManager will then be able
* to match that against a Request object. A request ID matches a PrefetchManager.Request
* object if a Thread with that request ID is currently inside the PrefetchManager.doRequest()
* method. There are several scenarios:
* 1. The thread is doing the work itself, and it is the only one that needs that data.
* -> dead simple, just cancel the request.
* 2. The thread is doing the work itself, but there are other threads waiting for the same
* data.
* -> Policy decision to be made. Probably cancel the request, and let the other threads
* start again.
* 3. The thread is waiting for another thread to finish the work, and there are no other
* threads waiting for the data.
* -> In this case, the thread that is doing the work is guaranteed to be a prefetch
* thread, so it can just be cancelled.
* 4. The thread is waiting for another thread to finish the work, but there are other threads
* waiting for the same data. Another thread waiting for the data will be any thread that
* is inside the doRequest() method with that PrefetchManager.Request object, regardless of
* whether that thread has registered a request ID or not.
* -> The thread that is doing the work may or may not be a prefetch thread. In either
* case, the waiting thread should be kicked out of the doRequest() method without
* jeopardising the thread that is performing the work.
*/
/**
* Adds a request to the Set of pending requests, and wakes up a Thread to handle it.
*
* @param result a ResultsBatches object that is making the request
* @param batchNo the batch number to be fetched
* @param optimise true if queries should be optimised
* @param explain true if this method should explain each query first
*/
public static void addRequest(ResultsBatches result, int batchNo, boolean optimise,
boolean explain) {
Request request = new Request(result, batchNo, optimise, explain);
synchronized (sync) {
synchronized (result) {
// Synchronise on BOTH locks, so we can muck about with anything.
if (!result.batches.containsKey(new Integer(batchNo))) {
// The request has not been done.
if (!serviced.contains(request)) {
// And it isn't currently being serviced.
//if (!pending.contains(request)) {
// LOG.debug("addRequest - adding request: "
// + request);
//}
pending.add(request);
if ((pending.size() + serviced.size()) > (serviceThreads * LOADING)) {
// There are too many requests for the servicing threads.
Thread newThread = new ServiceThread();
newThread.setDaemon(true);
newThread.setName("PrefetchManager ServiceThread");
newThread.start();
serviceThreads++;
LOG.info("addRequest - creating new ServiceThread. We now have "
+ serviceThreads);
} else {
// There may or may not be a service thread waiting. If not, a service
// thread will soon finish a request.
sync.notify();
}
//} else {
//LOG.debug("addRequest - the request is currently being serviced: "
// + request);
}
//} else {
//LOG.debug("addRequest - the request has already been done: " + request);
}
}
}
}
/**
* Returns when the given request is completed. If the given request is not already being
* serviced, then this method will start servicing the request in the current thread.
*
* @param result a ResultsBatches object that is making the request
* @param batchNo the batch number to be fetched
* @return a List containing the contents of the batch
* @param optimise true if queries should be optimised
* @param explain true if this method should explain each query first
* @throws ObjectStoreException if an error occurs in the underlying ObjectStore
* @throws IndexOutOfBoundsException if the batch is off the end of the results
*/
public static List