Merge "Omaha #3668 Give grid a custom job pool." into omaha_14.4.1

Former-commit-id: e83c1f20bb [formerly 199ccf1aba [formerly 5e22943a26] [formerly e83c1f20bb [formerly b98016900319d31c23be96640aa97f7b2efcee5b]]]
Former-commit-id: 199ccf1aba [formerly 5e22943a26]
Former-commit-id: 199ccf1aba
Former-commit-id: 69e1cb79b2
This commit is contained in:
Nate Jensen 2014-10-30 09:32:12 -05:00 committed by Gerrit Code Review
commit 2464d4bdaf
2 changed files with 165 additions and 48 deletions

View file

@ -0,0 +1,128 @@
/**
* This software was developed and / or modified by Raytheon Company,
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
*
* U.S. EXPORT CONTROLLED TECHNICAL DATA
* This software product contains export-restricted data whose
* export/transfer/disclosure is restricted by U.S. law. Dissemination
* to non-U.S. persons whether in the United States or abroad requires
* an export license or other authorization.
*
* Contractor Name: Raytheon Company
* Contractor Address: 6825 Pine Street, Suite 340
* Mail Stop B8
* Omaha, NE 68106
* 402.291.0100
*
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
* further licensing information.
**/
package com.raytheon.viz.grid.rsc.general;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
/**
* A pool of {@link Job} that executes {@link GridDataRequestRunner}s with a
* scheduling pattern that is ideal for smooth user interaction. This object
* expects {@link #schedule(GridDataRequestRunner)} to be called each time data
* is needed that is not yet available(primarily when the frame is displayed).
* Calling {@link #schedule(GridDataRequestRunner)} causes the runner to be
* bumped to the front of the queue so that data will be ready as soon as
* possible. This causes runners that are not being displayed to frequently get
* bumped to the back of the queue, which is desired since the user does not
* need that data yet.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------- -------- ----------- --------------------------
* Oct 29, 2014 3668 bsteffen Initial creation
*
* </pre>
*
* @author bsteffen
* @version 1.0
*/
public class GridDataRequestJobPool {
private static final int POOL_SIZE = Integer.getInteger(
"grid.request.pool.size", 10);
private static final GridDataRequestJobPool instance = new GridDataRequestJobPool();
public static void schedule(GridDataRequestRunner runner) {
instance.scheduleRunner(runner);
}
protected LinkedBlockingQueue<Job> jobQueue = new LinkedBlockingQueue<Job>();
protected List<GridDataRequestRunner> runners = new LinkedList<>();
private GridDataRequestJobPool(){
for (int i = 0; i <POOL_SIZE; i++) {
jobQueue.add(new GridDataRequestJob());
}
}
protected void scheduleRunner(GridDataRequestRunner runner) {
synchronized (runners) {
if (runners.isEmpty() || !(runners.get(0) == runner)) {
runners.remove(runner);
runners.add(0, runner);
}
}
Job job = jobQueue.poll();
if (job != null) {
job.schedule();
}
}
protected void reschedule(GridDataRequestRunner runner){
synchronized (runners) {
if(!runners.contains(runner)){
runners.add(runner);
}
}
}
protected GridDataRequestRunner getNextRunner(){
synchronized (runners) {
if(runners.isEmpty()){
return null;
}else{
return runners.remove(0);
}
}
}
private class GridDataRequestJob extends Job{
public GridDataRequestJob() {
super("Requesting Grid Data");
}
@Override
protected IStatus run(IProgressMonitor monitor) {
jobQueue.offer(this);
GridDataRequestRunner runner = getNextRunner();
while(runner != null){
if(runner.processOneRequest()){
reschedule(runner);
}
runner = getNextRunner();
}
return Status.OK_STATUS;
}
}
}

View file

