/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2009, Red Hat Middleware LLC, and individual contributors
 * as indicated by the @author tags. See the copyright.txt file in the
 * distribution for a full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.jboss.threads;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import java.util.concurrent.Executor;
import java.util.concurrent.RejectedExecutionException;
import org.jboss.logging.Logger;

/**
 * A task which depends on other tasks, and which may have tasks depending upon it.  Such a task is automatically
 * run when using a provided executor when all its dependencies are satisfied.
 */
public final class Dependency {
    private static final Logger log = Logger.getLogger(Dependency.class);

    private static final AtomicIntegerFieldUpdater<Dependency> depUpdater = AtomicIntegerFieldUpdater.newUpdater(Dependency.class, "remainingDependencies");

    /**
     * Number of dependencies left before this task can start.
     */
    @SuppressWarnings({ "UnusedDeclaration" })
    private volatile int remainingDependencies;

    private final Executor executor;
    private final Object lock = new Object();
    private Runner runner;
    private State state;

    Dependency(final Executor executor, final Runnable runnable, final int initialDepCount) {
        this.executor = executor;
        synchronized (lock) {
            runner = new Runner(runnable);
            state = State.WAITING;
            remainingDependencies = initialDepCount;
        }
    }

    void addDependent(Dependency task) {
        synchronized (lock) {
            switch (state) {
                case WAITING:
                case RUNNING: runner.dependents.add(task); return;
                case FAILED: return;
                case DONE: break; // fall out of lock
                default: throw new IllegalStateException();
            }
        }
        task.dependencyFinished();
    }

    void dependencyFinished() {
        final AtomicIntegerFieldUpdater<Dependency> updater = depUpdater;
        final int res = updater.decrementAndGet(this);
        if (res == 0) {
            final Dependency.Runner runner = this.runner;
            synchronized (lock) {
                try {
                    executor.execute(runner);
                    state = State.RUNNING;
                } catch (RejectedExecutionException e) {
                    log.errorf(e, "Error submitting task %s to executor", runner.runnable);
                    state = State.FAILED;
                    // clear stuff out since this object will likely be kept alive longer than these objects need to be
                    runner.runnable = null;
                    runner.dependents = null;
                    this.runner = null;
                }
            }
        } else if (res < 0) {
            // oops?
            updater.incrementAndGet(this);
        }
    }

    private void dependencyFailed() {
        final AtomicIntegerFieldUpdater<Dependency> updater = depUpdater;
        final int res = updater.decrementAndGet(this);
        if (res == 0) {
            synchronized (lock) {
                state = State.FAILED;
                final Dependency.Runner runner = this.runner;
                // clear stuff out since this object will likely be kept alive longer than these objects need to be
                runner.runnable = null;
                runner.dependents = null;
                this.runner = null;
            }
        } else if (res < 0) {
            // oops?
            updater.incrementAndGet(this);
        }
    }

    private class Runner implements Runnable {

        private List<Dependency> dependents = new ArrayList<Dependency>();
        private Runnable runnable;

        public Runner(final Runnable runnable) {
            this.runnable = runnable;
        }

        public void run() {
            boolean ok = false;
            CTH.set(Dependency.this);
            try {
                runnable.run();
                ok = true;
            } finally {
                CTH.set(null);
                final List<Dependency> tasks;
                synchronized (lock) {
                    tasks = dependents;
                    // clear stuff out in case some stupid executor holds on to the runnable
                    dependents = null;
                    runnable = null;
                    runner = null;
                    state = ok ? State.DONE : State.FAILED;
                }
                if (ok) {
                    for (Dependency task : tasks) {
                        task.dependencyFinished();
                    }
                } else {
                    for (Dependency task : tasks) {
                        task.dependencyFailed();
                    }
                }
            }
        }
    }

    private enum State {
        /**
         * Waiting for dependencies to be resolved.
         */
        WAITING,
        /**
         * Now running.
         */
        RUNNING,
        /**
         * Execution failed.
         */
        FAILED,
        /**
         * Execution completed.
         */
        DONE,
    }

    /**
     * Get the dependency task which this thread is currently running.  This may be used to add dependencies on the currently
     * running task.
     *
     * @return the currently running task, or {@code null} if the current thread is not running a dependency task
     */
    public static Dependency currentTask() {
        return CTH.get();
    }

    private static final ThreadLocal<Dependency> CTH = new ThreadLocal<Dependency>();
}

@@ -3935,36 +3935,8 @@
 s);%0A
-        if (res == 0) %7B%0A    
     
@@ -3953,36 +3953,32 @@
 onized (lock) %7B%0A
-    
             stat
@@ -3999,36 +3999,32 @@
 ED;%0A            
-    
 final Dependency
@@ -4045,36 +4045,32 @@
  = this.runner;%0A
-    
             // c
@@ -4169,36 +4169,32 @@
  be%0A            
-    
 runner.runnable 
@@ -4193,36 +4193,32 @@
 unnable = null;%0A
-    
             runn
@@ -4243,36 +4243,32 @@
 ll;%0A            
-    
 this.runner = nu
@@ -4271,36 +4271,32 @@
 = null;%0A        
-    
 %7D%0A        %7D else
@@ -4281,39 +4281,32 @@
       %7D%0A        
-%7D else 
 if (res %3C 0) %7B%0A 

Fix failed task handling