package apoc;

import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Transaction;
import org.neo4j.kernel.impl.util.JobScheduler;

import java.util.List;
import java.util.concurrent.*;
import java.util.function.Consumer;

public class Pools {
    public final static ExecutorService SINGLE = createSinglePool();
    public final static ExecutorService DEFAULT = createDefaultPool();
    public final static ScheduledExecutorService SCHEDULED = createScheduledPool();
    public static JobScheduler NEO4J_SCHEDULER = null;

    private Pools() {
        throw new UnsupportedOperationException();
    }

    public static ExecutorService createDefaultPool() {
        int threads = Runtime.getRuntime().availableProcessors()*2;
        int queueSize = threads * 25;
        return new ThreadPoolExecutor(threads / 2, threads, 30L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(queueSize),
                new CallerBlocksPolicy());
//                new ThreadPoolExecutor.CallerRunsPolicy());
    }
    static class CallerBlocksPolicy implements RejectedExecutionHandler {
        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
            if (!executor.isShutdown()) {
                try {
                    // block caller for 100ms
                    Thread.sleep(100);
                } catch (InterruptedException ie) {
                    // ignore
                }
                try {
                    // submit again
                    executor.submit(r).get();
                } catch (InterruptedException | ExecutionException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

    public static int getNoThreadsInDefaultPool() {
        return  Runtime.getRuntime().availableProcessors();
    }

    private static ExecutorService createSinglePool() {
        return Executors.newSingleThreadExecutor();
    }

    private static ScheduledExecutorService createScheduledPool() {
        return Executors.newScheduledThreadPool(Math.max(1, Runtime.getRuntime().availableProcessors() / 4));
    }

    public static <T> Future<Void> processBatch(List<T> batch, GraphDatabaseService db, Consumer<T> action) {
        return DEFAULT.submit((Callable<Void>) () -> {
                try (Transaction tx = db.beginTx()) {
                    batch.forEach(action);
                    tx.success();
                }
                return null;
            }
        );
    }

    public static <T> T force(Future<T> future) throws ExecutionException {
        while (true) {
            try {
                return future.get();
            } catch (InterruptedException e) {
                Thread.interrupted();
            }
        }
    }
}

@@ -196,16 +196,63 @@
 rent.*;%0A
+import java.util.concurrent.locks.LockSupport;%0A
 import j
@@ -1273,34 +1273,8 @@
 ) %7B%0A
-                try %7B%0A    
     
@@ -1308,17 +1308,17 @@
  for 100
-m
+n
 s%0A      
@@ -1331,130 +1331,35 @@
     
-  
-  Thread.sleep(100);%0A                %7D catch (InterruptedException ie) %7B%0A                    // ignore%0A                %7D
+LockSupport.parkNanos(100);
 %0A   

change callerblocks policy to wait only for 100ns until checking for free queue of executor again