@ -29,11 +29,10 @@ import com.raytheon.uf.common.status.UFStatus;
import com.raytheon.uf.common.status.UFStatus.Priority;
import com.raytheon.uf.common.time.DataTime;
import com.raytheon.uf.viz.core.exception.VizException;
import com.raytheon.uf.viz.core.jobs.JobPool;
/**
*
* Manages asynchronously requesting data for GridResources.
* Manages asynchronously requesting data for {@link AbstractGridResource}s.
*
* <pre>
*
@ -47,21 +46,15 @@ import com.raytheon.uf.viz.core.jobs.JobPool;
* Jun 24, 2013 2140 randerso Moved safe name code into AbstractVizResource
* Oct 07, 2014 3668 bclement uses executor instead of eclipse job
* renamed to GridDataRequestRunner
* Oct 23, 2014 3668 bsteffen replace executor with job pool so user
* sees progress.
* Oct 29, 2014 3668 bsteffen replace executor with custom job pool.
*
* </pre>
*
* @author bsteffen
* @version 1.0
* @see GridDataRequestJobPool
*/
class GridDataRequestRunner implements Runnable {
private static final int POOL_SIZE = Integer.getInteger(
"grid.request.pool.size", 10);
private static final JobPool jobPool = new JobPool("Requesting Grid Data",
POOL_SIZE);
class GridDataRequestRunner {
private static final transient IUFStatusHandler statusHandler = UFStatus
.getHandler(GridDataRequestRunner.class);
@ -80,6 +73,8 @@ class GridDataRequestRunner implements Runnable {
// been notified.)
public boolean exceptionHandled = false;
public boolean isRunning = false;
public GridDataRequest(DataTime time, List<PluginDataObject> pdos) {
this.time = time;
if (pdos == null) {
@ -91,21 +86,12 @@ class GridDataRequestRunner implements Runnable {
}
public boolean shouldRequest() {
return (gridData == null) && (exception == null);
return (gridData == null) && (exception == null)
&& (isRunning == false);
}
}
/**
* This class is not designed to handle multiple requests concurrently. To
* ensure this doesn't happen we track when it is scheduled and do not
* schedule again. It would have been simpler to synchronize the run method
* but that ties up threads from the pool that other resources should use.
* So we don't leave dangling requests this should only be modified while
* synchronized on requests.
*/
private volatile boolean scheduled = false;
private AbstractGridResource<?> resource;
private List<GridDataRequest> requests = new ArrayList<GridDataRequest>();
@ -114,42 +100,49 @@ class GridDataRequestRunner implements Runnable {
this.resource = resource;
}
@Override
public void run() {
for (GridDataRequest request = getNext(); request != null; request = getNext()) {
try {
request.gridData = resource.getData(request.time, request.pdos);
if (request.gridData == null) {
/*
* need to remove unfulfillable requests to avoid infinite
* loop.
*/
synchronized (requests) {
requests.remove(request);
}
} else {
resource.issueRefresh();
/**
* Attempt to process a request if there are any that need to be processed
*
* @return true if a request was processed or false if no requests need to
* be processed.
*/
public boolean processOneRequest() {
GridDataRequest request = getNext();
if (request == null) {
return false;
}
try {
request.gridData = resource.getData(request.time, request.pdos);
if (request.gridData == null) {
/* need to remove unfulfillable requests to avoid infinite loop. */
synchronized (requests) {
requests.remove(request);
}
} catch (VizException e) {
request.exception = e;
} else {
resource.issueRefresh();
}
} catch (VizException e) {
request.exception = e;
resource.issueRefresh();
} finally {
request.isRunning = false;
}
return true;
}
/**
* Get the next request that should be sent
* Get the next request that should be processed
*
* @return null if no request should be sent
* @return null if no request should be processed
*/
protected GridDataRequest getNext() {
synchronized (requests) {
for (GridDataRequest request : requests) {
if (request.shouldRequest()) {
request.isRunning = true;
return request;
}
}
scheduled = false;
}
return null;
}
@ -181,9 +174,8 @@ class GridDataRequestRunner implements Runnable {
if ((request.exception != null) && !request.exceptionHandled) {
handleExceptions();
}
if (!scheduled && request.shouldRequest()) {
scheduled = true;
jobPool.schedule(this);
if (request.shouldRequest()) {
GridDataRequestJobPool.schedule(this);
}
}
return null;
@ -268,9 +260,6 @@ class GridDataRequestRunner implements Runnable {
public void stopAndClear() {
synchronized (requests) {
requests.clear();
if (jobPool.cancel(this)) {
scheduled = false;
}
}
}