/*
 * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code 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 General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package com.sun.util.reentrant;

import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;

/**
 * This abstract ReentrantContextProvider helper class manages the creation,
 * storage, and retrieval of concrete ReentrantContext instances which can be
 * subclassed to hold cached contextual data.
 *
 * It supports reentrancy as every call to acquire() provides a new unique context
 * instance that must later be returned for reuse by a call to release(ctx)
 * (typically in a try/finally block).
 *
 * It has a couple of abstract implementations which store references in a queue
 * and/or thread-local storage.
 * The Providers can be configured to hold ReentrantContext instances in memory
 * using hard, soft or weak references.
 *
 * The acquire() and release() methods are used to retrieve and return the contexts.
 *
 * The {@code newContext()} method remains abstract in all implementations and
 * must be provided by the module to create a new subclass of ReentrantContext
 * with the appropriate contextual data in it.
 *
 * Sample Usage:
 * - create a subclass ReentrantContextImpl to hold the thread state:
 *
 * static final class ReentrantContextImpl extends ReentrantContext {
 *     // specific cached data
 * }
 *
 * - create the appropriate ReentrantContextProvider:
 *
 * private static final ReentrantContextProvider<ReentrantContextImpl> contextProvider =
 *     new ReentrantContextProviderTL<ReentrantContextImpl>(ReentrantContextProvider.REF_WEAK)
 *     {
 *         @Override
 *         protected ReentrantContextImpl newContext() {
 *             return new ReentrantContextImpl();
 *         }
 *     };
 * ...
 * void someMethod() {
 *     ReentrantContextImpl ctx = contextProvider.acquire();
 *     try {
 *         // use the context
 *     } finally {
 *         contextProvider.release(ctx);
 *     }
 * }
 *
 * @param <K> ReentrantContext subclass
 *
 * @see ReentrantContext
 */
public abstract class ReentrantContextProvider<K extends ReentrantContext>
{
    // thread-local storage: inactive
    static final byte USAGE_TL_INACTIVE = 0;
    // thread-local storage: in use
    static final byte USAGE_TL_IN_USE = 1;
    // CLQ storage
    static final byte USAGE_CLQ = 2;

    // hard reference
    public static final int REF_HARD = 0;
    // soft reference
    public static final int REF_SOFT = 1;
    // weak reference
    public static final int REF_WEAK = 2;

    /* members */
    // internal reference type
    private final int refType;

    /**
     * Create a new ReentrantContext provider using the given reference type
     * among hard, soft or weak
     *
     * @param refType reference type
     */
    protected ReentrantContextProvider(final int refType) {
        this.refType = refType;
    }

    /**
     * Create a new ReentrantContext instance
     *
     * @return new ReentrantContext instance
     */
    protected abstract K newContext();

    /**
     * Give a ReentrantContext instance for the current thread
     *
     * @return ReentrantContext instance
     */
    public abstract K acquire();

    /**
     * Restore the given ReentrantContext instance for reuse
     *
     * @param ctx ReentrantContext instance
     */
    public abstract void release(K ctx);

    @SuppressWarnings("unchecked")
    protected final Reference<K> getOrCreateReference(final K ctx) {
        if (ctx.reference == null) {
            // Create the reference:
            switch (refType) {
                case REF_HARD:
                    ctx.reference = new HardReference<K>(ctx);
                    break;
                case REF_SOFT:
                    ctx.reference = new SoftReference<K>(ctx);
                    break;
                default:
                case REF_WEAK:
                    ctx.reference = new WeakReference<K>(ctx);
                    break;
            }
        }
        return (Reference<K>) ctx.reference;
    }

    /* Missing HardReference implementation */
    static final class HardReference<V> extends WeakReference<V> {
        // kept strong reference:
        private final V strongRef;

        HardReference(final V referent) {
            // no referent needed for the parent WeakReference:
            super(null);
            this.strongRef = referent;
        }

        @Override
        public V get() {
            return strongRef;
        }
    }
}
