package io.ifar.skidroad.prepping;

import io.ifar.skidroad.LogFile;
import io.ifar.skidroad.tracking.LogFileTracker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * A WritingWorkerFactory which creates dummy workers and provides explict controls over when the workers run, consume
 * queue items, and exit.
 */
public class DummyPrepWorkerFactory implements PrepWorkerFactory {
    private final static Logger LOG = LoggerFactory.getLogger(DummyPrepWorkerFactory.class);

    public static class DummyWorker<T> implements Runnable {
        private final CountDownLatch runLatch;
        private final CountDownLatch exitLatch;

        public DummyWorker(CountDownLatch runLatch, CountDownLatch exitLatch) {
            this.runLatch = runLatch;
            this.exitLatch = exitLatch;
        }

        private final static Logger LOG = LoggerFactory.getLogger(DummyWorker.class);
        @Override
        public void run() {
            try {
                LOG.debug("Worker {} is alive.", Thread.currentThread().getName());
                if (!runLatch.await(1, TimeUnit.SECONDS))
                    LOG.error("Unreported test failure; took too long for run latch to be released.");
                if (!exitLatch.await(1, TimeUnit.SECONDS))
                    LOG.error("Unreported test failure; took too long for exit latch to be released.");
            } catch (InterruptedException e) {
                LOG.debug("Worker {} has been asked to exit.", Thread.currentThread().getName());
            }
            LOG.debug("Worker {} exiting.", Thread.currentThread().getName());
        }
    }


    private List<CountDownLatch> runCountLatches = new LinkedList<>();
    private List<Runnable> workersCreated = new ArrayList<>();
    private List<CountDownLatch> exitLatches = new ArrayList<>();

    @Override
    public Runnable buildWorker(LogFile logFileRecord, LogFileTracker tracker) {
        final CountDownLatch latch = new CountDownLatch(1);
        exitLatches.add(latch);

        Runnable worker = new Runnable() {
            @Override
            public void run() {
                try {
                    LOG.debug("Worker {} is alive.", Thread.currentThread().getName());
                    for (CountDownLatch creationLatch : runCountLatches)
                        creationLatch.countDown();
                    if (!latch.await(1, TimeUnit.SECONDS))
                        LOG.error("Unreported test failure; took too long for exit latch to be released.");
                } catch (InterruptedException e) {
                    LOG.debug("Worker {} has been asked to exit.", Thread.currentThread().getName());
                }
            }
        };

        workersCreated.add(worker);
        return worker;

    }

    /**
     * Create a latch that will be released when the specified number of new workers have been run.
     */
    public CountDownLatch getRunCountLatch(int creationsToExpect) {
        CountDownLatch latch = new CountDownLatch(creationsToExpect);
        runCountLatches.add(latch);
        return latch;
    }

    /**
     * Allows next worker to exit
     */
    public void exitNextWorker() {
        CountDownLatch latch = exitLatches.remove(0);
        latch.countDown();
    }

    /**
     * Lets all workers exit
     */
    public void stop() {
        for (CountDownLatch latch : exitLatches)
            latch.countDown();
    }
}

@@ -1873,70 +1873,13 @@
 ate 
-List%3CRunnable%3E workersCreated = new ArrayList%3C%3E();%0A    private
+final
  Lis
@@ -2085,16 +2085,57 @@
 tch(1);%0A
+        synchronized (exitLatches) %7B%0A    
         
@@ -2157,16 +2157,26 @@
 (latch);
+%0A        %7D
 %0A%0A      
@@ -2866,45 +2866,8 @@
   %7D;
-%0A%0A        workersCreated.add(worker);
 %0A   
@@ -2886,17 +2886,16 @@
 worker;%0A
-%0A
     %7D%0A%0A 
@@ -3293,24 +3293,65 @@
 tWorker() %7B%0A
+        synchronized (exitLatches) %7B%0A    
         Coun
@@ -3392,32 +3392,36 @@
 ove(0);%0A        
+    
 latch.countDown(
@@ -3419,24 +3419,34 @@
 ountDown();%0A
+        %7D%0A
     %7D%0A%0A    /
@@ -3506,24 +3506,65 @@
 id stop() %7B%0A
+        synchronized (exitLatches) %7B%0A    
         for 
@@ -3604,32 +3604,36 @@
 es)%0A            
+    
 latch.countDown(
@@ -3631,20 +3631,30 @@
 ountDown();%0A
+        %7D%0A
     %7D%0A%7D%0A

Fix concurrency bug in PrepWorkerManagerTest