org.eclipse.jdt.internal.compiler.ReadManager Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ecj Show documentation
Show all versions of ecj Show documentation
Eclipse Compiler for Java(TM)
/*******************************************************************************
* Copyright (c) 2008, 2013 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
*******************************************************************************/
package org.eclipse.jdt.internal.compiler;
import java.lang.reflect.InvocationTargetException;
import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
@SuppressWarnings({"rawtypes", "unchecked"})
public class ReadManager implements Runnable {
ICompilationUnit[] units;
int nextFileToRead;
ICompilationUnit[] filesRead;
char[][] contentsRead;
int readyToReadPosition;
int nextAvailablePosition;
Thread[] readingThreads;
char[] readInProcessMarker = new char[0];
int sleepingThreadCount;
private Throwable caughtException;
static final int START_CUSHION = 5;
public static final int THRESHOLD = 10;
static final int CACHE_SIZE = 15; // do not waste memory by keeping too many files in memory
public ReadManager(ICompilationUnit[] files, int length) {
// start the background threads to read the file's contents
int threadCount = 0;
try {
Class runtime = Class.forName("java.lang.Runtime"); //$NON-NLS-1$
java.lang.reflect.Method m = runtime.getDeclaredMethod("availableProcessors", new Class[0]); //$NON-NLS-1$
if (m != null) {
Integer result = (Integer) m.invoke(Runtime.getRuntime(), (Object[]) null);
threadCount = result.intValue() + 1;
if (threadCount < 2)
threadCount = 0;
else if (threadCount > CACHE_SIZE)
threadCount = CACHE_SIZE;
}
} catch (IllegalAccessException ignored) { // ignored
} catch (ClassNotFoundException e) { // ignored
} catch (SecurityException e) { // ignored
} catch (NoSuchMethodException e) { // ignored
} catch (IllegalArgumentException e) { // ignored
} catch (InvocationTargetException e) { // ignored
}
if (threadCount > 0) {
synchronized (this) {
this.units = new ICompilationUnit[length];
System.arraycopy(files, 0, this.units, 0, length);
this.nextFileToRead = START_CUSHION; // skip some files to reduce the number of times we have to wait
this.filesRead = new ICompilationUnit[CACHE_SIZE];
this.contentsRead = new char[CACHE_SIZE][];
this.readyToReadPosition = 0;
this.nextAvailablePosition = 0;
this.sleepingThreadCount = 0;
this.readingThreads = new Thread[threadCount];
for (int i = threadCount; --i >= 0;) {
this.readingThreads[i] = new Thread(this, "Compiler Source File Reader"); //$NON-NLS-1$
this.readingThreads[i].setDaemon(true);
this.readingThreads[i].start();
}
}
}
}
public char[] getContents(ICompilationUnit unit) throws Error {
Thread[] rThreads = this.readingThreads;
if (rThreads == null || this.units.length == 0) {
if (this.caughtException != null) {
// rethrow the caught exception from the readingThreads in the main compiler thread
if (this.caughtException instanceof Error)
throw (Error) this.caughtException;
throw (RuntimeException) this.caughtException;
}
return unit.getContents();
}
boolean yield = this.sleepingThreadCount == rThreads.length;
char[] result = null;
synchronized (this) {
if (unit == this.filesRead[this.readyToReadPosition]) {
result = this.contentsRead[this.readyToReadPosition];
while (result == this.readInProcessMarker || result == null) {
// let the readingThread know we're waiting
//System.out.print('|');
this.contentsRead[this.readyToReadPosition] = null;
try {
wait(250);
} catch (InterruptedException ignore) { // ignore
}
if (this.caughtException != null) {
// rethrow the caught exception from the readingThreads in the main compiler thread
if (this.caughtException instanceof Error)
throw (Error) this.caughtException;
throw (RuntimeException) this.caughtException;
}
result = this.contentsRead[this.readyToReadPosition];
}
// free spot for next file
this.filesRead[this.readyToReadPosition] = null;
this.contentsRead[this.readyToReadPosition] = null;
if (++this.readyToReadPosition >= this.contentsRead.length)
this.readyToReadPosition = 0;
if (this.sleepingThreadCount > 0) {
//System.out.print('+');
//System.out.print(this.nextFileToRead);
notify();
}
} else {
// must make sure we're reading ahead of the unit
int unitIndex = 0;
for (int l = this.units.length; unitIndex < l; unitIndex++)
if (this.units[unitIndex] == unit) break;
if (unitIndex == this.units.length) {
// attempting to read a unit that was not included in the initial files - should not happen
this.units = new ICompilationUnit[0]; // stop looking for more
} else if (unitIndex >= this.nextFileToRead) {
// start over
//System.out.println(unitIndex + " vs " + this.nextFileToRead);
this.nextFileToRead = unitIndex + START_CUSHION;
this.readyToReadPosition = 0;
this.nextAvailablePosition = 0;
this.filesRead = new ICompilationUnit[CACHE_SIZE];
this.contentsRead = new char[CACHE_SIZE][];
notifyAll();
}
}
}
if (yield)
Thread.yield(); // ensure other threads get a chance
if (result != null)
return result;
//System.out.print('-');
return unit.getContents();
}
@Override
public void run() {
try {
while (this.readingThreads != null && this.nextFileToRead < this.units.length) {
ICompilationUnit unit = null;
int position = -1;
synchronized (this) {
if (this.readingThreads == null) return;
while (this.filesRead[this.nextAvailablePosition] != null) {
this.sleepingThreadCount++;
try {
wait(250); // wait until a spot in contents is available
} catch (InterruptedException e) { // ignore
}
this.sleepingThreadCount--;
if (this.readingThreads == null) return;
}
if (this.nextFileToRead >= this.units.length) return;
unit = this.units[this.nextFileToRead++];
position = this.nextAvailablePosition;
if (++this.nextAvailablePosition >= this.contentsRead.length)
this.nextAvailablePosition = 0;
this.filesRead[position] = unit;
this.contentsRead[position] = this.readInProcessMarker; // mark the spot so we know its being read
}
char[] result = unit.getContents();
synchronized (this) {
if (this.filesRead[position] == unit) {
if (this.contentsRead[position] == null) // wake up main thread which is waiting for this file
notifyAll();
this.contentsRead[position] = result;
}
}
}
} catch (Error e) {
synchronized (this) {
this.caughtException = e;
shutdown();
}
return;
} catch (RuntimeException e) {
synchronized (this) {
this.caughtException = e;
shutdown();
}
return;
}
}
public synchronized void shutdown() {
this.readingThreads = null; // mark the read manager as shutting down so that the reading threads stop
notifyAll();
}
}