blob: a404b7294f8f40f0b85adbfaee44973abcc345a2 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008, 2016 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
* Technical University Berlin - extended API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.compiler;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.util.Messages;
import org.eclipse.objectteams.otdt.internal.core.compiler.control.Config;
public class ProcessTaskManager implements Runnable {
Compiler compiler;
private int unitIndex;
private Thread processingThread;
CompilationUnitDeclaration unitToProcess;
private Throwable caughtException;
// queue
volatile int currentIndex, availableIndex, size, sleepCount;
CompilationUnitDeclaration[] units;
public static final int PROCESSED_QUEUE_SIZE = 12;
//{ObjectTeams:
private Config sharedConfig;
/* This method is also a hook for jdt.ui.adaptor's ProjectWatcher. */
private void setConfig(Config config) {
this.sharedConfig= config;
}
// SH}
public ProcessTaskManager(Compiler compiler, int startingIndex) {
this.compiler = compiler;
this.unitIndex = startingIndex;
this.currentIndex = 0;
this.availableIndex = 0;
this.size = PROCESSED_QUEUE_SIZE;
this.sleepCount = 0; // 0 is no one, +1 is the processing thread & -1 is the writing/main thread
this.units = new CompilationUnitDeclaration[this.size];
synchronized (this) {
this.processingThread = new Thread(this, "Compiler Processing Task"); //$NON-NLS-1$
//{ObjectTeams:
setConfig(Config.getConfig());
// SH}
this.processingThread.setDaemon(true);
this.processingThread.start();
}
}
// add unit to the queue - wait if no space is available
private synchronized void addNextUnit(CompilationUnitDeclaration newElement) {
while (this.units[this.availableIndex] != null) {
//System.out.print('a');
//if (this.sleepCount < 0) throw new IllegalStateException(Integer.valueOf(this.sleepCount).toString());
this.sleepCount = 1;
try {
wait(250);
} catch (InterruptedException ignore) {
// ignore
}
this.sleepCount = 0;
}
this.units[this.availableIndex++] = newElement;
if (this.availableIndex >= this.size)
this.availableIndex = 0;
if (this.sleepCount <= -1)
notify(); // wake up writing thread to accept next unit - could be the last one - must avoid deadlock
}
public CompilationUnitDeclaration removeNextUnit() throws Error {
CompilationUnitDeclaration next = null;
boolean yield = false;
synchronized (this) {
next = this.units[this.currentIndex];
if (next == null || this.caughtException != null) {
do {
if (this.processingThread == null) {
if (this.caughtException != null) {
// rethrow the caught exception from the processingThread in the main compiler thread
if (this.caughtException instanceof Error)
throw (Error) this.caughtException;
throw (RuntimeException) this.caughtException;
}
return null;
}
//System.out.print('r');
//if (this.sleepCount > 0) throw new IllegalStateException(Integer.valueOf(this.sleepCount).toString());
this.sleepCount = -1;
try {
wait(100);
} catch (InterruptedException ignore) {
// ignore
}
this.sleepCount = 0;
next = this.units[this.currentIndex];
} while (next == null);
}
this.units[this.currentIndex++] = null;
if (this.currentIndex >= this.size)
this.currentIndex = 0;
if (this.sleepCount >= 1 && ++this.sleepCount > 4) {
notify(); // wake up processing thread to add next unit but only after removing some elements first
yield = this.sleepCount > 8;
}
}
if (yield)
Thread.yield();
return next;
}
@Override
public void run() {
//{ObjectTeams:
Config.addConfig(this.sharedConfig);
// SH}
boolean noAnnotations = this.compiler.annotationProcessorManager == null;
while (this.processingThread != null) {
this.unitToProcess = null;
int index = -1;
boolean cleanup = noAnnotations || this.compiler.shouldCleanup(this.unitIndex);
try {
synchronized (this) {
if (this.processingThread == null) return;
this.unitToProcess = this.compiler.getUnitToProcess(this.unitIndex);
if (this.unitToProcess == null) {
this.processingThread = null;
return;
}
index = this.unitIndex++;
if (this.unitToProcess.compilationResult.hasBeenAccepted)
continue;
}
try {
this.compiler.reportProgress(Messages.bind(Messages.compilation_processing, new String(this.unitToProcess.getFileName())));
if (this.compiler.options.verbose)
this.compiler.out.println(
Messages.bind(Messages.compilation_process,
new String[] {
String.valueOf(index + 1),
String.valueOf(this.compiler.totalUnits),
new String(this.unitToProcess.getFileName())
}));
this.compiler.process(this.unitToProcess, index);
} finally {
// cleanup compilation unit result, but only if not annotation processed.
if (this.unitToProcess != null && cleanup)
this.unitToProcess.cleanUp();
}
addNextUnit(this.unitToProcess);
} catch (Error e) {
synchronized (this) {
this.processingThread = null;
this.caughtException = e;
}
return;
} catch (RuntimeException e) {
synchronized (this) {
this.processingThread = null;
this.caughtException = e;
}
return;
}
}
}
public void shutdown() {
try {
Thread t = null;
synchronized (this) {
if (this.processingThread != null) {
t = this.processingThread;
this.processingThread = null;
notifyAll();
}
}
if (t != null)
t.join(250); // do not wait forever
} catch (InterruptedException ignored) {
// ignore
}
}
}