/* * Copyright (c) 2003-onwards Shaven Puppy Ltd * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of 'Shaven Puppy' nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.shavenpuppy.jglib.util; import org.lwjgl.Sys; /** * A PriorityPool takes care of allocating objects by priority from a finite pool. */ public class PriorityPool { /** Pool wrapper */ private final class PoolWrapper { /** The index into the pool, when active */ private int index; /** The Pooled thing's owner */ private Object owner; /** The age of this Pooled thing */ private long age; /** The actual pooled object */ private final PriorityPooled pooled; /** * Constructor */ PoolWrapper(PriorityPooled pooled) { this.pooled = pooled; } int getPriority() { return pooled.getPriority(); } boolean isLocked() { return pooled.isLocked(); } /** * @return true if this is active */ boolean isActive() { return owner != null; } /** * Deactivate this object. The owner is cleared. */ void deactivate() { if (isActive()) { pooled.deactivate(); owner = null; returnToPool(this); } } /** * Allocate this object. The object's priority is set and it becomes active. * @param priority The object's new priority * @param owner The object's new owner */ void allocate(int priority, Object owner) { this.owner = owner; age = Sys.getTime(); pooled.allocate(owner); pooled.setPriority(priority); } /** * Perform ticking. If this object is active then we check to see whether it * should be deactivated. */ void tick() { if (isActive()) { pooled.tick(); if (!pooled.isActive()) { deactivate(); } } } /** * Reset to initial 'unused' state */ void reset() { deactivate(); } } /** The pool */ private final PoolWrapper[] pool; /** Copy of the pool */ private final PoolWrapper[] poolCopy; /** The number of active entries */ private int inUse; /** * Constructor for PriorityPool. * @param pool[] the pooled objects, which should be all the same class, and unique, and not null */ public PriorityPool(PriorityPooled[] pooled) { pool = new PoolWrapper[pooled.length]; poolCopy = new PoolWrapper[pooled.length]; for (int i = 0; i < pooled.length; i ++) { pool[i] = new PoolWrapper(pooled[i]); pool[i].index = i; } } /** * Allocate an object from the pool. The rule is as follows: * <ol> * <li>If the pool contains an inactive entry, it is activated and returned.</li> * <li>Otherwise if no entries in the pool are at a lower priority, null is returned.</li> * <li>Otherwise the oldest entry of the same priority is deactivated and returned.</li> * </ol> * @param priority The priority of this allocation * @param owner The new owner of the object * @return an object from the pool, or null if no object can be returned. */ public PriorityPooled allocate(int priority, Object owner) { PriorityPooled ret = null; // If we haven't used all the slots, just return the next unused slot // right away: if (inUse < pool.length) { pool[inUse].allocate(priority, owner); return pool[inUse ++].pooled; } // Search through the list of unlocked things finding the oldest lowest priority one: int lowestPriority = 999999; long oldestAge = Long.MAX_VALUE;//0x7FFFFFFFFFFFFFFFL; int oldestIndex = -1; for (int i = 0; i < inUse ; i ++) { if (pool[i].isLocked()) { continue; } if (pool[i].getPriority() == lowestPriority) { if (pool[i].age < oldestAge) { oldestAge = pool[i].age; oldestIndex = i; } } else if (pool[i].getPriority() < lowestPriority) { lowestPriority = pool[i].getPriority(); oldestAge = pool[i].age; oldestIndex = i; } } if (oldestIndex >= 0) { // Get the actual object, because when it's deactivated it'll get moved PoolWrapper pw = pool[oldestIndex]; if (pw.getPriority() <= priority) { // Deactivate someone else's and snatch it pw.deactivate(); inUse ++; pw.allocate(priority, owner); ret = pw.pooled; } } return ret; } /** * Return a pooled item back to the pool of inactive things. * @param item The thing to return */ private void returnToPool(PoolWrapper item) { pool[item.index] = pool[inUse - 1]; pool[item.index].index = item.index; pool[inUse - 1] = item; item.index = -- inUse; } /** * Tick. Call this every frame. This will check each active item the pool to see * if it has deactivated. */ public void tick() { final int n = inUse; System.arraycopy(pool, 0, poolCopy, 0, inUse); for (int i = 0; i < n; i ++) { poolCopy[i].tick(); } } /** * Reset the pool. All pooled objects are deactivated. */ public void reset() { for (int i = inUse; --i >= 0; ) { pool[i].reset(); } } }