Added ObjectPool factory + a 'slow' one that is compatible with android
This commit is contained in:
parent
7187108f85
commit
2fb565fade
@ -19,44 +19,21 @@ package dorkbox.util.objectPool;
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.security.AccessController;
|
|
||||||
import java.security.PrivilegedExceptionAction;
|
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
import sun.misc.Unsafe;
|
import dorkbox.util.Sys;
|
||||||
|
|
||||||
|
|
||||||
public class FastObjectPool<T> {
|
class FastObjectPool<T> implements ObjectPool<T> {
|
||||||
public static final Unsafe THE_UNSAFE;
|
|
||||||
static {
|
|
||||||
try {
|
|
||||||
final PrivilegedExceptionAction<Unsafe> action = new PrivilegedExceptionAction<Unsafe>() {
|
|
||||||
@Override
|
|
||||||
public Unsafe run() throws Exception {
|
|
||||||
Class<sun.misc.Unsafe> unsafeClass = sun.misc.Unsafe.class;
|
|
||||||
Field theUnsafe = unsafeClass.getDeclaredField("theUnsafe");
|
|
||||||
theUnsafe.setAccessible(true);
|
|
||||||
Object unsafeObject = theUnsafe.get(null);
|
|
||||||
if (unsafeClass.isInstance(unsafeObject)) {
|
|
||||||
return unsafeClass.cast(unsafeObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new NoSuchFieldError("the Unsafe");
|
private static final boolean FREE = true;
|
||||||
}
|
private static final boolean USED = false;
|
||||||
};
|
|
||||||
|
|
||||||
THE_UNSAFE = AccessController.doPrivileged(action);
|
private final ObjectPoolHolder<T>[] objects;
|
||||||
}
|
private final PoolableObject<T> poolableObject;
|
||||||
catch (Exception e) {
|
|
||||||
throw new RuntimeException("Unable to load unsafe", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Holder<T>[] objects;
|
|
||||||
|
|
||||||
private volatile int takePointer;
|
private volatile int takePointer;
|
||||||
private int releasePointer;
|
private volatile int releasePointer;
|
||||||
|
|
||||||
private final int mask;
|
private final int mask;
|
||||||
private final long BASE;
|
private final long BASE;
|
||||||
@ -64,10 +41,11 @@ public class FastObjectPool<T> {
|
|||||||
private final long ASHIFT;
|
private final long ASHIFT;
|
||||||
|
|
||||||
public ReentrantLock lock = new ReentrantLock();
|
public ReentrantLock lock = new ReentrantLock();
|
||||||
private ThreadLocal<Holder<T>> localValue = new ThreadLocal<>();
|
private ThreadLocal<ObjectPoolHolder<T>> localValue = new ThreadLocal<>();
|
||||||
|
|
||||||
public FastObjectPool(PoolFactory<T> factory, int size) {
|
FastObjectPool(PoolableObject<T> poolableObject, int size) {
|
||||||
|
|
||||||
|
this.poolableObject = poolableObject;
|
||||||
int newSize = 1;
|
int newSize = 1;
|
||||||
while (newSize < size) {
|
while (newSize < size) {
|
||||||
newSize = newSize << 1;
|
newSize = newSize << 1;
|
||||||
@ -76,38 +54,48 @@ public class FastObjectPool<T> {
|
|||||||
size = newSize;
|
size = newSize;
|
||||||
|
|
||||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||||
Holder<T>[] stuff = new Holder[size];
|
ObjectPoolHolder<T>[] stuff = new ObjectPoolHolder[size];
|
||||||
this.objects = stuff;
|
this.objects = stuff;
|
||||||
|
|
||||||
for (int x=0;x<size;x++) {
|
for (int x=0;x<size;x++) {
|
||||||
this.objects[x] = new Holder<T>(factory.create());
|
this.objects[x] = new ObjectPoolHolder<T>(poolableObject.create());
|
||||||
}
|
}
|
||||||
|
|
||||||
this.mask = size-1;
|
this.mask = size-1;
|
||||||
this.releasePointer = size;
|
this.releasePointer = size;
|
||||||
this.BASE = THE_UNSAFE.arrayBaseOffset(Holder[].class);
|
this.BASE = Sys.unsafe.arrayBaseOffset(ObjectPoolHolder[].class);
|
||||||
this.INDEXSCALE = THE_UNSAFE.arrayIndexScale(Holder[].class);
|
this.INDEXSCALE = Sys.unsafe.arrayIndexScale(ObjectPoolHolder[].class);
|
||||||
this.ASHIFT = 31 - Integer.numberOfLeadingZeros((int) this.INDEXSCALE);
|
this.ASHIFT = 31 - Integer.numberOfLeadingZeros((int) this.INDEXSCALE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Holder<T> take() {
|
@Override
|
||||||
|
public ObjectPoolHolder<T> take() {
|
||||||
int localTakePointer;
|
int localTakePointer;
|
||||||
|
|
||||||
Holder<T> localObject = this.localValue.get();
|
// if we have an object available in the cache, use it instead.
|
||||||
|
ObjectPoolHolder<T> localObject = this.localValue.get();
|
||||||
if (localObject != null) {
|
if (localObject != null) {
|
||||||
if(localObject.state.compareAndSet(Holder.FREE, Holder.USED)) {
|
if (localObject.state.compareAndSet(FREE, USED)) {
|
||||||
|
this.poolableObject.activate(localObject.getValue());
|
||||||
return localObject;
|
return localObject;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sun.misc.Unsafe unsafe = Sys.unsafe;
|
||||||
|
|
||||||
while (this.releasePointer != (localTakePointer=this.takePointer)) {
|
while (this.releasePointer != (localTakePointer=this.takePointer)) {
|
||||||
int index = localTakePointer & this.mask;
|
int index = localTakePointer & this.mask;
|
||||||
Holder<T> holder = this.objects[index];
|
|
||||||
|
ObjectPoolHolder<T> holder = this.objects[index];
|
||||||
//if(holder!=null && THE_UNSAFE.compareAndSwapObject(objects, (index*INDEXSCALE)+BASE, holder, null))
|
//if(holder!=null && THE_UNSAFE.compareAndSwapObject(objects, (index*INDEXSCALE)+BASE, holder, null))
|
||||||
if (holder != null && THE_UNSAFE.compareAndSwapObject(this.objects, (index<<this.ASHIFT)+this.BASE, holder, null)) {
|
if (holder != null && unsafe.compareAndSwapObject(this.objects, (index<<this.ASHIFT)+this.BASE, holder, null)) {
|
||||||
this.takePointer = localTakePointer+1;
|
this.takePointer = localTakePointer+1;
|
||||||
if (holder.state.compareAndSet(Holder.FREE, Holder.USED)) {
|
|
||||||
|
// the use of a threadlocal reference here helps eliminates contention. This also checks OTHER threads,
|
||||||
|
// as they might have one sitting on the cache
|
||||||
|
if (holder.state.compareAndSet(FREE, USED)) {
|
||||||
this.localValue.set(holder);
|
this.localValue.set(holder);
|
||||||
|
this.poolableObject.activate(holder.getValue());
|
||||||
return holder;
|
return holder;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -115,7 +103,8 @@ public class FastObjectPool<T> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void release(Holder<T> object) throws InterruptedException {
|
@Override
|
||||||
|
public void release(ObjectPoolHolder<T> object) throws InterruptedException {
|
||||||
this.lock.lockInterruptibly();
|
this.lock.lockInterruptibly();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -123,9 +112,10 @@ public class FastObjectPool<T> {
|
|||||||
//long index = ((localValue & mask) * INDEXSCALE ) + BASE;
|
//long index = ((localValue & mask) * INDEXSCALE ) + BASE;
|
||||||
long index = ((localValue & this.mask)<<this.ASHIFT ) + this.BASE;
|
long index = ((localValue & this.mask)<<this.ASHIFT ) + this.BASE;
|
||||||
|
|
||||||
if (object.state.compareAndSet(Holder.USED, Holder.FREE)) {
|
if (object.state.compareAndSet(USED, FREE)) {
|
||||||
THE_UNSAFE.putOrderedObject(this.objects, index, object);
|
Sys.unsafe.putOrderedObject(this.objects, index, object);
|
||||||
this.releasePointer = localValue+1;
|
this.releasePointer = localValue+1;
|
||||||
|
this.poolableObject.passivate(object.getValue());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throw new IllegalArgumentException("Invalid reference passed");
|
throw new IllegalArgumentException("Invalid reference passed");
|
||||||
|
@ -1,21 +0,0 @@
|
|||||||
package dorkbox.util.objectPool;
|
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
|
|
||||||
public class Holder<T> {
|
|
||||||
private T value;
|
|
||||||
|
|
||||||
static final int FREE = 0;
|
|
||||||
static final int USED = 1;
|
|
||||||
|
|
||||||
AtomicInteger state = new AtomicInteger(FREE);
|
|
||||||
|
|
||||||
|
|
||||||
public Holder(T value) {
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public T getValue() {
|
|
||||||
return this.value;
|
|
||||||
}
|
|
||||||
}
|
|
13
Dorkbox-Util/src/dorkbox/util/objectPool/ObjectPool.java
Normal file
13
Dorkbox-Util/src/dorkbox/util/objectPool/ObjectPool.java
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package dorkbox.util.objectPool;
|
||||||
|
|
||||||
|
public interface ObjectPool<T> {
|
||||||
|
/**
|
||||||
|
* Takes an object from the pool
|
||||||
|
*/
|
||||||
|
public ObjectPoolHolder<T> take();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return object to the pool
|
||||||
|
*/
|
||||||
|
public void release(ObjectPoolHolder<T> object) throws InterruptedException;
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package dorkbox.util.objectPool;
|
||||||
|
|
||||||
|
import dorkbox.util.Sys;
|
||||||
|
|
||||||
|
public class ObjectPoolFactory<T> {
|
||||||
|
|
||||||
|
private ObjectPoolFactory() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> ObjectPool<T> create(PoolableObject<T> poolableObject, int size) {
|
||||||
|
if (Sys.isAndroid) {
|
||||||
|
// unfortunately, unsafe is not available in android
|
||||||
|
SlowObjectPool<T> slowObjectPool = new SlowObjectPool<T>(poolableObject, size);
|
||||||
|
return slowObjectPool;
|
||||||
|
} else {
|
||||||
|
// here we use FAST (via UNSAFE) one!
|
||||||
|
FastObjectPool<T> fastObjectPool = new FastObjectPool<T>(poolableObject, size);
|
||||||
|
return fastObjectPool;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package dorkbox.util.objectPool;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
public class ObjectPoolHolder<T> {
|
||||||
|
private T value;
|
||||||
|
|
||||||
|
AtomicBoolean state = new AtomicBoolean(true);
|
||||||
|
|
||||||
|
|
||||||
|
public ObjectPoolHolder(T value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T getValue() {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +0,0 @@
|
|||||||
package dorkbox.util.objectPool;
|
|
||||||
|
|
||||||
public interface PoolFactory<T> {
|
|
||||||
public T create();
|
|
||||||
}
|
|
19
Dorkbox-Util/src/dorkbox/util/objectPool/PoolableObject.java
Normal file
19
Dorkbox-Util/src/dorkbox/util/objectPool/PoolableObject.java
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package dorkbox.util.objectPool;
|
||||||
|
|
||||||
|
|
||||||
|
public interface PoolableObject<T> {
|
||||||
|
/**
|
||||||
|
* called when a new instance is created
|
||||||
|
*/
|
||||||
|
public T create();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* invoked on every instance that is borrowed from the pool
|
||||||
|
*/
|
||||||
|
public void activate(T t);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* invoked on every instance that is returned to the pool
|
||||||
|
*/
|
||||||
|
public void passivate(T t);
|
||||||
|
}
|
88
Dorkbox-Util/src/dorkbox/util/objectPool/SlowObjectPool.java
Normal file
88
Dorkbox-Util/src/dorkbox/util/objectPool/SlowObjectPool.java
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
package dorkbox.util.objectPool;
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* from: http://ashkrit.blogspot.de/2013/05/lock-less-java-object-pool.html
|
||||||
|
* https://github.com/ashkrit/blog/tree/master/FastObjectPool
|
||||||
|
* copyright ashkrit 2013
|
||||||
|
*
|
||||||
|
* MODIFIED by Nathan Robinson to be a version that is compatible with Android.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.util.concurrent.LinkedBlockingDeque;
|
||||||
|
|
||||||
|
|
||||||
|
class SlowObjectPool<T> implements ObjectPool<T> {
|
||||||
|
|
||||||
|
private static final boolean FREE = true;
|
||||||
|
private static final boolean USED = false;
|
||||||
|
|
||||||
|
|
||||||
|
private final LinkedBlockingDeque<ObjectPoolHolder<T>> queue;
|
||||||
|
private final PoolableObject<T> poolableObject;
|
||||||
|
|
||||||
|
private ThreadLocal<ObjectPoolHolder<T>> localValue = new ThreadLocal<>();
|
||||||
|
|
||||||
|
SlowObjectPool(PoolableObject<T> poolableObject, int size) {
|
||||||
|
|
||||||
|
this.queue = new LinkedBlockingDeque<ObjectPoolHolder<T>>(size);
|
||||||
|
|
||||||
|
this.poolableObject = poolableObject;
|
||||||
|
for (int x=0;x<size;x++) {
|
||||||
|
this.queue.add(new ObjectPoolHolder<T>(poolableObject.create()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ObjectPoolHolder<T> take() {
|
||||||
|
// if we have an object available in the cache, use it instead.
|
||||||
|
ObjectPoolHolder<T> localObject = this.localValue.get();
|
||||||
|
if (localObject != null) {
|
||||||
|
if (localObject.state.compareAndSet(FREE, USED)) {
|
||||||
|
this.poolableObject.activate(localObject.getValue());
|
||||||
|
return localObject;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectPoolHolder<T> holder = this.queue.poll();
|
||||||
|
|
||||||
|
if (holder == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// the use of a threadlocal reference here helps eliminates contention. This also checks OTHER threads,
|
||||||
|
// as they might have one sitting on the cache
|
||||||
|
if (holder.state.compareAndSet(FREE, USED)) {
|
||||||
|
this.localValue.set(holder);
|
||||||
|
this.poolableObject.activate(holder.getValue());
|
||||||
|
return holder;
|
||||||
|
} else {
|
||||||
|
// put it back into the queue
|
||||||
|
this.queue.offer(holder);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void release(ObjectPoolHolder<T> object) throws InterruptedException {
|
||||||
|
if (object.state.compareAndSet(USED, FREE)) {
|
||||||
|
this.queue.offer(object);
|
||||||
|
this.poolableObject.passivate(object.getValue());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new IllegalArgumentException("Invalid reference passed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user