//
//  ========================================================================
//  Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
//  ------------------------------------------------------------------------
//  All rights reserved. This program and the accompanying materials
//  are made available under the terms of the Eclipse Public License v1.0
//  and Apache License v2.0 which accompanies this distribution.
//
//      The Eclipse Public License is available at
//      http://www.eclipse.org/legal/epl-v10.html
//
//      The Apache License v2.0 is available at
//      http://www.opensource.org/licenses/apache2.0.php
//
//  You may elect to redistribute this code under either of these licenses.
//  ========================================================================
//

package org.eclipse.jetty.util;

/**
 * Utility class that splits calls to {@link #invoke(Object)} into calls to {@link #fork(Object)} or {@link #call(Object)}
 * depending on the max number of reentrant calls to {@link #invoke(Object)}.
 * <p/>
 * This class prevents {@link StackOverflowError}s in case of methods that end up invoking themselves,
 * such is common for {@link Callback#succeeded()}.
 * <p/>
 * Typical use case is:
 * <pre>
 * public void reentrantMethod(Object param)
 * {
 *     if (condition || tooManyReenters)
 *         fork(param)
 *     else
 *         call(param)
 * }
 * </pre>
 * Calculating {@code tooManyReenters} usually involves using a {@link ThreadLocal} and algebra on the
 * number of reentrant invocations, which is factored out in this class for convenience.
 * <p />
 * The same code using this class becomes:
 * <pre>
 * private final ForkInvoker invoker = ...;
 *
 * public void reentrantMethod(Object param)
 * {
 *     invoker.invoke(param);
 * }
 * </pre>
 *
 */
public abstract class ForkInvoker<T>
{
    private static final ThreadLocal<Integer> __invocations = new ThreadLocal<Integer>()
    {
        @Override
        protected Integer initialValue()
        {
            return 0;
        }
    };
    private final int _maxInvocations;

    /**
     * Creates an instance with the given max number of reentrant calls to {@link #invoke(Object)}
     * <p/>
     * If {@code maxInvocations} is zero or negative, it is interpreted
     * as if the max number of reentrant calls is infinite.
     *
     * @param maxInvocations the max number of reentrant calls to {@link #invoke(Object)}
     */
    public ForkInvoker(int maxInvocations)
    {
        _maxInvocations = maxInvocations;
    }

    /**
     * Invokes either {@link #fork(Object)} or {@link #call(Object)}.
     * If {@link #condition()} returns true, {@link #fork(Object)} is invoked.
     * Otherwise, if the max number of reentrant calls is positive and the
     * actual number of reentrant invocations exceeds it, {@link #fork(Object)} is invoked.
     * Otherwise, {@link #call(Object)} is invoked.
     * @param arg TODO
     *
     * @return true if {@link #fork(Object)} has been called, false otherwise
     */
    public boolean invoke(T arg)
    {
        boolean countInvocations = _maxInvocations > 0;
        int invocations = __invocations.get();
        if (condition() || countInvocations && invocations > _maxInvocations)
        {
            fork(arg);
            return true;
        }
        else
        {
            if (countInvocations)
                __invocations.set(invocations + 1);
            try
            {
                call(arg);
                return false;
            }
            finally
            {
                if (countInvocations)
                    __invocations.set(invocations);
            }
        }
    }

    /**
     * Subclasses should override this method returning true if they want
     * {@link #invoke(Object)} to call {@link #fork(Object)}.
     *
     * @return true if {@link #invoke(Object)} should call {@link #fork(Object)}, false otherwise
     */
    protected boolean condition()
    {
        return false;
    }

    /**
     * Executes the forked invocation
     * @param arg TODO
     */
    public abstract void fork(T arg);

    /**
     * Executes the direct, non-forked, invocation
     * @param arg TODO
     */
    public abstract void call(T arg);
}
