package org.eclipse.jdt.internal.core.search.processing;
import org.eclipse.core.runtime.*;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jdt.internal.core.util.Messages;
import org.eclipse.jdt.internal.core.util.Util;
public abstract class JobManager implements Runnable {
protected IJob[] awaitingJobs = new IJob[10];
protected int jobStart = 0;
protected int jobEnd = -1;
protected boolean executing = false;
protected Thread processingThread;
protected Job progressJob;
private int enableCount = 1;
public static boolean VERBOSE = false;
public boolean activated = false;
private int awaitingClients = 0;
public void activateProcessing() {
this.activated = true;
}
public synchronized int awaitingJobsCount() {
return this.activated ? this.jobEnd - this.jobStart + 1 : 1;
}
public synchronized IJob currentJob() {
if (this.enableCount > 0 && this.jobStart <= this.jobEnd)
return this.awaitingJobs[this.jobStart];
return null;
}
public synchronized void disable() {
this.enableCount--;
if (VERBOSE)
Util.verbose("DISABLING background indexing");
}
public void discardJobs(String jobFamily) {
if (VERBOSE)
Util.verbose("DISCARD background job family - " + jobFamily);
try {
IJob currentJob;
synchronized(this){
currentJob = currentJob();
disable();
}
if (currentJob != null && (jobFamily == null || currentJob.belongsTo(jobFamily))) {
currentJob.cancel();
while (this.processingThread != null && this.executing){
try {
if (VERBOSE)
Util.verbose("-> waiting end of current background job - " + currentJob);
Thread.sleep(50);
} catch(InterruptedException e){
}
}
}
int loc = -1;
synchronized(this) {
for (int i = this.jobStart; i <= this.jobEnd; i++) {
currentJob = this.awaitingJobs[i];
if (currentJob != null) {
this.awaitingJobs[i] = null;
if (!(jobFamily == null || currentJob.belongsTo(jobFamily))) {
this.awaitingJobs[++loc] = currentJob;
} else {
if (VERBOSE)
Util.verbose("-> discarding background job - " + currentJob);
currentJob.cancel();
}
}
}
this.jobStart = 0;
this.jobEnd = loc;
}
} finally {
enable();
}
if (VERBOSE)
Util.verbose("DISCARD DONE with background job family - " + jobFamily);
}
public synchronized void enable() {
this.enableCount++;
if (VERBOSE)
Util.verbose("ENABLING background indexing");
notifyAll();
}
protected synchronized boolean isJobWaiting(IJob request) {
for (int i = this.jobEnd; i > this.jobStart; i--)
if (request.equals(this.awaitingJobs[i])) return true;
return false;
}
protected synchronized void moveToNextJob() {
if (this.jobStart <= this.jobEnd) {
this.awaitingJobs[this.jobStart++] = null;
if (this.jobStart > this.jobEnd) {
this.jobStart = 0;
this.jobEnd = -1;
}
}
}
protected void notifyIdle(long idlingTime) {
}
public boolean performConcurrentJob(IJob searchJob, int waitingPolicy, IProgressMonitor monitor) {
if (VERBOSE)
Util.verbose("STARTING concurrent job - " + searchJob);
searchJob.ensureReadyToRun();
boolean status = IJob.FAILED;
try {
SubMonitor subMonitor = SubMonitor.convert(monitor);
if (awaitingJobsCount() > 0) {
switch (waitingPolicy) {
case IJob.ForceImmediate :
if (VERBOSE)
Util.verbose("-> NOT READY - forcing immediate - " + searchJob);
try {
disable();
status = searchJob.execute(subMonitor);
} finally {
enable();
}
if (VERBOSE)
Util.verbose("FINISHED concurrent job - " + searchJob);
return status;
case IJob.CancelIfNotReady :
if (VERBOSE)
Util.verbose("-> NOT READY - cancelling - " + searchJob);
if (VERBOSE)
Util.verbose("CANCELED concurrent job - " + searchJob);
throw new OperationCanceledException();
case IJob.WaitUntilReady :
int totalWork = 1000;
SubMonitor waitMonitor = subMonitor.setWorkRemaining(10).split(8).setWorkRemaining(totalWork);
Thread t = this.processingThread;
int originalPriority = t == null ? -1 : t.getPriority();
try {
if (t != null)
t.setPriority(Thread.currentThread().getPriority());
synchronized(this) {
this.awaitingClients++;
}
IJob previousJob = null;
int awaitingJobsCount;
int lastJobsCount = totalWork;
float lastWorked = 0;
float totalWorked = 0;
while ((awaitingJobsCount = awaitingJobsCount()) > 0) {
if (waitMonitor.isCanceled() || this.processingThread == null)
throw new OperationCanceledException();
IJob currentJob = currentJob();
if (currentJob != null && currentJob != previousJob) {
if (VERBOSE)
Util.verbose("-> NOT READY - waiting until ready - " + searchJob);
String indexing = Messages.bind(Messages.jobmanager_filesToIndex, currentJob.getJobFamily(), Integer.toString(awaitingJobsCount));
waitMonitor.subTask(indexing);
float ratio = awaitingJobsCount < totalWork ? 1 : ((float) totalWork) / awaitingJobsCount;
if (lastJobsCount > awaitingJobsCount) {
totalWorked += (lastJobsCount - awaitingJobsCount) * ratio;
} else {
totalWorked += ratio;
}
if (totalWorked - lastWorked >= 1) {
waitMonitor.worked((int) (totalWorked - lastWorked));
lastWorked = totalWorked;
}
lastJobsCount = awaitingJobsCount;
previousJob = currentJob;
}
try {
if (VERBOSE)
Util.verbose("-> GOING TO SLEEP - " + searchJob);
Thread.sleep(50);
} catch (InterruptedException e) {
}
}
} finally {
synchronized(this) {
this.awaitingClients--;
}
if (t != null && originalPriority > -1 && t.isAlive())
t.setPriority(originalPriority);
}
}
}
status = searchJob.execute(subMonitor);
} finally {
SubMonitor.done(monitor);
if (VERBOSE)
Util.verbose("FINISHED concurrent job - " + searchJob);
}
return status;
}
public abstract String processName();
public synchronized void request(IJob job) {
job.ensureReadyToRun();
int size = this.awaitingJobs.length;
if (++this.jobEnd == size) {
this.jobEnd -= this.jobStart;
if (this.jobEnd < 50 && this.jobEnd < this.jobStart) {
System.arraycopy(this.awaitingJobs, this.jobStart, this.awaitingJobs, 0, this.jobEnd);
for (int i = this.jobStart; i < size; i++)
this.awaitingJobs[i] = null;
} else {
System.arraycopy(this.awaitingJobs, this.jobStart, this.awaitingJobs = new IJob[size * 2], 0, this.jobEnd);
}
this.jobStart = 0;
}
this.awaitingJobs[this.jobEnd] = job;
if (VERBOSE) {
Util.verbose("REQUEST background job - " + job);
Util.verbose("AWAITING JOBS count: " + awaitingJobsCount());
}
notifyAll();
}
public void reset() {
if (VERBOSE)
Util.verbose("Reset");
Thread thread;
synchronized (this) {
thread = this.processingThread;
}
if (thread != null) {
discardJobs(null);
} else {
synchronized (this) {
this.processingThread = new Thread(this, processName());
this.processingThread.setDaemon(true);
this.processingThread.setPriority(Thread.NORM_PRIORITY-1);
this.processingThread.setContextClassLoader(this.getClass().getClassLoader());
this.processingThread.start();
}
}
}
@Override
public void run() {
long idlingStart = -1;
activateProcessing();
try {
class ProgressJob extends Job {
ProgressJob(String name) {
super(name);
}
@Override
protected IStatus run(IProgressMonitor monitor) {
IJob job = currentJob();
while (!monitor.isCanceled() && job != null) {
String taskName = new StringBuffer(Messages.jobmanager_indexing)
.append(Messages.bind(Messages.jobmanager_filesToIndex, job.getJobFamily(), Integer.toString(awaitingJobsCount())))
.toString();
monitor.subTask(taskName);
setName(taskName);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
job = currentJob();
}
return Status.OK_STATUS;
}
}
this.progressJob = null;
while (this.processingThread != null) {
try {
IJob job;
synchronized (this) {
if (this.processingThread == null) continue;
if ((job = currentJob()) == null) {
if (this.progressJob != null) {
this.progressJob.cancel();
this.progressJob = null;
}
if (idlingStart < 0)
idlingStart = System.currentTimeMillis();
else
notifyIdle(System.currentTimeMillis() - idlingStart);
this.wait();
} else {
idlingStart = -1;
}
}
if (job == null) {
notifyIdle(System.currentTimeMillis() - idlingStart);
Thread.sleep(500);
continue;
}
if (VERBOSE) {
Util.verbose(awaitingJobsCount() + " awaiting jobs");
Util.verbose("STARTING background job - " + job);
}
try {
this.executing = true;
if (this.progressJob == null) {
this.progressJob = new ProgressJob(Messages.bind(Messages.jobmanager_indexing, "", ""));
this.progressJob.setPriority(Job.LONG);
this.progressJob.setSystem(true);
this.progressJob.schedule();
}
job.execute(null);
} finally {
this.executing = false;
if (VERBOSE)
Util.verbose("FINISHED background job - " + job);
moveToNextJob();
if (this.awaitingClients == 0 && job.waitNeeded()) {
if (VERBOSE) {
Util.verbose("WAITING after job - " + job);
}
Thread.sleep(5);
}
}
} catch (InterruptedException e) {
}
}
} catch (RuntimeException e) {
if (this.processingThread != null) {
Util.log(e, "Background Indexer Crash Recovery");
discardJobs(null);
this.processingThread = null;
reset();
}
throw e;
} catch (Error e) {
if (this.processingThread != null && !(e instanceof ThreadDeath)) {
Util.log(e, "Background Indexer Crash Recovery");
discardJobs(null);
this.processingThread = null;
reset();
}
throw e;
}
}
public void shutdown() {
if (VERBOSE)
Util.verbose("Shutdown");
disable();
discardJobs(null);
Thread thread = this.processingThread;
try {
if (thread != null) {
synchronized (this) {
this.processingThread = null;
notifyAll();
}
thread.join();
}
Job job = this.progressJob;
if (job != null) {
job.cancel();
job.join();
}
} catch (InterruptedException e) {
}
}
@Override
public String toString() {
StringBuffer buffer = new StringBuffer(10);
buffer.append("Enable count:").append(this.enableCount).append('\n');
int numJobs = this.jobEnd - this.jobStart + 1;
buffer.append("Jobs in queue:").append(numJobs).append('\n');
for (int i = 0; i < numJobs && i < 15; i++) {
buffer.append(i).append(" - job["+i+"]: ").append(this.awaitingJobs[this.jobStart+i]).append('\n');
}
return buffer.toString();
}
}