diff --git a/.travis.yml b/.travis.yml index 8a29df8..f4ff54d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: java jdk: oraclejdk8 -install: -- mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V +before_install: + - chmod +x buildscript script: ./buildscript os: linux \ No newline at end of file diff --git a/buildscript b/buildscript old mode 100644 new mode 100755 index d806e9e..2bd19ce --- a/buildscript +++ b/buildscript @@ -1 +1,9 @@ -mvn test -B \ No newline at end of file +rootdir=$(pwd) +for dir in $(find . -maxdepth 1 -type d); do + if [ "$dir" != "." ] && [ "$dir" != "./.git" ]; then + cd $dir + mvn test -B + cd $rootdir + fi +done + diff --git a/hw1pool/.idea/checkstyle-idea.xml b/hw1pool/.idea/checkstyle-idea.xml new file mode 100644 index 0000000..7b2331a --- /dev/null +++ b/hw1pool/.idea/checkstyle-idea.xml @@ -0,0 +1,16 @@ + + + + + + \ No newline at end of file diff --git a/hw1pool/.idea/compiler.xml b/hw1pool/.idea/compiler.xml new file mode 100644 index 0000000..f1eb2ae --- /dev/null +++ b/hw1pool/.idea/compiler.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/hw1pool/.idea/misc.xml b/hw1pool/.idea/misc.xml new file mode 100644 index 0000000..462106b --- /dev/null +++ b/hw1pool/.idea/misc.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/hw1pool/.idea/modules.xml b/hw1pool/.idea/modules.xml new file mode 100644 index 0000000..a384a3a --- /dev/null +++ b/hw1pool/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/hw1pool/.idea/uiDesigner.xml b/hw1pool/.idea/uiDesigner.xml new file mode 100644 index 0000000..e96534f --- /dev/null +++ b/hw1pool/.idea/uiDesigner.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/hw1pool/hw1pool.iml b/hw1pool/hw1pool.iml new file mode 100644 index 0000000..57bbe94 --- /dev/null +++ b/hw1pool/hw1pool.iml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/hw1pool/pom.xml b/hw1pool/pom.xml new file mode 100644 index 0000000..b8d16a7 --- /dev/null +++ b/hw1pool/pom.xml @@ -0,0 +1,40 @@ + + + 4.0.0 + + ru.spbau.group202.sharkova + hw1pool + 1.0-SNAPSHOT + + + + + junit + junit + 4.9 + test + + + + com.github.stefanbirkner + system-rules + 1.16.0 + test + + + + + com.intellij + annotations + 12.0 + + + + + 1.8 + 1.8 + + + \ No newline at end of file diff --git a/hw1pool/src/main/java/ru/spbau/group202/sharkova/hw1pool/LightExecutionException.java b/hw1pool/src/main/java/ru/spbau/group202/sharkova/hw1pool/LightExecutionException.java new file mode 100644 index 0000000..dcaa81e --- /dev/null +++ b/hw1pool/src/main/java/ru/spbau/group202/sharkova/hw1pool/LightExecutionException.java @@ -0,0 +1,11 @@ +package ru.spbau.group202.sharkova.hw1pool; + +/** + * This exception is thrown by LightFuture get() method + * whenever an exception occurred during supplier get() method execution. + */ +public class LightExecutionException extends Exception { + LightExecutionException(Exception e) { + super(e); + } +} diff --git a/hw1pool/src/main/java/ru/spbau/group202/sharkova/hw1pool/LightFuture.java b/hw1pool/src/main/java/ru/spbau/group202/sharkova/hw1pool/LightFuture.java new file mode 100644 index 0000000..0a650b2 --- /dev/null +++ b/hw1pool/src/main/java/ru/spbau/group202/sharkova/hw1pool/LightFuture.java @@ -0,0 +1,33 @@ +package ru.spbau.group202.sharkova.hw1pool; + +import java.util.function.Function; + +/** + * This interface represents a task for a thread pool. + * Available methods: check if the task is completed, + * get task result, chain the execution result to another task. + * @param task execution result type parameter + * */ +public interface LightFuture { + /** + * Indicates the state of the task. + * @return true if the task is completed. + */ + boolean isReady(); + + /** + * Returns task execution result; + * if not completed, waits until the execution is finished. + * @throws LightExecutionException if the corresponding supplier + * throws an exception. + * */ + T get() throws LightExecutionException; + + /** + * Takes current task result and applies a given Function object + * to it, creating a new task. + * @param function the task that uses the current task result + * @return a new task + * */ + LightFuture thenApply(Function function); +} diff --git a/hw1pool/src/main/java/ru/spbau/group202/sharkova/hw1pool/ThreadPool.java b/hw1pool/src/main/java/ru/spbau/group202/sharkova/hw1pool/ThreadPool.java new file mode 100644 index 0000000..4158566 --- /dev/null +++ b/hw1pool/src/main/java/ru/spbau/group202/sharkova/hw1pool/ThreadPool.java @@ -0,0 +1,129 @@ +package ru.spbau.group202.sharkova.hw1pool; + +import java.util.ArrayList; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.function.Function; +import java.util.function.Supplier; + +/** + * Thread pool implementation. + * Uses a given number of threads to execute LightFuture tasks. + * @param task return type parameter + */ +public class ThreadPool { + private ArrayList threads = new ArrayList<>(); + private final Queue tasks = new ConcurrentLinkedQueue<>(); + + public ThreadPool(int numberOfThreads) { + for (int i = 0; i < numberOfThreads; i++) { + threads.add(new Thread(new ThreadPoolTask())); + threads.get(i).setDaemon(true); + threads.get(i).start(); + } + } + + /** + * Add a new task to the queue. + * @param supplier provides a job to be executed + * @return LightFuture task encapsulating the provided supplier + */ + public LightFuture add(Supplier supplier) { + LightFutureTask task = new LightFutureTask(supplier); + + synchronized (tasks) { + tasks.add(task); + } + + return task; + } + + /** + * Interrupts all threads in the pool. + */ + public void shutdown() { + for (Thread thread : threads) { + thread.interrupt(); + } + } + + /** + * Utility class implementing LightFuture interface. + */ + private class LightFutureTask implements LightFuture{ + private boolean isReady; + private T result; + private Exception exception; + private Supplier supplier; + + public LightFutureTask(Supplier supplier) { + this.supplier = supplier; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isReady() { + return isReady; + } + + /** + * {@inheritDoc} + */ + @Override + public T get() throws LightExecutionException { + while (!isReady()) { + Thread.yield(); + } + + if (exception != null) { + throw new LightExecutionException(exception); + } + + return result; + + } + + /** + * {@inheritDoc} + */ + @Override + public LightFuture thenApply(Function function) { + return add(() -> { + try { + return function.apply(get()); + } catch (LightExecutionException e) { + throw new RuntimeException(e); + } + }); + } + } + + /** + * Utility class used for queue task executing. + */ + private class ThreadPoolTask implements Runnable { + @Override + public void run() { + while (!Thread.interrupted()) { + LightFutureTask task = null; + synchronized (ThreadPool.this) { + if (!tasks.isEmpty()) { + task = tasks.poll(); + } + } + + if (task != null) { + try { + task.result = task.supplier.get(); + } catch (Exception e) { + task.exception = e; + } + + task.isReady = true; + } + } + } + } +} diff --git a/hw1pool/src/test/java/ru/spbau/group202/sharkova/hw1pool/ThreadPoolTest.java b/hw1pool/src/test/java/ru/spbau/group202/sharkova/hw1pool/ThreadPoolTest.java new file mode 100644 index 0000000..db64e4f --- /dev/null +++ b/hw1pool/src/test/java/ru/spbau/group202/sharkova/hw1pool/ThreadPoolTest.java @@ -0,0 +1,168 @@ +package ru.spbau.group202.sharkova.hw1pool; + +import org.junit.Test; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.function.Supplier; + +import static org.junit.Assert.*; + +/** + * This class tests ThreadPool methods tests + * and correctness of LightFuture methods implementation + * used inside ThreadPool. + */ +public class ThreadPoolTest { + + private static final int numberOfIterations = 10; + private static final int numberOfThreads = 10; + + @Test + public void testBasicIntegerSupplier() throws LightExecutionException { + ThreadPool threadPool = new ThreadPool<>(numberOfThreads); + LightFuture lf = threadPool.add(() -> 16); + assertEquals(new Integer(16), lf.get()); + } + + @Test + public void testIncrementalSupplier() throws LightExecutionException { + ThreadPool threadPool = new ThreadPool<>(numberOfThreads); + LightFuture lf1 = threadPool.add(new IncrementalSupplier()); + assertEquals(new Integer(0), lf1.get()); + threadPool.shutdown(); + threadPool = new ThreadPool<>(1); + LightFuture lf2 = threadPool.add(new IncrementalSupplier()); + assertEquals(lf1.get(), lf2.get()); + } + + @Test + public void testIncrementalSupplierMultipleQueries() throws LightExecutionException { + ThreadPool threadPool = new ThreadPool<>(numberOfThreads); + LightFuture lf = threadPool.add(new IncrementalSupplier()); + for (int i = 0; i < numberOfIterations; i++) { + assertEquals(new Integer(0), lf.get()); + } + } + + @Test + public void testMultipleTasksMultipleQueries() throws LightExecutionException { + ThreadPool threadPool = new ThreadPool<>(numberOfThreads); + LightFuture lf1 = threadPool.add(() -> 20 * 30 * 40 - 50 + 60 - 70 + 80 * 90); + LightFuture lf2 = threadPool.add(() -> 31140); + Supplier supplier = new IncrementalSupplier(); + LightFuture lf3 = threadPool.add(supplier); + LightFuture lf4 = threadPool.add(supplier); + LightFuture lf5 = threadPool.add(new IncrementalSupplier()); + + for (int i = 0; i < numberOfIterations; i++) { + assertEquals(lf2.get(), lf1.get()); + } + + for (int i = 0; i < numberOfIterations; i++) { + assertEquals(new Integer(0), lf3.get()); + } + + for (int i = 0; i < numberOfIterations; i++) { + assertEquals(lf3.get(), lf5.get()); + } + + for (int i = 0; i < numberOfIterations; i++) { + assertEquals(new Integer(1), lf4.get()); + } + } + + @Test + public void testNullSupplier() throws LightExecutionException { + ThreadPool threadPool = new ThreadPool<>(numberOfThreads); + LightFuture lf1 = threadPool.add(() -> null); + LightFuture lf2 = threadPool.add(Object::new); + + for (int i = 0; i < numberOfIterations; i++) { + assertEquals(null, lf1.get()); + } + + for (int i = 0; i < numberOfIterations; i++) { + assertNotNull(lf2.get()); + } + } + + @Test + public void testThenApplyInteger() throws LightExecutionException { + ThreadPool threadPool = new ThreadPool<>(numberOfThreads); + LightFuture lf1 = threadPool.add(() -> 30); + LightFuture lf2 = lf1.thenApply((x) -> (x + 2)); + assertEquals(new Integer(32), lf2.get()); + } + + @Test + public void testThenApply() throws LightExecutionException { + ThreadPool threadPool = new ThreadPool<>(numberOfThreads); + LightFuture lf1 = threadPool.add(() -> { + int res = 1; + for (int i = 1; i <= 12; i++) { + res *= i; + } + return res; + }); + assertNotNull(lf1.get()); + LightFuture lf2 = lf1.thenApply((x) -> null); + assertNull(lf2.get()); + } + + @Test + public void testIsReady() throws LightExecutionException { + ThreadPool threadPool = new ThreadPool<>(numberOfThreads); + LightFuture lf1 = threadPool.add(Object::new); + lf1.get(); + assertTrue(lf1.isReady()); + + LightFuture lf2 = threadPool.add(() -> { + while (true) { + Thread.yield(); + } + }); + for (int i = 0; i < numberOfIterations; i++) { + assertFalse(lf2.isReady()); + } + } + + @SuppressWarnings("unchecked") + @Test + public void testShutdown() throws LightExecutionException, NoSuchFieldException, IllegalAccessException { + ThreadPool threadPool = new ThreadPool<>(numberOfThreads); + LightFuture lf = threadPool.add(() -> { + while (true) { + Thread.yield(); + } + }); + + Field threads = ThreadPool.class.getDeclaredField("threads"); + threads.setAccessible(true); + for (Thread thread : (ArrayList) threads.get(threadPool)) { + assertFalse(thread.isInterrupted()); + } + + threadPool.shutdown(); + for (Thread thread : (ArrayList) threads.get(threadPool)) { + assertTrue(thread.isInterrupted()); + } + } + + @Test(expected = LightExecutionException.class) + public void testLightExecutionException() throws LightExecutionException { + ThreadPool threadPool = new ThreadPool<>(numberOfThreads); + threadPool.add(() -> { + throw new NullPointerException(); + }).get(); + } + + private class IncrementalSupplier implements Supplier { + private int cnt = 0; + @Override + public Integer get() { + return cnt++; + } + } + +} \ No newline at end of file