/*
 * Copyright (c) 2016 Daniel Ennis (Aikar) - MIT License
 *
 *  Permission is hereby granted, free of charge, to any person obtaining
 *  a copy of this software and associated documentation files (the
 *  "Software"), to deal in the Software without restriction, including
 *  without limitation the rights to use, copy, modify, merge, publish,
 *  distribute, sublicense, and/or sell copies of the Software, and to
 *  permit persons to whom the Software is furnished to do so, subject to
 *  the following conditions:
 *
 *  The above copyright notice and this permission notice shall be
 *  included in all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 *  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 *  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 *  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

package co.aikar.taskchain;


import co.aikar.taskchain.TaskChainTasks.Task;

import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

class SharedTaskChain<R> extends TaskChain<R> {
    private final String name;
    private final Map<String, Queue<SharedTaskChain>> sharedChains;
    private Queue<SharedTaskChain> queue;
    private boolean isReady;

    SharedTaskChain(String name, TaskChainFactory factory) {
        super(factory);
        this.sharedChains = factory.getSharedChains();
        this.name = name;

        synchronized (this.sharedChains) {
            this.queue = sharedChains.get(this.name);
            if (this.queue == null) {
                this.queue = new ConcurrentLinkedQueue<>();
                this.sharedChains.put(this.name, this.queue);
            }
            this.queue.add(this);
        }
    }

    @Override
    public void execute(Consumer<Boolean> done, BiConsumer<Exception, Task<?, ?>> errorHandler) {
        this.setErrorHandler(errorHandler);
        this.setDoneCallback((finished) -> {
            this.setDoneCallback(done);
            this.done(finished);
            processQueue();
        });

        synchronized (sharedChains) {
            isReady = true;
        }
        if (queue.peek() == this) {
            execute0();
        }
    }

    /**
     * Launches the next TaskChain in the queue if it is ready, or cleans up the queue if nothing left to do.
     */
    private void processQueue() {
        this.queue.poll(); // Remove self
        final SharedTaskChain next;
        synchronized (this.sharedChains) {
            next = this.queue.peek();
            if (next == null) {
                this.sharedChains.remove(this.name);
                return;
            }
            if (!next.isReady) {
                // Created but wasn't executed yet. Wait until the chain executes itself.
                return;
            }
        }

        next.execute0();
    }
}

@@ -1603,16 +1603,25 @@
 private 
+volatile 
 boolean 
@@ -1622,21 +1622,23 @@
 olean is
-Ready
+Pending
 ;%0A%0A    S
@@ -2458,16 +2458,21 @@
 onized (
+this.
 sharedCh
@@ -2495,22 +2495,50 @@
     
-isReady = true
+this.isPending = this.queue.peek() != this
 ;%0A  
@@ -2561,28 +2561,23 @@
 if (
-queue.peek() == this
+!this.isPending
 ) %7B%0A
@@ -3087,13 +3087,15 @@
 t.is
-Ready
+Pending
 ) %7B%0A

Fix concurrency issue with shared chains