| /******************************************************************************* |
| * 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 |
| } |
| } |
| } |