package org.jenkinsci.plugins.lucene.search.databackend;

import org.apache.log4j.Logger;

import java.util.*;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

public class BurstExecutor<T> {
    private static final Logger LOGGER = Logger.getLogger(BurstExecutor.class);
    private final LinkedBlockingQueue<T> workQueue = new LinkedBlockingQueue<T>();
    private final TreeSet<WorkerThread> activeThreads = new TreeSet<WorkerThread>();
    private final RunWithArgument<T> worker;
    private final int maxThreads;
    private boolean started;

    private BurstExecutor(RunWithArgument<T> worker, int maxThreads) {
        this.worker = worker;
        this.maxThreads = maxThreads;
    }

    public void add(T workload) {
        workQueue.add(workload);
        if (started) {
            ensureEnoughThreadToFinishJob();
        }
    }

    private synchronized void ensureEnoughThreadToFinishJob() {
        if (!workQueue.isEmpty() && activeThreads.size() < maxThreads) {
            WorkerThread thread = new WorkerThread();
            activeThreads.add(thread);
            thread.start();
        }
    }

    private synchronized void removeThread(WorkerThread wt) {
        activeThreads.remove(wt);
    }

    private class WorkerThread extends Thread {
        @Override
        public void run() {
            try {
                while (!workQueue.isEmpty()) {
                    try {
                        T poll = workQueue.poll(1000, TimeUnit.MILLISECONDS);
                        if (poll != null) {
                            worker.run(poll);
                        }
                    } catch (Exception e) {
                        LOGGER.error("WorkerThread exception", e);
                    }
                }
            } finally {
                removeThread(this);
            }
        }
    }

    public synchronized void waitForCompletion() throws InterruptedException {
        if (!started) {
            throw new IllegalStateException("Not started yet");
        }
        ensureEnoughThreadToFinishJob();
        while(! activeThreads.isEmpty()) {
            activeThreads.first().join();
        }
    }

    public static<T> BurstExecutor<T> create(RunWithArgument<T> worker, int maxThreads) {
        return new BurstExecutor<T>(worker, maxThreads);
    }

    public BurstExecutor<T> andStart() {
        started = true;
        int startThreads = Math.min(workQueue.size(), maxThreads);
        for(int i = 0; i < startThreads; i++) {
            ensureEnoughThreadToFinishJob();
        }
        return this;
    }
}

@@ -405,20 +405,20 @@
 e final 
-Tree
+Hash
 Set%3CWork
@@ -451,12 +451,12 @@
 new 
-Tree
+Hash
 Set%3C
@@ -579,16 +579,103 @@
 started;
+%0A    // Only for giving better names to threads%0A    private static int threadIndex = 1;
 %0A%0A    pr
@@ -1392,16 +1392,104 @@
 hread %7B%0A
+        WorkerThread() %7B%0A            super(%22WorkerThread-%22 + threadIndex++);%0A        %7D%0A%0A
         
@@ -1536,32 +1536,93 @@
           try %7B%0A
+                LOGGER.debug(%22Started thread %22 + getName());%0A
                 
@@ -1796,24 +1796,104 @@
  != null) %7B%0A
+                            LOGGER.debug(%22Procesing with thread %22 + getName());%0A
             
@@ -2046,16 +2046,34 @@
 erThread
+ %22 + getName() + %22
  excepti
@@ -2141,24 +2141,82 @@
 %7D finally %7B%0A
+                LOGGER.debug(%22Quit thread %22 + getName());%0A
             
@@ -2281,29 +2281,16 @@
  public 
-synchronized 
 void wai
@@ -2487,94 +2487,393 @@
     
-while(! activeThreads.isEmpty()) %7B%0A            activeThreads.first().join();%0A        %7D
+WorkerThread workerThread;%0A        while((workerThread = getFirstWorkerThread()) != null) %7B%0A            workerThread.join();%0A        %7D%0A    %7D%0A%0A    private synchronized WorkerThread getFirstWorkerThread() %7B%0A        WorkerThread workerThread = null;%0A        if (!activeThreads.isEmpty()) %7B%0A            workerThread = activeThreads.iterator().next();%0A        %7D%0A        return workerThread;
 %0A   

Fix deadlocks and wrong Set-type