/*
 * Javolution - Java(TM) Solution for Real-Time and Embedded Systems
 * Copyright (C) 2009 - Javolution (http://javolution.org/)
 * All rights reserved.
 * 
 * Permission to use, copy, modify, and distribute this software is
 * freely granted, provided that this notice is preserved.
 */
package javolution.context;

import java.lang.ThreadLocal;
import javolution.util.FastMap;
import javolution.util.FastTable;

/**
 * <p> This class represents a shared pool context for object
 *     allocation/recycling. Unlike {@link HeapContext}, objects recycled
 *     can be reused by all threads running in a {@link PoolContext}.[code]
 *         // To avoid each new thread to enter a PoolContext, the default
 *         // allocator context can be configured to PoolContext.class
 *         Thread thread1 = new Thread() {
 *            public void run() {
 *                PoolContext.enter();
 *                try {
 *                    ...
 *                    myObject = myObjectFactory.object(); // Returns an object potentially recycled by thread2.
 *                    ...
 *                } finally {
 *                    PoolContext.exit();
 *                }
 *            }};
 *        Thread thread2 = new Thread() {
 *            public void run() {
 *                PoolContext.enter();
 *                try {
 *                    ...
 *                    myObjectFactory.recycle(myObject); // Recycles an object potentially created by thread1.
 *                    ...
 *                } finally {
 *                    PoolContext.exit();
 *                }
 *            }};
 *     [/code]</p>
 *
 * @author  <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
 * @version 5.3, March 10, 2009
 */
public class PoolContext extends AllocatorContext {

    /**
     * Holds the factory to allocator mapping (per thread).
     */
    private static final ThreadLocal FACTORY_TO_ALLOCATOR = new ThreadLocal() {

        protected Object initialValue() {
            return new FastMap();
        }
    };
    /**
     * Holds the allocators which have been activated (per thread).
     */
    private static final ThreadLocal ACTIVE_ALLOCATORS = new ThreadLocal() {

        protected Object initialValue() {
            return new FastTable();
        }
    };

    /**
     * Enters a pool context.
     */
    public static void enter() {
         Context.enter(PoolContext.class);
    }

    /**
     * Exits the current pool context.
     *
     * @throws ClassCastException if the context is not a pool context.
     */
    public static void exit() {
         Context.exit(PoolContext.class);
    }

    /**
     * Default constructor.
     */
    public PoolContext() {
    }

    // Overrides.
    protected void deactivate() {
        FastTable allocators = (FastTable) ACTIVE_ALLOCATORS.get();
        for (int i = 0, n = allocators.size(); i < n;) {
            ((Allocator) allocators.get(i++)).user = null;
        }
        allocators.clear();
    }

    // Overrides.
    protected Allocator getAllocator(ObjectFactory factory) {
        final FastMap factoryToAllocator = (FastMap) FACTORY_TO_ALLOCATOR.get();
        PoolAllocator allocator = (PoolAllocator) factoryToAllocator.get(factory);
        if (allocator == null) {
            allocator = new PoolAllocator(factory);
            factoryToAllocator.put(factory, allocator);
        }
        if (allocator.user == null) { // Activate.
            allocator.user = Thread.currentThread();
            FastTable activeAllocators = (FastTable) ACTIVE_ALLOCATORS.get();
            activeAllocators.add(allocator);
        }
        return allocator;
    }

    // Overrides.
    protected void enterAction() {
        getOuter().getAllocatorContext().deactivate();
    }

    // Overrides.
    protected void exitAction() {
        this.deactivate();
    }

    // Holds heap allocator implementation.
    private static final class PoolAllocator extends Allocator {

        private static final FastMap FACTORY_TO_POOL = new FastMap();
        private final ObjectFactory _factory;
        private final FastTable _recycled;

        public PoolAllocator(ObjectFactory factory) {
            _factory = factory;
            synchronized (FACTORY_TO_POOL) {
                FastTable recycled = (FastTable) FACTORY_TO_POOL.get(factory);
                if (recycled == null) {
                    recycled = new FastTable();
                    FACTORY_TO_POOL.put(factory, recycled);
                }
                _recycled = recycled;
            }
        }

        protected Object allocate() {
            if (_recycled.isEmpty()) return _factory.create();
            synchronized (_recycled) {
                return _recycled.removeLast();
            }
        }

        protected void recycle(Object object) {
            if (_factory.doCleanup())
                _factory.cleanup(object);
            synchronized (_recycled) {
               _recycled.addLast(object);
            }
        }

        public String toString() {
            return "Pool allocator for " + _factory.getClass();
        }
    }
}