forked from MagicBane/Server
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
192 lines
5.4 KiB
192 lines
5.4 KiB
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . |
|
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· |
|
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ |
|
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ |
|
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ |
|
// Magicbane Emulator Project © 2013 - 2022 |
|
// www.magicbane.com |
|
|
|
|
|
package engine.pooling; |
|
|
|
import org.pmw.tinylog.Logger; |
|
|
|
import java.util.ArrayList; |
|
|
|
public abstract class ObjectPool<T> { |
|
protected final static int DEFAULT_SIZE = 1000; |
|
|
|
// Simple + quick list |
|
private final ArrayList<T> pool; |
|
|
|
/** |
|
* Once the ArrayList fills to <b>threshold</b>, subsequent .put() calls |
|
* result in the object being thrown away. Default is 75% of supplied |
|
* <b>size</b>. |
|
*/ |
|
private int threshold; |
|
|
|
/** |
|
* Constructor that allows the caller to specify initial array size and |
|
* percent of prefill. |
|
* |
|
* @param size - initial size for the containing array (pool) |
|
*/ |
|
public ObjectPool(int size) { |
|
if (size == 0) { |
|
size = DEFAULT_SIZE; |
|
} |
|
|
|
threshold = (int) (0.75 * size); |
|
|
|
this.pool = new ArrayList<>(size); |
|
|
|
} |
|
|
|
/** |
|
* Default Constructor that uses default initial Array size and percent |
|
* prefill values |
|
*/ |
|
public ObjectPool() { |
|
this(DEFAULT_SIZE); |
|
} |
|
|
|
/** |
|
* Forces pool to add <i>numberOfObjects</i> to the pool. This may cause |
|
* internal ArrayList to resize. |
|
* |
|
* @param numberOfObjects |
|
*/ |
|
public void fill(int numberOfObjects) { |
|
for (int i = 0; i < numberOfObjects; ++i) |
|
this.makeAndAdd(); |
|
} |
|
|
|
/** |
|
* Forces subclasses to implement factory routine for object creation. |
|
*/ |
|
protected abstract T makeNewObject(); |
|
|
|
/** |
|
* Forces subclasses to implement a way to reset object to a reusable state. |
|
*/ |
|
protected abstract void resetObject(T obj); |
|
|
|
/** |
|
* Generic Get routine. If the pool is empty, then one (or more) of T type |
|
* objects will be created. If more than one T is created, then they will be |
|
* put into the pool. One T will always be created and returned. |
|
*/ |
|
public T get() { |
|
synchronized (pool) { |
|
//Logger.debug("Objectpool.get(). Pool size before get: " + pool.size()); |
|
if (pool.size() > 0) { |
|
return pool.remove(0); |
|
} |
|
} |
|
|
|
this.handlePoolExhaustion(); |
|
|
|
T obj = this.makeNewObject(); |
|
|
|
if (obj == null) { |
|
Logger.error("Pooling failure: Object creation failed."); |
|
} |
|
return obj; |
|
} |
|
|
|
protected void handlePoolExhaustion() { |
|
Logger.debug("Pool exhausted, making more objects."); |
|
|
|
// If none exist, make (and pool) a few |
|
// Dont sync the loop, let the makeNewObject() |
|
// call return before locking the pool object |
|
for (int i = 0; i < 5; ++i) |
|
this.makeAndAdd(); |
|
|
|
} |
|
|
|
/** |
|
* Generic put routine. If the current pool size is below threshold, this |
|
* object will be pooled, otherwise it will be NULL'ed and scheduled for |
|
* Garbage Collection |
|
* |
|
* @param obj |
|
*/ |
|
public void put(T obj) { |
|
synchronized (pool) { |
|
if (pool.size() >= this.threshold) { |
|
//Logger.debug("Objectpool.put() rejected. Pool size: " + pool.size()); |
|
return; |
|
} |
|
//Logger.debug("Objectpool.put(). Pool size: " + pool.size()); |
|
this.resetObject(obj); |
|
this.pool.add(obj); |
|
} |
|
} |
|
|
|
/** |
|
* Helper method. Attempts to create and add a new <i>T</i> object. If this |
|
* fails, an error is logged. |
|
*/ |
|
protected final void makeAndAdd() { |
|
T obj = this.makeNewObject(); |
|
if (obj == null) { |
|
Logger.error("Pooling failure: Object creation failed."); |
|
} else { |
|
this.put(obj); |
|
} |
|
} |
|
|
|
/** |
|
* @return the current size of the pool, not the maximum capacity of the |
|
* Array holding the pool. |
|
*/ |
|
public final int getPoolSize() { |
|
synchronized (this.pool) { |
|
return this.pool.size(); |
|
} |
|
} |
|
|
|
/** |
|
* Culls the pool and removes half of the stored objects. Removed objects |
|
* are NULL'ed and scheduled form Garbage Collection. |
|
* |
|
* @return the number of Objects removed from the pool. |
|
*/ |
|
public int cullHalf() { |
|
int full, half; |
|
synchronized (this.pool) { |
|
full = this.pool.size(); |
|
if (full < 1) { |
|
return full; |
|
} |
|
|
|
half = (full / 2); |
|
|
|
for (int i = 0; i < (full / 2); ++i) { |
|
T obj = this.get(); |
|
obj = null; // Null out for GC |
|
} |
|
} |
|
return half; |
|
} |
|
|
|
/** |
|
* @return the threshold |
|
*/ |
|
public final int getThreshold() { |
|
return threshold; |
|
} |
|
|
|
/** |
|
* @param threshold the threshold to set |
|
*/ |
|
public void setThreshold(int threshold) { |
|
if (threshold < 0) |
|
return; |
|
|
|
this.threshold = threshold; |
|
} |
|
|
|
}
|
|
|