diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..6b8dde0 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,7 @@ +language: java +jdk: + - oraclejdk8 +os: + - linux +script: + - chmod +x buildscript.sh && ./buildscript.sh diff --git a/01.Pool/pom.xml b/01.Pool/pom.xml new file mode 100644 index 0000000..a0377c5 --- /dev/null +++ b/01.Pool/pom.xml @@ -0,0 +1,31 @@ + + + 4.0.0 + + 01 + Pool + 1.0-SNAPSHOT + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + + + + + + + junit + junit + 4.12 + + + + + \ No newline at end of file diff --git a/01.Pool/src/main/java/ru/spbau/mit/alyokhina/LightExecutionException.java b/01.Pool/src/main/java/ru/spbau/mit/alyokhina/LightExecutionException.java new file mode 100644 index 0000000..745c303 --- /dev/null +++ b/01.Pool/src/main/java/ru/spbau/mit/alyokhina/LightExecutionException.java @@ -0,0 +1,8 @@ +package ru.spbau.mit.alyokhina; + +/** if in calculating supplier of the LightFuture we caught an exception, we throw LightExecutionException */ +public class LightExecutionException extends Exception { + public LightExecutionException(String msg) { + super(msg); + } +} diff --git a/01.Pool/src/main/java/ru/spbau/mit/alyokhina/LightFuture.java b/01.Pool/src/main/java/ru/spbau/mit/alyokhina/LightFuture.java new file mode 100644 index 0000000..6f53c17 --- /dev/null +++ b/01.Pool/src/main/java/ru/spbau/mit/alyokhina/LightFuture.java @@ -0,0 +1,22 @@ +package ru.spbau.mit.alyokhina; + +import java.util.function.Function; + +/** Interface for tasks accepted for execution */ +public interface LightFuture { + /** if task was calculated then return true, else - false */ + boolean isReady(); + + /** + * Calculate supplier + * @return the value obtained + * @throws LightExecutionException if in calculating supplier of the LightFuture we caught an exception + */ + T get() throws LightExecutionException; + + /** + * Accepts an object of type Function that can be applied to the result of this task T and returns a new task T accepted for execution + * @return new task type of T + */ + LightFuture thenApply(Function function); +} diff --git a/01.Pool/src/main/java/ru/spbau/mit/alyokhina/Pool.java b/01.Pool/src/main/java/ru/spbau/mit/alyokhina/Pool.java new file mode 100644 index 0000000..fe008bb --- /dev/null +++ b/01.Pool/src/main/java/ru/spbau/mit/alyokhina/Pool.java @@ -0,0 +1,165 @@ +package ru.spbau.mit.alyokhina; + +import java.util.ArrayDeque; +import java.util.Queue; +import java.util.function.Function; +import java.util.function.Supplier; + +/** + * Task pool with a fixed number of threads + */ +public class Pool { + /** + * All created threads + */ + private Thread threads[]; + + /** + * Accepted tasks + */ + private final Queue tasks = new ArrayDeque<>(); + + /** + * Constructor + * + * @param n number of threads + */ + public Pool(int n) { + threads = new Thread[n]; + for (int i = 0; i < threads.length; i++) { + threads[i] = new Thread(() -> { + LightFutureImpl cur; + try { + while (!Thread.interrupted()) { + synchronized (tasks) { + while (tasks.isEmpty()) { + tasks.wait(); + } + cur = tasks.poll(); + } + cur.calculate(); + } + } + catch (InterruptedException ignored) { + } + } + ); + threads[i].start(); + } + } + + /** + * Shutting down all threads + */ + public void shutdown() { + for (Thread thread : threads) { + thread.interrupt(); + } + } + + /** Return all threads */ + public Thread[] getThreads() { + return threads; + } + + /** + * Add new task and notify threads that are waiting for a new task + */ + public LightFuture add(Supplier supplier) { + LightFutureImpl newTask = new LightFutureImpl(supplier); + synchronized (tasks) { + tasks.add(newTask); + tasks.notify(); + } + return newTask; + } + + /** + * Class for task implements LightFuture + */ + private class LightFutureImpl implements LightFuture { + /** + * value was calculated + */ + private boolean ready = false; + + /** + * Error in calculating the supplier + */ + private LightExecutionException exception; + private Supplier supplier; + /** + * the value that was received when calculating the supplier + */ + private T ans = null; + + /** + * Constructor + * + * @param supplier for calculating value + */ + public LightFutureImpl(Supplier supplier) { + this.supplier = supplier; + } + + /** + * Value was calculated + * + * @return true if value was calculated, false - else + */ + @Override + public boolean isReady() { + return ready; + } + + /** + * Calculate task + */ + private synchronized void calculate() { + if (!ready) { + try { + ans = supplier.get(); + } catch (Exception e) { + exception = new LightExecutionException(e.getMessage()); + } + ready = true; + notifyAll(); + } + } + + /** + * expect calculation if not calculated yet + * + * @return result which was calculated + * @throws LightExecutionException if in calculating supplier of the LightFuture we caught an exception + */ + public synchronized T get() throws LightExecutionException { + while (!ready) { + try { + wait(); + } catch (InterruptedException e) { + throw new LightExecutionException("Interrupted"); + } + } + if (exception != null) { + throw exception; + } + return ans; + } + + /** + * Accepts an object of type Function that can be applied to the result of this task T and returns a new task E accepted for execution + * + * @return new task type of E + */ + public LightFuture thenApply(Function function) { + return add(() -> { + try { + return function.apply(get()); + } catch (LightExecutionException e) { + throw new RuntimeException(e.getMessage(), e.getCause()); + } + }); + } + } +} \ No newline at end of file diff --git a/01.Pool/src/test/java/ru/spbau/mit/alyokhina/PoolTest.java b/01.Pool/src/test/java/ru/spbau/mit/alyokhina/PoolTest.java new file mode 100644 index 0000000..9a4884a --- /dev/null +++ b/01.Pool/src/test/java/ru/spbau/mit/alyokhina/PoolTest.java @@ -0,0 +1,100 @@ +package ru.spbau.mit.alyokhina; + +import org.junit.Test; + +import static org.junit.Assert.*; + +public class PoolTest { + @Test + public void testConstructor() { + Pool threadPools = new Pool<>(5); + for (int i = 0; i < 5; i++) { + final int j = i; + threadPools.add(() -> j); + } + } + + /** + * @throws LightExecutionException if in calculating supplier of the LightFuture we caught an exception + */ + @Test + public void testWithOneThread() throws LightExecutionException { + Pool threadPools = new Pool<>(1); + LightFuture task = threadPools.add(() -> 1408); + assertEquals((Integer) 1408, task.get()); + } + + + /** + * @throws LightExecutionException if in calculating supplier of the LightFuture we caught an exception + */ + @Test + public void testAdd() throws LightExecutionException { + Pool threadPools = new Pool<>(15); + LightFuture tasks[] = new LightFuture[20]; + for (int i = 0; i < 20; i++) { + final int j = i; + tasks[i] = threadPools.add(() -> j); + } + + for (int i = 0; i < 20; i++) { + assertEquals((Integer) i, tasks[i].get()); + } + + } + + @Test(expected = LightExecutionException.class) + public void testLightExecutionException() throws LightExecutionException { + Pool threadPools = new Pool<>(15); + LightFuture tasks[] = new LightFuture[5]; + for (int i = 0; i < 5; i++) { + tasks[i] = threadPools.add(() -> { + throw new RuntimeException(""); + }); + } + for (int i = 0; i < 5; i++) { + tasks[i].get(); + } + } + + @Test + public void testShutdown() { + Pool threadPools = new Pool<>(15); + LightFuture tasks[] = new LightFuture[20]; + for (int i = 0; i < 20; i++) { + final int j = i; + tasks[i] = threadPools.add(() -> j); + } + threadPools.shutdown(); + try { + Thread.sleep(3000); + } catch (InterruptedException e) { + System.out.println(e.getMessage()); + } + Thread[] threads = threadPools.getThreads(); + for (int i = 0; i < threads.length; i++) { + assertEquals(false, threads[i].isAlive()); + } + } + + /** + * @throws LightExecutionException if in calculating supplier of the LightFuture we caught an exception + */ + @Test + public void tesThenApplyOnce() throws LightExecutionException { + Pool threadPools = new Pool<>(15); + LightFuture task = threadPools.add(() -> 5).thenApply(x -> x * 2); + assertEquals((Integer) 10, task.get()); + } + + /** + * @throws LightExecutionException if in calculating supplier of the LightFuture we caught an exception + */ + @Test + public void tesThenApply() throws LightExecutionException { + Pool threadPools = new Pool<>(15); + LightFuture task = threadPools.add(() -> 5).thenApply(x -> x * 2).thenApply(x -> x + 4); + assertEquals((Integer) 14, task.get()); + } + +} \ No newline at end of file diff --git a/buildscript.sh b/buildscript.sh new file mode 100755 index 0000000..1cc07e7 --- /dev/null +++ b/buildscript.sh @@ -0,0 +1,9 @@ +#!/bin/bash +files=$(find . -maxdepth 1 -type d | grep "./0.*") +for file in $files +do + cd $file + mvn test -B + cd ../ +done +