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
/******************************************************************************* * 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 *******************************************************************************/
package org.eclipse.jdt.internal.compiler; import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; import org.eclipse.jdt.internal.compiler.util.Messages; 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; 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$ 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() { 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 | 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 } } }