From 2fb565fade039ca2a598d84057a15ddf0506300a Mon Sep 17 00:00:00 2001 From: nathan Date: Fri, 3 Oct 2014 16:04:50 +0200 Subject: [PATCH] Added ObjectPool factory + a 'slow' one that is compatible with android --- .../util/objectPool/FastObjectPool.java | 82 ++++++++--------- .../src/dorkbox/util/objectPool/Holder.java | 21 ----- .../dorkbox/util/objectPool/ObjectPool.java | 13 +++ .../util/objectPool/ObjectPoolFactory.java | 21 +++++ .../util/objectPool/ObjectPoolHolder.java | 18 ++++ .../dorkbox/util/objectPool/PoolFactory.java | 5 -- .../util/objectPool/PoolableObject.java | 19 ++++ .../util/objectPool/SlowObjectPool.java | 88 +++++++++++++++++++ 8 files changed, 195 insertions(+), 72 deletions(-) delete mode 100644 Dorkbox-Util/src/dorkbox/util/objectPool/Holder.java create mode 100644 Dorkbox-Util/src/dorkbox/util/objectPool/ObjectPool.java create mode 100644 Dorkbox-Util/src/dorkbox/util/objectPool/ObjectPoolFactory.java create mode 100644 Dorkbox-Util/src/dorkbox/util/objectPool/ObjectPoolHolder.java delete mode 100644 Dorkbox-Util/src/dorkbox/util/objectPool/PoolFactory.java create mode 100644 Dorkbox-Util/src/dorkbox/util/objectPool/PoolableObject.java create mode 100644 Dorkbox-Util/src/dorkbox/util/objectPool/SlowObjectPool.java diff --git a/Dorkbox-Util/src/dorkbox/util/objectPool/FastObjectPool.java b/Dorkbox-Util/src/dorkbox/util/objectPool/FastObjectPool.java index 9d388ca..0321c45 100644 --- a/Dorkbox-Util/src/dorkbox/util/objectPool/FastObjectPool.java +++ b/Dorkbox-Util/src/dorkbox/util/objectPool/FastObjectPool.java @@ -19,44 +19,21 @@ package dorkbox.util.objectPool; * limitations under the License. */ -import java.lang.reflect.Field; -import java.security.AccessController; -import java.security.PrivilegedExceptionAction; import java.util.concurrent.locks.ReentrantLock; -import sun.misc.Unsafe; +import dorkbox.util.Sys; -public class FastObjectPool { - public static final Unsafe THE_UNSAFE; - static { - try { - final PrivilegedExceptionAction action = new PrivilegedExceptionAction() { - @Override - public Unsafe run() throws Exception { - Class 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); - } +class FastObjectPool implements ObjectPool { - throw new NoSuchFieldError("the Unsafe"); - } - }; + private static final boolean FREE = true; + private static final boolean USED = false; - THE_UNSAFE = AccessController.doPrivileged(action); - } - catch (Exception e) { - throw new RuntimeException("Unable to load unsafe", e); - } - } - - private Holder[] objects; + private final ObjectPoolHolder[] objects; + private final PoolableObject poolableObject; private volatile int takePointer; - private int releasePointer; + private volatile int releasePointer; private final int mask; private final long BASE; @@ -64,10 +41,11 @@ public class FastObjectPool { private final long ASHIFT; public ReentrantLock lock = new ReentrantLock(); - private ThreadLocal> localValue = new ThreadLocal<>(); + private ThreadLocal> localValue = new ThreadLocal<>(); - public FastObjectPool(PoolFactory factory, int size) { + FastObjectPool(PoolableObject poolableObject, int size) { + this.poolableObject = poolableObject; int newSize = 1; while (newSize < size) { newSize = newSize << 1; @@ -76,38 +54,48 @@ public class FastObjectPool { size = newSize; @SuppressWarnings({"unchecked", "rawtypes"}) - Holder[] stuff = new Holder[size]; + ObjectPoolHolder[] stuff = new ObjectPoolHolder[size]; this.objects = stuff; for (int x=0;x(factory.create()); + this.objects[x] = new ObjectPoolHolder(poolableObject.create()); } this.mask = size-1; this.releasePointer = size; - this.BASE = THE_UNSAFE.arrayBaseOffset(Holder[].class); - this.INDEXSCALE = THE_UNSAFE.arrayIndexScale(Holder[].class); + this.BASE = Sys.unsafe.arrayBaseOffset(ObjectPoolHolder[].class); + this.INDEXSCALE = Sys.unsafe.arrayIndexScale(ObjectPoolHolder[].class); this.ASHIFT = 31 - Integer.numberOfLeadingZeros((int) this.INDEXSCALE); } - public Holder take() { + @Override + public ObjectPoolHolder take() { int localTakePointer; - Holder localObject = this.localValue.get(); + // if we have an object available in the cache, use it instead. + ObjectPoolHolder localObject = this.localValue.get(); if (localObject != null) { - if(localObject.state.compareAndSet(Holder.FREE, Holder.USED)) { + if (localObject.state.compareAndSet(FREE, USED)) { + this.poolableObject.activate(localObject.getValue()); return localObject; } } + sun.misc.Unsafe unsafe = Sys.unsafe; + while (this.releasePointer != (localTakePointer=this.takePointer)) { int index = localTakePointer & this.mask; - Holder holder = this.objects[index]; + + ObjectPoolHolder holder = this.objects[index]; //if(holder!=null && THE_UNSAFE.compareAndSwapObject(objects, (index*INDEXSCALE)+BASE, holder, null)) - if (holder != null && THE_UNSAFE.compareAndSwapObject(this.objects, (index< { return null; } - public void release(Holder object) throws InterruptedException { + @Override + public void release(ObjectPoolHolder object) throws InterruptedException { this.lock.lockInterruptibly(); try { - int localValue=this.releasePointer; + int localValue = this.releasePointer; //long index = ((localValue & mask) * INDEXSCALE ) + BASE; long index = ((localValue & this.mask)< { - 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; - } -} diff --git a/Dorkbox-Util/src/dorkbox/util/objectPool/ObjectPool.java b/Dorkbox-Util/src/dorkbox/util/objectPool/ObjectPool.java new file mode 100644 index 0000000..23a09d8 --- /dev/null +++ b/Dorkbox-Util/src/dorkbox/util/objectPool/ObjectPool.java @@ -0,0 +1,13 @@ +package dorkbox.util.objectPool; + +public interface ObjectPool { + /** + * Takes an object from the pool + */ + public ObjectPoolHolder take(); + + /** + * Return object to the pool + */ + public void release(ObjectPoolHolder object) throws InterruptedException; +} diff --git a/Dorkbox-Util/src/dorkbox/util/objectPool/ObjectPoolFactory.java b/Dorkbox-Util/src/dorkbox/util/objectPool/ObjectPoolFactory.java new file mode 100644 index 0000000..b5b77f3 --- /dev/null +++ b/Dorkbox-Util/src/dorkbox/util/objectPool/ObjectPoolFactory.java @@ -0,0 +1,21 @@ +package dorkbox.util.objectPool; + +import dorkbox.util.Sys; + +public class ObjectPoolFactory { + + private ObjectPoolFactory() { + } + + public static ObjectPool create(PoolableObject poolableObject, int size) { + if (Sys.isAndroid) { + // unfortunately, unsafe is not available in android + SlowObjectPool slowObjectPool = new SlowObjectPool(poolableObject, size); + return slowObjectPool; + } else { + // here we use FAST (via UNSAFE) one! + FastObjectPool fastObjectPool = new FastObjectPool(poolableObject, size); + return fastObjectPool; + } + } +} \ No newline at end of file diff --git a/Dorkbox-Util/src/dorkbox/util/objectPool/ObjectPoolHolder.java b/Dorkbox-Util/src/dorkbox/util/objectPool/ObjectPoolHolder.java new file mode 100644 index 0000000..0ce124f --- /dev/null +++ b/Dorkbox-Util/src/dorkbox/util/objectPool/ObjectPoolHolder.java @@ -0,0 +1,18 @@ +package dorkbox.util.objectPool; + +import java.util.concurrent.atomic.AtomicBoolean; + +public class ObjectPoolHolder { + private T value; + + AtomicBoolean state = new AtomicBoolean(true); + + + public ObjectPoolHolder(T value) { + this.value = value; + } + + public T getValue() { + return this.value; + } +} diff --git a/Dorkbox-Util/src/dorkbox/util/objectPool/PoolFactory.java b/Dorkbox-Util/src/dorkbox/util/objectPool/PoolFactory.java deleted file mode 100644 index 2b70461..0000000 --- a/Dorkbox-Util/src/dorkbox/util/objectPool/PoolFactory.java +++ /dev/null @@ -1,5 +0,0 @@ -package dorkbox.util.objectPool; - -public interface PoolFactory { - public T create(); -} \ No newline at end of file diff --git a/Dorkbox-Util/src/dorkbox/util/objectPool/PoolableObject.java b/Dorkbox-Util/src/dorkbox/util/objectPool/PoolableObject.java new file mode 100644 index 0000000..6b0dfb4 --- /dev/null +++ b/Dorkbox-Util/src/dorkbox/util/objectPool/PoolableObject.java @@ -0,0 +1,19 @@ +package dorkbox.util.objectPool; + + +public interface PoolableObject { + /** + * 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); +} diff --git a/Dorkbox-Util/src/dorkbox/util/objectPool/SlowObjectPool.java b/Dorkbox-Util/src/dorkbox/util/objectPool/SlowObjectPool.java new file mode 100644 index 0000000..390cc96 --- /dev/null +++ b/Dorkbox-Util/src/dorkbox/util/objectPool/SlowObjectPool.java @@ -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 implements ObjectPool { + + private static final boolean FREE = true; + private static final boolean USED = false; + + + private final LinkedBlockingDeque> queue; + private final PoolableObject poolableObject; + + private ThreadLocal> localValue = new ThreadLocal<>(); + + SlowObjectPool(PoolableObject poolableObject, int size) { + + this.queue = new LinkedBlockingDeque>(size); + + this.poolableObject = poolableObject; + for (int x=0;x(poolableObject.create())); + } + } + + @Override + public ObjectPoolHolder take() { + // if we have an object available in the cache, use it instead. + ObjectPoolHolder localObject = this.localValue.get(); + if (localObject != null) { + if (localObject.state.compareAndSet(FREE, USED)) { + this.poolableObject.activate(localObject.getValue()); + return localObject; + } + } + + ObjectPoolHolder 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 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"); + } + } +} \ No newline at end of file