From b6c9c9f18e466d684cda172574363902ce8f26fd Mon Sep 17 00:00:00 2001 From: Martin Wittlinger Date: Fri, 28 Feb 2020 17:16:56 +0100 Subject: [PATCH 01/11] add first try for parallel processor --- .../processing/AbstractParallelProcessor.java | 55 ++++++++++++++ .../processors/ParallelProcessorTest.java | 71 +++++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 src/main/java/spoon/processing/AbstractParallelProcessor.java create mode 100644 src/test/java/spoon/test/processing/processors/ParallelProcessorTest.java diff --git a/src/main/java/spoon/processing/AbstractParallelProcessor.java b/src/main/java/spoon/processing/AbstractParallelProcessor.java new file mode 100644 index 00000000000..dfb5dc7edf6 --- /dev/null +++ b/src/main/java/spoon/processing/AbstractParallelProcessor.java @@ -0,0 +1,55 @@ +/** + * Copyright (C) 2006-2019 INRIA and contributors + * + * Spoon is available either under the terms of the MIT License (see LICENSE-MIT.txt) of the Cecill-C License (see LICENSE-CECILL-C.txt). You as the user are entitled to choose the terms under which to adopt Spoon. + */ +package spoon.processing; + +import java.util.Collection; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import spoon.reflect.declaration.CtElement; + +/** + * AbstractParallelProcessor + */ +public abstract class AbstractParallelProcessor extends AbstractProcessor { + + private ExecutorService service; + private ArrayBlockingQueue> processorQueue; + + public AbstractParallelProcessor(Collection> processors) { + processorQueue = new ArrayBlockingQueue<>(processors.size()); + processorQueue.addAll(processors); + service = Executors.newFixedThreadPool(processors.size()); + } + + @Override + public final void process(E element) { + try { + Processor usedProcessor = processorQueue.take(); + service.execute(() -> { + try { + usedProcessor.process(element); + processorQueue.add(usedProcessor); + } catch (Exception e) { + // allows throwing exception, but keeping the processor in the queue + processorQueue.add(usedProcessor); + throw e; + } + }); + } catch (InterruptedException e) { + // because rethrow is not possible here. + Thread.currentThread().interrupt(); + e.printStackTrace(); + } + } + + @Override + public void processingDone() { + service.shutdown(); + super.processingDone(); + } +} diff --git a/src/test/java/spoon/test/processing/processors/ParallelProcessorTest.java b/src/test/java/spoon/test/processing/processors/ParallelProcessorTest.java new file mode 100644 index 00000000000..05bca841fe3 --- /dev/null +++ b/src/test/java/spoon/test/processing/processors/ParallelProcessorTest.java @@ -0,0 +1,71 @@ +/** + * Copyright (C) 2006-2019 INRIA and contributors + * + * Spoon is available either under the terms of the MIT License (see LICENSE-MIT.txt) of the Cecill-C License (see LICENSE-CECILL-C.txt). You as the user are entitled to choose the terms under which to adopt Spoon. + */ +package spoon.test.processing.processors; + +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicReferenceArray; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import spoon.FluentLauncher; +import spoon.processing.AbstractParallelProcessor; +import spoon.processing.AbstractProcessor; +import spoon.processing.Processor; +import spoon.reflect.declaration.CtElement; + +/** + * ParallelProcessorTest + */ +public class ParallelProcessorTest { + @Rule + public TemporaryFolder folderFactory = new TemporaryFolder(); + + @Test + public void printWithThreadNumber() throws IOException { + Integer[] counter = new Integer[] { 0, 0, 0, 0 }; + AtomicReferenceArray atomicCounter = new AtomicReferenceArray(counter); + Processor p1 = new AbstractProcessor() { + @Override + public void process(CtElement element) { + atomicCounter.getAndUpdate(0, i -> i + 1); + } + }; + Processor p2 = new AbstractProcessor() { + @Override + public void process(CtElement element) { + atomicCounter.getAndUpdate(1, i -> i + 1); + } + }; + Processor p3 = new AbstractProcessor() { + @Override + public void process(CtElement element) { + atomicCounter.getAndUpdate(2, i -> i + 1); + } + }; + Processor p4 = new AbstractProcessor() { + @Override + public void process(CtElement element) { + atomicCounter.getAndUpdate(3, i -> i + 1); + } + }; + new FluentLauncher().inputResource("src/test/resources/deprecated/input") + .processor(new AbstractParallelProcessor(Arrays.asList(p1, p2, p3, p4)) { + }) + .noClasspath(true) + .outputDirectory(folderFactory.newFolder()) + .buildModel(); + System.out.println(atomicCounter.toString()); + for (int j = 0; j < atomicCounter.length(); j++) { + // after processing every entry should be > 0. + assertTrue(atomicCounter.get(j) > 0); + } + } +} From 87fc7bc585274d9a3073b0ac7dcd7e00f531b4f4 Mon Sep 17 00:00:00 2001 From: Martin Wittlinger Date: Fri, 28 Feb 2020 17:51:47 +0100 Subject: [PATCH 02/11] change add to put --- .../java/spoon/processing/AbstractParallelProcessor.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/spoon/processing/AbstractParallelProcessor.java b/src/main/java/spoon/processing/AbstractParallelProcessor.java index dfb5dc7edf6..17aa22389bc 100644 --- a/src/main/java/spoon/processing/AbstractParallelProcessor.java +++ b/src/main/java/spoon/processing/AbstractParallelProcessor.java @@ -33,6 +33,11 @@ public final void process(E element) { service.execute(() -> { try { usedProcessor.process(element); + processorQueue.put(usedProcessor); + } catch (InterruptedException e) { + // because rethrow is not possible here. + Thread.currentThread().interrupt(); + e.printStackTrace(); processorQueue.add(usedProcessor); } catch (Exception e) { // allows throwing exception, but keeping the processor in the queue From 16e8db1b1ac0cbba9e175fe7826e41c4adfdae15 Mon Sep 17 00:00:00 2001 From: Martin Wittlinger Date: Fri, 28 Feb 2020 18:22:00 +0100 Subject: [PATCH 03/11] change test --- .../processors/ParallelProcessorTest.java | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/test/java/spoon/test/processing/processors/ParallelProcessorTest.java b/src/test/java/spoon/test/processing/processors/ParallelProcessorTest.java index 05bca841fe3..8b7e02c4a88 100644 --- a/src/test/java/spoon/test/processing/processors/ParallelProcessorTest.java +++ b/src/test/java/spoon/test/processing/processors/ParallelProcessorTest.java @@ -9,6 +9,7 @@ import java.io.IOException; import java.util.Arrays; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReferenceArray; import org.junit.Rule; @@ -62,10 +63,20 @@ public void process(CtElement element) { .noClasspath(true) .outputDirectory(folderFactory.newFolder()) .buildModel(); - System.out.println(atomicCounter.toString()); + AtomicInteger singleThreadCounter = new AtomicInteger(0); + new FluentLauncher().inputResource("src/test/resources/deprecated/input") + .processor(new AbstractProcessor() { + @Override + public void process(CtElement element) { + singleThreadCounter.incrementAndGet(); + } + }) + .noClasspath(true) + .outputDirectory(folderFactory.newFolder()) + .buildModel(); for (int j = 0; j < atomicCounter.length(); j++) { - // after processing every entry should be > 0. - assertTrue(atomicCounter.get(j) > 0); + singleThreadCounter.set(singleThreadCounter.get() - atomicCounter.get(j)); } + assertTrue(singleThreadCounter.get() == 0); } } From 600806248f3c6d93a3705a47e4804ab732375a0f Mon Sep 17 00:00:00 2001 From: Martin Wittlinger Date: Fri, 28 Feb 2020 19:31:41 +0100 Subject: [PATCH 04/11] add new constructor accepting a consumer and a number --- .../processing/AbstractParallelProcessor.java | 14 +++++ .../processors/ParallelProcessorTest.java | 63 ++++++++++++++++++- 2 files changed, 76 insertions(+), 1 deletion(-) diff --git a/src/main/java/spoon/processing/AbstractParallelProcessor.java b/src/main/java/spoon/processing/AbstractParallelProcessor.java index 17aa22389bc..4de9c5f91b0 100644 --- a/src/main/java/spoon/processing/AbstractParallelProcessor.java +++ b/src/main/java/spoon/processing/AbstractParallelProcessor.java @@ -9,6 +9,7 @@ import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.function.Consumer; import spoon.reflect.declaration.CtElement; @@ -26,6 +27,19 @@ public AbstractParallelProcessor(Collection> processors) { service = Executors.newFixedThreadPool(processors.size()); } + public AbstractParallelProcessor(Consumer processFunction, int numberOfProcessors) { + processorQueue = new ArrayBlockingQueue<>(numberOfProcessors); + for (int i = 0; i < numberOfProcessors; i++) { + processorQueue.add(new AbstractProcessor() { + @Override + public void process(E element) { + processFunction.accept(element); + } + }); + } + service = Executors.newFixedThreadPool(numberOfProcessors); + } + @Override public final void process(E element) { try { diff --git a/src/test/java/spoon/test/processing/processors/ParallelProcessorTest.java b/src/test/java/spoon/test/processing/processors/ParallelProcessorTest.java index 8b7e02c4a88..20d27fd50ba 100644 --- a/src/test/java/spoon/test/processing/processors/ParallelProcessorTest.java +++ b/src/test/java/spoon/test/processing/processors/ParallelProcessorTest.java @@ -30,7 +30,7 @@ public class ParallelProcessorTest { public TemporaryFolder folderFactory = new TemporaryFolder(); @Test - public void printWithThreadNumber() throws IOException { + public void compareWithSingleThreaded1() throws IOException { Integer[] counter = new Integer[] { 0, 0, 0, 0 }; AtomicReferenceArray atomicCounter = new AtomicReferenceArray(counter); Processor p1 = new AbstractProcessor() { @@ -79,4 +79,65 @@ public void process(CtElement element) { } assertTrue(singleThreadCounter.get() == 0); } + + @Test + public void compareWithSingleThreaded2() throws IOException { + Integer[] counter = new Integer[] { 0, 0, 0, 0 }; + AtomicReferenceArray atomicCounter = new AtomicReferenceArray(counter); + Processor p1 = new AbstractProcessor() { + @Override + public void process(CtElement element) { + atomicCounter.getAndUpdate(0, i -> i + 1); + } + }; + new FluentLauncher().inputResource("src/test/resources/deprecated/input") + .processor(new AbstractParallelProcessor(Arrays.asList(p1)) { + }) + .noClasspath(true) + .outputDirectory(folderFactory.newFolder()) + .buildModel(); + AtomicInteger singleThreadCounter = new AtomicInteger(0); + new FluentLauncher().inputResource("src/test/resources/deprecated/input") + .processor(new AbstractProcessor() { + @Override + public void process(CtElement element) { + singleThreadCounter.incrementAndGet(); + } + }) + .noClasspath(true) + .outputDirectory(folderFactory.newFolder()) + .buildModel(); + for (int j = 0; j < atomicCounter.length(); j++) { + singleThreadCounter.set(singleThreadCounter.get() - atomicCounter.get(j)); + } + assertTrue(singleThreadCounter.get() == 0); + } + + @Test + public void consumerConstructorTest() throws IOException { + Integer[] counter = new Integer[] { 0, 0, 0, 0 }; + AtomicReferenceArray atomicCounter = new AtomicReferenceArray(counter); + new FluentLauncher().inputResource("src/test/resources/deprecated/input") + .processor( + new AbstractParallelProcessor((e) -> atomicCounter.getAndUpdate(0, i -> i + 1), 4) { + }) + .noClasspath(true) + .outputDirectory(folderFactory.newFolder()) + .buildModel(); + AtomicInteger singleThreadCounter = new AtomicInteger(0); + new FluentLauncher().inputResource("src/test/resources/deprecated/input") + .processor(new AbstractProcessor() { + @Override + public void process(CtElement element) { + singleThreadCounter.incrementAndGet(); + } + }) + .noClasspath(true) + .outputDirectory(folderFactory.newFolder()) + .buildModel(); + for (int j = 0; j < atomicCounter.length(); j++) { + singleThreadCounter.set(singleThreadCounter.get() - atomicCounter.get(j)); + } + assertTrue(singleThreadCounter.get() == 0); + } } From 6584fc15faf35e03a0004b174e226b94cd23045e Mon Sep 17 00:00:00 2001 From: Martin Wittlinger Date: Sun, 1 Mar 2020 13:21:15 +0100 Subject: [PATCH 05/11] add check if some processors are same --- .../processing/AbstractParallelProcessor.java | 24 +++++++++++++++---- .../processors/ParallelProcessorTest.java | 21 ++++++++++++++++ 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/src/main/java/spoon/processing/AbstractParallelProcessor.java b/src/main/java/spoon/processing/AbstractParallelProcessor.java index 4de9c5f91b0..2dd23c04041 100644 --- a/src/main/java/spoon/processing/AbstractParallelProcessor.java +++ b/src/main/java/spoon/processing/AbstractParallelProcessor.java @@ -11,6 +11,7 @@ import java.util.concurrent.Executors; import java.util.function.Consumer; +import spoon.SpoonException; import spoon.reflect.declaration.CtElement; /** @@ -22,11 +23,24 @@ public abstract class AbstractParallelProcessor extends Abs private ArrayBlockingQueue> processorQueue; public AbstractParallelProcessor(Collection> processors) { + long distinctNumber = processors.stream().distinct().count(); + if (distinctNumber == 0) { + throw new SpoonException("List of processors is empty"); + } + if (distinctNumber != processors.size()) { + throw new SpoonException("Some processors are the same"); + } processorQueue = new ArrayBlockingQueue<>(processors.size()); processorQueue.addAll(processors); service = Executors.newFixedThreadPool(processors.size()); } + /** + * + * @param processFunction + * @param numberOfProcessors + * @throws IllegalArgumentException if numberOfProcessors is less than 1 + */ public AbstractParallelProcessor(Consumer processFunction, int numberOfProcessors) { processorQueue = new ArrayBlockingQueue<>(numberOfProcessors); for (int i = 0; i < numberOfProcessors; i++) { @@ -43,19 +57,19 @@ public void process(E element) { @Override public final void process(E element) { try { - Processor usedProcessor = processorQueue.take(); + Processor currentProcessor = processorQueue.take(); service.execute(() -> { try { - usedProcessor.process(element); - processorQueue.put(usedProcessor); + currentProcessor.process(element); + processorQueue.put(currentProcessor); } catch (InterruptedException e) { // because rethrow is not possible here. Thread.currentThread().interrupt(); e.printStackTrace(); - processorQueue.add(usedProcessor); + processorQueue.add(currentProcessor); } catch (Exception e) { // allows throwing exception, but keeping the processor in the queue - processorQueue.add(usedProcessor); + processorQueue.add(currentProcessor); throw e; } }); diff --git a/src/test/java/spoon/test/processing/processors/ParallelProcessorTest.java b/src/test/java/spoon/test/processing/processors/ParallelProcessorTest.java index 20d27fd50ba..88a745a64a1 100644 --- a/src/test/java/spoon/test/processing/processors/ParallelProcessorTest.java +++ b/src/test/java/spoon/test/processing/processors/ParallelProcessorTest.java @@ -6,6 +6,7 @@ package spoon.test.processing.processors; import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertThrows; import java.io.IOException; import java.util.Arrays; @@ -17,6 +18,7 @@ import org.junit.rules.TemporaryFolder; import spoon.FluentLauncher; +import spoon.SpoonException; import spoon.processing.AbstractParallelProcessor; import spoon.processing.AbstractProcessor; import spoon.processing.Processor; @@ -140,4 +142,23 @@ public void process(CtElement element) { } assertTrue(singleThreadCounter.get() == 0); } + + @Test + public void testDistinctCheck() throws IOException { + Integer[] counter = new Integer[] { 0, 0, 0, 0 }; + AtomicReferenceArray atomicCounter = new AtomicReferenceArray(counter); + Processor p1 = new AbstractProcessor() { + @Override + public void process(CtElement element) { + atomicCounter.getAndUpdate(0, i -> i + 1); + } + }; + assertThrows(SpoonException.class, + () -> new FluentLauncher().inputResource("src/test/resources/deprecated/input") + .processor(new AbstractParallelProcessor(Arrays.asList(p1, p1)) { + }) + .noClasspath(true) + .outputDirectory(folderFactory.newFolder()) + .buildModel()); + } } From d8a46483b547d640fad58a84f118ece0982d6bad Mon Sep 17 00:00:00 2001 From: Martin Wittlinger Date: Mon, 2 Mar 2020 18:08:34 +0100 Subject: [PATCH 06/11] add processorfactory and new constructor. removed distinct check, because it relays on hashcode --- .../processing/AbstractParallelProcessor.java | 27 ++++++---- .../processing/AbstractProcessorFactory.java | 16 ++++++ .../processors/ParallelProcessorTest.java | 52 +++++++++++++------ 3 files changed, 68 insertions(+), 27 deletions(-) create mode 100644 src/main/java/spoon/processing/AbstractProcessorFactory.java diff --git a/src/main/java/spoon/processing/AbstractParallelProcessor.java b/src/main/java/spoon/processing/AbstractParallelProcessor.java index 2dd23c04041..8c9676e4f2a 100644 --- a/src/main/java/spoon/processing/AbstractParallelProcessor.java +++ b/src/main/java/spoon/processing/AbstractParallelProcessor.java @@ -5,11 +5,12 @@ */ package spoon.processing; -import java.util.Collection; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.function.Consumer; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; import spoon.SpoonException; import spoon.reflect.declaration.CtElement; @@ -22,17 +23,21 @@ public abstract class AbstractParallelProcessor extends Abs private ExecutorService service; private ArrayBlockingQueue> processorQueue; - public AbstractParallelProcessor(Collection> processors) { - long distinctNumber = processors.stream().distinct().count(); - if (distinctNumber == 0) { - throw new SpoonException("List of processors is empty"); - } - if (distinctNumber != processors.size()) { - throw new SpoonException("Some processors are the same"); + public AbstractParallelProcessor(Iterable> processors) { + // added casts because long number of processors seems big and constructors need + // it + int processorNumber = (int) StreamSupport.stream(processors.spliterator(), false).count(); + processorQueue = new ArrayBlockingQueue<>(processorNumber); + processors.forEach(processorQueue::add); + service = Executors.newFixedThreadPool(processorNumber); + } + + public AbstractParallelProcessor(AbstractProcessorFactory processorFactory, int numberOfProcessors) { + processorQueue = new ArrayBlockingQueue<>(numberOfProcessors); + service = Executors.newFixedThreadPool(numberOfProcessors); + for (int i = 0; i < numberOfProcessors; i++) { + processorQueue.add(processorFactory.createProcessor()); } - processorQueue = new ArrayBlockingQueue<>(processors.size()); - processorQueue.addAll(processors); - service = Executors.newFixedThreadPool(processors.size()); } /** diff --git a/src/main/java/spoon/processing/AbstractProcessorFactory.java b/src/main/java/spoon/processing/AbstractProcessorFactory.java new file mode 100644 index 00000000000..622c3b70ee9 --- /dev/null +++ b/src/main/java/spoon/processing/AbstractProcessorFactory.java @@ -0,0 +1,16 @@ +/** + * Copyright (C) 2006-2019 INRIA and contributors + * + * Spoon is available either under the terms of the MIT License (see LICENSE-MIT.txt) of the Cecill-C License (see LICENSE-CECILL-C.txt). You as the user are entitled to choose the terms under which to adopt Spoon. + */ +package spoon.processing; + +import spoon.reflect.declaration.CtElement; + +/** + * AbstractProcessorFactory + */ +public interface AbstractProcessorFactory { + + Processor createProcessor(); +} diff --git a/src/test/java/spoon/test/processing/processors/ParallelProcessorTest.java b/src/test/java/spoon/test/processing/processors/ParallelProcessorTest.java index 88a745a64a1..89a54bb28ee 100644 --- a/src/test/java/spoon/test/processing/processors/ParallelProcessorTest.java +++ b/src/test/java/spoon/test/processing/processors/ParallelProcessorTest.java @@ -6,7 +6,6 @@ package spoon.test.processing.processors; import static org.junit.Assert.assertTrue; -import static org.junit.jupiter.api.Assertions.assertThrows; import java.io.IOException; import java.util.Arrays; @@ -18,9 +17,9 @@ import org.junit.rules.TemporaryFolder; import spoon.FluentLauncher; -import spoon.SpoonException; import spoon.processing.AbstractParallelProcessor; import spoon.processing.AbstractProcessor; +import spoon.processing.AbstractProcessorFactory; import spoon.processing.Processor; import spoon.reflect.declaration.CtElement; @@ -120,9 +119,8 @@ public void consumerConstructorTest() throws IOException { Integer[] counter = new Integer[] { 0, 0, 0, 0 }; AtomicReferenceArray atomicCounter = new AtomicReferenceArray(counter); new FluentLauncher().inputResource("src/test/resources/deprecated/input") - .processor( - new AbstractParallelProcessor((e) -> atomicCounter.getAndUpdate(0, i -> i + 1), 4) { - }) + .processor(new AbstractParallelProcessor((e) -> atomicCounter.getAndUpdate(0, i -> i + 1), 4) { + }) .noClasspath(true) .outputDirectory(folderFactory.newFolder()) .buildModel(); @@ -144,21 +142,43 @@ public void process(CtElement element) { } @Test - public void testDistinctCheck() throws IOException { + public void testFactory() throws IOException { Integer[] counter = new Integer[] { 0, 0, 0, 0 }; AtomicReferenceArray atomicCounter = new AtomicReferenceArray(counter); - Processor p1 = new AbstractProcessor() { + AbstractProcessorFactory factory = new AbstractProcessorFactory() { + private int number = -1; + @Override - public void process(CtElement element) { - atomicCounter.getAndUpdate(0, i -> i + 1); + public Processor createProcessor() { + number++; + return new AbstractProcessor() { + @Override + public void process(CtElement element) { + atomicCounter.getAndUpdate(number, i -> i + 1); + } + }; } }; - assertThrows(SpoonException.class, - () -> new FluentLauncher().inputResource("src/test/resources/deprecated/input") - .processor(new AbstractParallelProcessor(Arrays.asList(p1, p1)) { - }) - .noClasspath(true) - .outputDirectory(folderFactory.newFolder()) - .buildModel()); + new FluentLauncher().inputResource("src/test/resources/deprecated/input") + .processor(new AbstractParallelProcessor(factory, 4) { + }) + .noClasspath(true) + .outputDirectory(folderFactory.newFolder()) + .buildModel(); + AtomicInteger singleThreadCounter = new AtomicInteger(0); + new FluentLauncher().inputResource("src/test/resources/deprecated/input") + .processor(new AbstractProcessor() { + @Override + public void process(CtElement element) { + singleThreadCounter.incrementAndGet(); + } + }) + .noClasspath(true) + .outputDirectory(folderFactory.newFolder()) + .buildModel(); + for (int j = 0; j < atomicCounter.length(); j++) { + singleThreadCounter.set(singleThreadCounter.get() - atomicCounter.get(j)); + } + assertTrue(singleThreadCounter.get() == 0); } } From 29a14c57a5c30281dac0ddfd4aada4d6c19a52b8 Mon Sep 17 00:00:00 2001 From: Martin Wittlinger Date: Tue, 3 Mar 2020 19:06:02 +0100 Subject: [PATCH 07/11] refactor code and tests. add doc --- .../processing/AbstractParallelProcessor.java | 66 +++++- .../processing/AbstractProcessorFactory.java | 16 -- .../processors/ParallelProcessorTest.java | 213 ++++++++++-------- 3 files changed, 173 insertions(+), 122 deletions(-) delete mode 100644 src/main/java/spoon/processing/AbstractProcessorFactory.java diff --git a/src/main/java/spoon/processing/AbstractParallelProcessor.java b/src/main/java/spoon/processing/AbstractParallelProcessor.java index 8c9676e4f2a..f46c89a9798 100644 --- a/src/main/java/spoon/processing/AbstractParallelProcessor.java +++ b/src/main/java/spoon/processing/AbstractParallelProcessor.java @@ -1,50 +1,93 @@ /** * Copyright (C) 2006-2019 INRIA and contributors * - * Spoon is available either under the terms of the MIT License (see LICENSE-MIT.txt) of the Cecill-C License (see LICENSE-CECILL-C.txt). You as the user are entitled to choose the terms under which to adopt Spoon. + * Spoon is available either under the terms of the MIT License (see LICENSE-MIT.txt) of the Cecill-C License (see LICENSE-CECILL-C.txt). + * You as the user are entitled to choose the terms under which to adopt Spoon. */ package spoon.processing; +import java.util.Iterator; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.function.Consumer; -import java.util.stream.Stream; import java.util.stream.StreamSupport; import spoon.SpoonException; import spoon.reflect.declaration.CtElement; /** - * AbstractParallelProcessor + * AbstractParallelProcessor allows using multiple threads for concurrent + * processing with {@link AbstractProcessor}. + * + * This class should only be used if all processors do the same. + * Otherwise the result may vary from the expected result. All processors + * must synchronize shared fields like Collections by themselves. Multiple + * constructors exist for different approaches creating this. You can create + * this processor with either a Iterable of processors or a Consumer. + * + * @implNote for creating and managing threads a + * {@link Executors#newFixedThreadPool()} is used. Creating more + * threads then cores can harm the performance. Using a different + * thread pool could increase the performance, but this class should + * be general usage. If you need better performance you may want to + * use an own class with different parallel approach. */ public abstract class AbstractParallelProcessor extends AbstractProcessor { private ExecutorService service; private ArrayBlockingQueue> processorQueue; + /** + * Creates a new AbstractParallelProcessor from given iterable. The iterable is + * fully consumed. Giving an endless iterable of processors will result in + * errors. The processors must follow the guidelines given in the class + * description. + * + * @param processors iterable of processors. + * @throws IllegalArgumentException if size of iterable is less than 1. + * + */ public AbstractParallelProcessor(Iterable> processors) { - // added casts because long number of processors seems big and constructors need - // it + // added cast because constructors need int int processorNumber = (int) StreamSupport.stream(processors.spliterator(), false).count(); processorQueue = new ArrayBlockingQueue<>(processorNumber); processors.forEach(processorQueue::add); service = Executors.newFixedThreadPool(processorNumber); } - public AbstractParallelProcessor(AbstractProcessorFactory processorFactory, int numberOfProcessors) { + /** + * Creates a new AbstractParallelProcessor from given iterable. The processors + * must follow the guidelines given in the class description. + * + * @param processors iterable of processors. + * @param numberOfProcessors number consumed from the iterable added to the + * active processors. + * @throws SpoonException if iterable has less values then + * numberOfProcessors. + * @throws IllegalArgumentException if numberOfProcessors is less than 1. + * + */ + public AbstractParallelProcessor(Iterable> processors, int numberOfProcessors) { processorQueue = new ArrayBlockingQueue<>(numberOfProcessors); service = Executors.newFixedThreadPool(numberOfProcessors); + Iterator> it = processors.iterator(); for (int i = 0; i < numberOfProcessors; i++) { - processorQueue.add(processorFactory.createProcessor()); + if (!it.hasNext()) { + throw new SpoonException("not enough elements provided, iterable is already empty"); + } + processorQueue.add(it.next()); } } /** + * Creates a new AbstractParallelProcessor from given consumer. The processors + * must follow the guidelines given in the class description. * - * @param processFunction - * @param numberOfProcessors - * @throws IllegalArgumentException if numberOfProcessors is less than 1 + * @param processFunction Represents an operation that accepts a single + * element E and returns no result. + * @param numberOfProcessors number of concurrent running processors. + * @throws IllegalArgumentException if numberOfProcessors is less than 1. */ public AbstractParallelProcessor(Consumer processFunction, int numberOfProcessors) { processorQueue = new ArrayBlockingQueue<>(numberOfProcessors); @@ -85,6 +128,9 @@ public final void process(E element) { } } + /** + * Cleans the threadpool after processing. + */ @Override public void processingDone() { service.shutdown(); diff --git a/src/main/java/spoon/processing/AbstractProcessorFactory.java b/src/main/java/spoon/processing/AbstractProcessorFactory.java deleted file mode 100644 index 622c3b70ee9..00000000000 --- a/src/main/java/spoon/processing/AbstractProcessorFactory.java +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Copyright (C) 2006-2019 INRIA and contributors - * - * Spoon is available either under the terms of the MIT License (see LICENSE-MIT.txt) of the Cecill-C License (see LICENSE-CECILL-C.txt). You as the user are entitled to choose the terms under which to adopt Spoon. - */ -package spoon.processing; - -import spoon.reflect.declaration.CtElement; - -/** - * AbstractProcessorFactory - */ -public interface AbstractProcessorFactory { - - Processor createProcessor(); -} diff --git a/src/test/java/spoon/test/processing/processors/ParallelProcessorTest.java b/src/test/java/spoon/test/processing/processors/ParallelProcessorTest.java index 89a54bb28ee..bca465e1b4f 100644 --- a/src/test/java/spoon/test/processing/processors/ParallelProcessorTest.java +++ b/src/test/java/spoon/test/processing/processors/ParallelProcessorTest.java @@ -6,9 +6,11 @@ package spoon.test.processing.processors; import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertThrows; import java.io.IOException; import java.util.Arrays; +import java.util.Collections; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReferenceArray; @@ -17,64 +19,54 @@ import org.junit.rules.TemporaryFolder; import spoon.FluentLauncher; +import spoon.SpoonException; import spoon.processing.AbstractParallelProcessor; import spoon.processing.AbstractProcessor; -import spoon.processing.AbstractProcessorFactory; import spoon.processing.Processor; import spoon.reflect.declaration.CtElement; -/** - * ParallelProcessorTest - */ public class ParallelProcessorTest { + private static final String INPUT_FILES = "src/test/resources/deprecated/input"; @Rule public TemporaryFolder folderFactory = new TemporaryFolder(); - @Test - public void compareWithSingleThreaded1() throws IOException { + private AtomicReferenceArray createCounter() { Integer[] counter = new Integer[] { 0, 0, 0, 0 }; AtomicReferenceArray atomicCounter = new AtomicReferenceArray(counter); - Processor p1 = new AbstractProcessor() { - @Override - public void process(CtElement element) { - atomicCounter.getAndUpdate(0, i -> i + 1); - } - }; - Processor p2 = new AbstractProcessor() { - @Override - public void process(CtElement element) { - atomicCounter.getAndUpdate(1, i -> i + 1); - } - }; - Processor p3 = new AbstractProcessor() { - @Override - public void process(CtElement element) { - atomicCounter.getAndUpdate(2, i -> i + 1); - } - }; - Processor p4 = new AbstractProcessor() { + return atomicCounter; + } + + private Processor createProcessor(AtomicReferenceArray atomicCounter, int digit) { + Processor processor = new AbstractProcessor() { @Override public void process(CtElement element) { - atomicCounter.getAndUpdate(3, i -> i + 1); + atomicCounter.getAndUpdate(digit, i -> i + 1); } }; - new FluentLauncher().inputResource("src/test/resources/deprecated/input") + return processor; + } + + @Test + public void compareWithSingleThreaded1() throws IOException { + AtomicReferenceArray atomicCounter = createCounter(); + Processor p1 = createProcessor(atomicCounter, 0); + Processor p2 = createProcessor(atomicCounter, 1); + Processor p3 = createProcessor(atomicCounter, 2); + Processor p4 = createProcessor(atomicCounter, 3); + + new FluentLauncher().inputResource(INPUT_FILES) .processor(new AbstractParallelProcessor(Arrays.asList(p1, p2, p3, p4)) { }) .noClasspath(true) .outputDirectory(folderFactory.newFolder()) .buildModel(); AtomicInteger singleThreadCounter = new AtomicInteger(0); - new FluentLauncher().inputResource("src/test/resources/deprecated/input") - .processor(new AbstractProcessor() { - @Override - public void process(CtElement element) { - singleThreadCounter.incrementAndGet(); - } - }) - .noClasspath(true) - .outputDirectory(folderFactory.newFolder()) - .buildModel(); + new FluentLauncher().inputResource(INPUT_FILES).processor(new AbstractProcessor() { + @Override + public void process(CtElement element) { + singleThreadCounter.incrementAndGet(); + } + }).noClasspath(true).outputDirectory(folderFactory.newFolder()).buildModel(); for (int j = 0; j < atomicCounter.length(); j++) { singleThreadCounter.set(singleThreadCounter.get() - atomicCounter.get(j)); } @@ -83,31 +75,22 @@ public void process(CtElement element) { @Test public void compareWithSingleThreaded2() throws IOException { - Integer[] counter = new Integer[] { 0, 0, 0, 0 }; - AtomicReferenceArray atomicCounter = new AtomicReferenceArray(counter); - Processor p1 = new AbstractProcessor() { - @Override - public void process(CtElement element) { - atomicCounter.getAndUpdate(0, i -> i + 1); - } - }; - new FluentLauncher().inputResource("src/test/resources/deprecated/input") + AtomicReferenceArray atomicCounter = createCounter(); + Processor p1 = createProcessor(atomicCounter, 0); + + new FluentLauncher().inputResource(INPUT_FILES) .processor(new AbstractParallelProcessor(Arrays.asList(p1)) { }) .noClasspath(true) .outputDirectory(folderFactory.newFolder()) .buildModel(); AtomicInteger singleThreadCounter = new AtomicInteger(0); - new FluentLauncher().inputResource("src/test/resources/deprecated/input") - .processor(new AbstractProcessor() { - @Override - public void process(CtElement element) { - singleThreadCounter.incrementAndGet(); - } - }) - .noClasspath(true) - .outputDirectory(folderFactory.newFolder()) - .buildModel(); + new FluentLauncher().inputResource(INPUT_FILES).processor(new AbstractProcessor() { + @Override + public void process(CtElement element) { + singleThreadCounter.incrementAndGet(); + } + }).noClasspath(true).outputDirectory(folderFactory.newFolder()).buildModel(); for (int j = 0; j < atomicCounter.length(); j++) { singleThreadCounter.set(singleThreadCounter.get() - atomicCounter.get(j)); } @@ -116,69 +99,107 @@ public void process(CtElement element) { @Test public void consumerConstructorTest() throws IOException { - Integer[] counter = new Integer[] { 0, 0, 0, 0 }; - AtomicReferenceArray atomicCounter = new AtomicReferenceArray(counter); - new FluentLauncher().inputResource("src/test/resources/deprecated/input") + AtomicReferenceArray atomicCounter = createCounter(); + new FluentLauncher().inputResource(INPUT_FILES) .processor(new AbstractParallelProcessor((e) -> atomicCounter.getAndUpdate(0, i -> i + 1), 4) { }) .noClasspath(true) .outputDirectory(folderFactory.newFolder()) .buildModel(); AtomicInteger singleThreadCounter = new AtomicInteger(0); - new FluentLauncher().inputResource("src/test/resources/deprecated/input") - .processor(new AbstractProcessor() { - @Override - public void process(CtElement element) { - singleThreadCounter.incrementAndGet(); - } + new FluentLauncher().inputResource(INPUT_FILES).processor(new AbstractProcessor() { + @Override + public void process(CtElement element) { + singleThreadCounter.incrementAndGet(); + } + }).noClasspath(true).outputDirectory(folderFactory.newFolder()).buildModel(); + for (int j = 0; j < atomicCounter.length(); j++) { + singleThreadCounter.set(singleThreadCounter.get() - atomicCounter.get(j)); + } + assertTrue(singleThreadCounter.get() == 0); + } + + @Test + public void compareWithSingleThreaded3() throws IOException { + AtomicReferenceArray atomicCounter = createCounter(); + Processor p1 = createProcessor(atomicCounter, 0); + Processor p2 = createProcessor(atomicCounter, 1); + Processor p3 = createProcessor(atomicCounter, 2); + Processor p4 = createProcessor(atomicCounter, 3); + + new FluentLauncher().inputResource(INPUT_FILES) + .processor(new AbstractParallelProcessor(Arrays.asList(p1, p2, p3, p4), 3) { }) .noClasspath(true) .outputDirectory(folderFactory.newFolder()) .buildModel(); + AtomicInteger singleThreadCounter = new AtomicInteger(0); + new FluentLauncher().inputResource(INPUT_FILES).processor(new AbstractProcessor() { + @Override + public void process(CtElement element) { + singleThreadCounter.incrementAndGet(); + } + }).noClasspath(true).outputDirectory(folderFactory.newFolder()).buildModel(); for (int j = 0; j < atomicCounter.length(); j++) { singleThreadCounter.set(singleThreadCounter.get() - atomicCounter.get(j)); } assertTrue(singleThreadCounter.get() == 0); + // because only 3 are used + assertTrue(atomicCounter.get(3) == 0); } @Test - public void testFactory() throws IOException { - Integer[] counter = new Integer[] { 0, 0, 0, 0 }; - AtomicReferenceArray atomicCounter = new AtomicReferenceArray(counter); - AbstractProcessorFactory factory = new AbstractProcessorFactory() { - private int number = -1; + public void testSize() throws IOException { + AtomicReferenceArray atomicCounter = createCounter(); + Processor p1 = createProcessor(atomicCounter, 0); - @Override - public Processor createProcessor() { - number++; - return new AbstractProcessor() { - @Override - public void process(CtElement element) { - atomicCounter.getAndUpdate(number, i -> i + 1); - } - }; - } - }; - new FluentLauncher().inputResource("src/test/resources/deprecated/input") - .processor(new AbstractParallelProcessor(factory, 4) { + assertThrows(IllegalArgumentException.class, () -> new FluentLauncher().inputResource(INPUT_FILES) + .processor(new AbstractParallelProcessor(Arrays.asList(p1), 0) { }) .noClasspath(true) .outputDirectory(folderFactory.newFolder()) - .buildModel(); - AtomicInteger singleThreadCounter = new AtomicInteger(0); - new FluentLauncher().inputResource("src/test/resources/deprecated/input") - .processor(new AbstractProcessor() { - @Override - public void process(CtElement element) { - singleThreadCounter.incrementAndGet(); - } + .buildModel()); + } + + @Test + public void testSize2() throws IOException { + AtomicReferenceArray atomicCounter = createCounter(); + Processor p1 = createProcessor(atomicCounter, 0); + assertThrows(IllegalArgumentException.class, () -> new FluentLauncher().inputResource(INPUT_FILES) + .processor(new AbstractParallelProcessor(Arrays.asList(p1), -5) { }) .noClasspath(true) .outputDirectory(folderFactory.newFolder()) - .buildModel(); - for (int j = 0; j < atomicCounter.length(); j++) { - singleThreadCounter.set(singleThreadCounter.get() - atomicCounter.get(j)); - } - assertTrue(singleThreadCounter.get() == 0); + .buildModel()); + } + + @Test + public void testSize3() throws IOException { + assertThrows(SpoonException.class, () -> new FluentLauncher().inputResource(INPUT_FILES) + .processor(new AbstractParallelProcessor(Collections.emptyList(), 1) { + }) + .noClasspath(true) + .outputDirectory(folderFactory.newFolder()) + .buildModel()); + } + + @Test + public void testSize4() throws IOException { + assertThrows(IllegalArgumentException.class, () -> new FluentLauncher().inputResource(INPUT_FILES) + .processor(new AbstractParallelProcessor(Collections.emptyList()) { + }) + .noClasspath(true) + .outputDirectory(folderFactory.newFolder()) + .buildModel()); + } + + @Test + public void testSize5() throws IOException { + assertThrows(IllegalArgumentException.class, () -> new FluentLauncher().inputResource(INPUT_FILES) + .processor(new AbstractParallelProcessor((v) -> v.toString(), 0) { + }) + .noClasspath(true) + .outputDirectory(folderFactory.newFolder()) + .buildModel()); } } From ce1e4f60829f260a0500a65d1cb0e20946d20272 Mon Sep 17 00:00:00 2001 From: Martin Wittlinger Date: Tue, 3 Mar 2020 19:23:55 +0100 Subject: [PATCH 08/11] fix header --- src/main/java/spoon/processing/AbstractParallelProcessor.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/spoon/processing/AbstractParallelProcessor.java b/src/main/java/spoon/processing/AbstractParallelProcessor.java index f46c89a9798..78ea348dda4 100644 --- a/src/main/java/spoon/processing/AbstractParallelProcessor.java +++ b/src/main/java/spoon/processing/AbstractParallelProcessor.java @@ -1,8 +1,7 @@ /** * Copyright (C) 2006-2019 INRIA and contributors * - * Spoon is available either under the terms of the MIT License (see LICENSE-MIT.txt) of the Cecill-C License (see LICENSE-CECILL-C.txt). - * You as the user are entitled to choose the terms under which to adopt Spoon. + * Spoon is available either under the terms of the MIT License (see LICENSE-MIT.txt) of the Cecill-C License (see LICENSE-CECILL-C.txt). You as the user are entitled to choose the terms under which to adopt Spoon. */ package spoon.processing; From d2a1f48dea87ee2d5c814648ae0974b93702df4d Mon Sep 17 00:00:00 2001 From: Martin Wittlinger Date: Tue, 3 Mar 2020 19:38:31 +0100 Subject: [PATCH 09/11] fix doc --- .../spoon/processing/AbstractParallelProcessor.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main/java/spoon/processing/AbstractParallelProcessor.java b/src/main/java/spoon/processing/AbstractParallelProcessor.java index 78ea348dda4..5ade1a77db7 100644 --- a/src/main/java/spoon/processing/AbstractParallelProcessor.java +++ b/src/main/java/spoon/processing/AbstractParallelProcessor.java @@ -25,12 +25,11 @@ * constructors exist for different approaches creating this. You can create * this processor with either a Iterable of processors or a Consumer. * - * @implNote for creating and managing threads a - * {@link Executors#newFixedThreadPool()} is used. Creating more - * threads then cores can harm the performance. Using a different - * thread pool could increase the performance, but this class should - * be general usage. If you need better performance you may want to - * use an own class with different parallel approach. + * For creating and managing threads a {@link Executors#newFixedThreadPool()} is + * used. Creating more threads then cores can harm the performance. Using a + * different thread pool could increase the performance, but this class should + * be general usage. If you need better performance you may want to use an own + * class with different parallel approach. */ public abstract class AbstractParallelProcessor extends AbstractProcessor { From a64dfb402ab9efe6870d51353e0069c3a574536d Mon Sep 17 00:00:00 2001 From: Martin Wittlinger Date: Fri, 6 Mar 2020 21:57:36 +0100 Subject: [PATCH 10/11] add contracts to test cases --- .../processors/ParallelProcessorTest.java | 32 +++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/src/test/java/spoon/test/processing/processors/ParallelProcessorTest.java b/src/test/java/spoon/test/processing/processors/ParallelProcessorTest.java index bca465e1b4f..0bc7fe851b8 100644 --- a/src/test/java/spoon/test/processing/processors/ParallelProcessorTest.java +++ b/src/test/java/spoon/test/processing/processors/ParallelProcessorTest.java @@ -48,7 +48,13 @@ public void process(CtElement element) { @Test public void compareWithSingleThreaded1() throws IOException { + // contract: running with 4 processors parallel must produce the same result as + // single threaded processor. + // for testing this a simple processor counting visited nodes is used. + + // create a countingArray for the concurrent processors. AtomicReferenceArray atomicCounter = createCounter(); + // create processors Processor p1 = createProcessor(atomicCounter, 0); Processor p2 = createProcessor(atomicCounter, 1); Processor p3 = createProcessor(atomicCounter, 2); @@ -60,6 +66,7 @@ public void compareWithSingleThreaded1() throws IOException { .noClasspath(true) .outputDirectory(folderFactory.newFolder()) .buildModel(); + AtomicInteger singleThreadCounter = new AtomicInteger(0); new FluentLauncher().inputResource(INPUT_FILES).processor(new AbstractProcessor() { @Override @@ -67,6 +74,11 @@ public void process(CtElement element) { singleThreadCounter.incrementAndGet(); } }).noClasspath(true).outputDirectory(folderFactory.newFolder()).buildModel(); + + // after processing both |singleThreadCounter| == sum(|atomicCounter|) must be + // true. + // for checking this subtract each array value from the + // singleThreadCounter and check for == 0 for (int j = 0; j < atomicCounter.length(); j++) { singleThreadCounter.set(singleThreadCounter.get() - atomicCounter.get(j)); } @@ -75,6 +87,9 @@ public void process(CtElement element) { @Test public void compareWithSingleThreaded2() throws IOException { + // contract: a parallelProcessor with one thread must produce the same result as + // a normal processor. + AtomicReferenceArray atomicCounter = createCounter(); Processor p1 = createProcessor(atomicCounter, 0); @@ -91,14 +106,14 @@ public void process(CtElement element) { singleThreadCounter.incrementAndGet(); } }).noClasspath(true).outputDirectory(folderFactory.newFolder()).buildModel(); - for (int j = 0; j < atomicCounter.length(); j++) { - singleThreadCounter.set(singleThreadCounter.get() - atomicCounter.get(j)); - } + singleThreadCounter.set(singleThreadCounter.get() - atomicCounter.get(0)); assertTrue(singleThreadCounter.get() == 0); } @Test public void consumerConstructorTest() throws IOException { + // contract: creating with consumer constructor must produces correct results. + // See other tests for explanation how the testing works. AtomicReferenceArray atomicCounter = createCounter(); new FluentLauncher().inputResource(INPUT_FILES) .processor(new AbstractParallelProcessor((e) -> atomicCounter.getAndUpdate(0, i -> i + 1), 4) { @@ -121,6 +136,9 @@ public void process(CtElement element) { @Test public void compareWithSingleThreaded3() throws IOException { + // contract: using an iterable with more elements than used should only use the + // given number. Result must be correct too. + // Here the iterable has size 4 and only 3 are used. AtomicReferenceArray atomicCounter = createCounter(); Processor p1 = createProcessor(atomicCounter, 0); Processor p2 = createProcessor(atomicCounter, 1); @@ -150,6 +168,7 @@ public void process(CtElement element) { @Test public void testSize() throws IOException { + // contract: a thread pool with size zero must not created. AtomicReferenceArray atomicCounter = createCounter(); Processor p1 = createProcessor(atomicCounter, 0); @@ -163,6 +182,7 @@ public void testSize() throws IOException { @Test public void testSize2() throws IOException { + // contract: negative processor numbers must throw an exception. AtomicReferenceArray atomicCounter = createCounter(); Processor p1 = createProcessor(atomicCounter, 0); assertThrows(IllegalArgumentException.class, () -> new FluentLauncher().inputResource(INPUT_FILES) @@ -175,6 +195,8 @@ public void testSize2() throws IOException { @Test public void testSize3() throws IOException { + // contract: trying to consume more processor than provided must throw an + // exception. assertThrows(SpoonException.class, () -> new FluentLauncher().inputResource(INPUT_FILES) .processor(new AbstractParallelProcessor(Collections.emptyList(), 1) { }) @@ -185,6 +207,8 @@ public void testSize3() throws IOException { @Test public void testSize4() throws IOException { + // contract: trying to consume more processor than provided must throw an + // exception. assertThrows(IllegalArgumentException.class, () -> new FluentLauncher().inputResource(INPUT_FILES) .processor(new AbstractParallelProcessor(Collections.emptyList()) { }) @@ -195,6 +219,8 @@ public void testSize4() throws IOException { @Test public void testSize5() throws IOException { + // contract: a thread pool with size zero must not created. + assertThrows(IllegalArgumentException.class, () -> new FluentLauncher().inputResource(INPUT_FILES) .processor(new AbstractParallelProcessor((v) -> v.toString(), 0) { }) From 922444e578786123c68d79132dd0c73e43e34b16 Mon Sep 17 00:00:00 2001 From: Martin Wittlinger Date: Fri, 6 Mar 2020 22:24:59 +0100 Subject: [PATCH 11/11] add documentation --- doc/processor.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/doc/processor.md b/doc/processor.md index 06f3274881e..7557bdebb0a 100644 --- a/doc/processor.md +++ b/doc/processor.md @@ -53,3 +53,15 @@ public class CatchProcessor extends AbstractProcessor { } } ``` +## Parallel Processor + +Lets assume you want to use multiple cores for your processor. Spoon provides a simple high-level API for this task. +Using the CatchProcessor from before create a `AbstractParallelProcessor`. + +```java + Processor parallelProcessor = new AbstractParallelProcessor( + Arrays.asList(new CatchProcessor(), new CatchProcessor())) {}; +``` +Now you have the same processor behavior as before, but 2 parallel running processor. +You can upscale this pretty high, but keep in mind to not use more parallel processors than available cores for maximum speedup. +For more information about parallel processor and the API have a look in the [documentation](http://spoon.gforge.inria.fr/mvnsites/spoon-core/apidocs/spoon/processing/AbstractParallelProcessor.html). \ No newline at end of file