Added support for converting collections to a blocking/suspending pool
parent
c8a4639ad9
commit
05dd096b31
|
@ -17,11 +17,13 @@ package dorkbox.objectPool
|
||||||
|
|
||||||
import com.conversantmedia.util.concurrent.DisruptorBlockingQueue
|
import com.conversantmedia.util.concurrent.DisruptorBlockingQueue
|
||||||
import dorkbox.objectPool.blocking.BlockingPool
|
import dorkbox.objectPool.blocking.BlockingPool
|
||||||
|
import dorkbox.objectPool.blocking.BlockingPoolCollection
|
||||||
import dorkbox.objectPool.nonBlocking.BoundedNonBlockingPool
|
import dorkbox.objectPool.nonBlocking.BoundedNonBlockingPool
|
||||||
import dorkbox.objectPool.nonBlocking.NonBlockingPool
|
import dorkbox.objectPool.nonBlocking.NonBlockingPool
|
||||||
import dorkbox.objectPool.nonBlocking.NonBlockingSoftPool
|
import dorkbox.objectPool.nonBlocking.NonBlockingSoftPool
|
||||||
import dorkbox.objectPool.suspending.ChannelQueue
|
import dorkbox.objectPool.suspending.ChannelQueue
|
||||||
import dorkbox.objectPool.suspending.SuspendingPool
|
import dorkbox.objectPool.suspending.SuspendingPool
|
||||||
|
import dorkbox.objectPool.suspending.SuspendingPoolCollection
|
||||||
import dorkbox.objectPool.suspending.SuspendingQueue
|
import dorkbox.objectPool.suspending.SuspendingQueue
|
||||||
import java.lang.ref.SoftReference
|
import java.lang.ref.SoftReference
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
@ -70,7 +72,6 @@ object ObjectPool {
|
||||||
return SuspendingPool(poolObject, size, queue)
|
return SuspendingPool(poolObject, size, queue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a high-performance blocking pool of a specific size, where the entire pool is initially filled, and when the pool is empty, a
|
* Creates a high-performance blocking pool of a specific size, where the entire pool is initially filled, and when the pool is empty, a
|
||||||
* [Pool.take] will wait for a corresponding [Pool.put].
|
* [Pool.take] will wait for a corresponding [Pool.put].
|
||||||
|
@ -201,4 +202,66 @@ object ObjectPool {
|
||||||
fun <T> nonBlockingBounded(poolObject: BoundedPoolObject<T>, maxSize: Int, queue: Queue<T>): Pool<T> {
|
fun <T> nonBlockingBounded(poolObject: BoundedPoolObject<T>, maxSize: Int, queue: Queue<T>): Pool<T> {
|
||||||
return BoundedNonBlockingPool(poolObject, maxSize, queue)
|
return BoundedNonBlockingPool(poolObject, maxSize, queue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a suspending pool of a specific size, where the entire pool is initially filled, and when the pool is empty, a
|
||||||
|
* [Pool.take] will wait for a corresponding [Pool.put].
|
||||||
|
*
|
||||||
|
* @param poolObject controls the lifecycle of the pooled objects.
|
||||||
|
* @param size the size of the pool to create
|
||||||
|
* @param <T> the type of object used in the pool
|
||||||
|
*
|
||||||
|
* @return a suspending pool using the kotlin Channel implementation of a specific size
|
||||||
|
*/
|
||||||
|
fun <T> suspending(collection: Collection<T>): dorkbox.objectPool.SuspendingPool<T> {
|
||||||
|
return suspending(ChannelQueue(collection.size), collection)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a suspending pool of an existing collection, where the entire pool is initially filled, and when the pool is empty, a
|
||||||
|
* [Pool.take] will wait for a corresponding [Pool.put].
|
||||||
|
*
|
||||||
|
* @param collection the existing collection to convert to a pool
|
||||||
|
* @param <T> the type of object used in the pool
|
||||||
|
*
|
||||||
|
* @return a suspending pool using the kotlin Channel implementation of a specific size
|
||||||
|
*/
|
||||||
|
fun <T> suspending(queue: SuspendingQueue<T>, collection: Collection<T>): dorkbox.objectPool.SuspendingPool<T> {
|
||||||
|
return SuspendingPoolCollection(queue, collection)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a high-performance blocking pool of an existing collection, where the entire pool is initially filled, and when the pool is empty, a
|
||||||
|
* [Pool.take] will wait for a corresponding [Pool.put].
|
||||||
|
*
|
||||||
|
* @param collection the existing collection to convert to a pool
|
||||||
|
* @param <T> the type of object used in the pool
|
||||||
|
*
|
||||||
|
* @return a blocking pool using the DisruptorBlockingQueue implementation of a specific size
|
||||||
|
*/
|
||||||
|
fun <T> blocking(collection: Collection<T>): Pool<T> {
|
||||||
|
return blocking(DisruptorBlockingQueue(collection.size), collection)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a blocking pool from an existing collection, where the entire pool is initially filled, and when the pool is empty, a
|
||||||
|
* [Pool.take] will wait for a corresponding [Pool.put].
|
||||||
|
*
|
||||||
|
* @param queue the blocking queue implementation to use
|
||||||
|
* @param collection the existing collection to convert to a pool
|
||||||
|
* @param <T> the type of object used in the pool
|
||||||
|
*
|
||||||
|
* @return a blocking pool using the specified [BlockingQueue] implementation of a specific size
|
||||||
|
*/
|
||||||
|
fun <T> blocking(queue: BlockingQueue<T>, collection: Collection<T>): Pool<T> {
|
||||||
|
return BlockingPoolCollection(queue, collection)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2020 dorkbox, llc
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
package dorkbox.objectPool.blocking
|
||||||
|
|
||||||
|
import dorkbox.objectPool.Pool
|
||||||
|
import java.util.concurrent.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A blocking pool of a specific collection, where the entire pool is initially filled, and when the pool is empty,
|
||||||
|
* a [Pool.take] will wait for a corresponding [Pool.put].
|
||||||
|
*
|
||||||
|
* @author dorkbox, llc
|
||||||
|
*/
|
||||||
|
internal class BlockingPoolCollection<T> constructor(
|
||||||
|
private val queue: BlockingQueue<T>,
|
||||||
|
collection: Collection<T>) : Pool<T> {
|
||||||
|
|
||||||
|
private val dummyValue: T
|
||||||
|
|
||||||
|
init {
|
||||||
|
dummyValue = collection.elementAt(0)
|
||||||
|
|
||||||
|
for (it in collection) {
|
||||||
|
queue.offer(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes an object from the pool, Blocks until an item is available in the pool.
|
||||||
|
*
|
||||||
|
* This method catches [InterruptedException] and discards it silently.
|
||||||
|
*/
|
||||||
|
override fun take(): T {
|
||||||
|
return try {
|
||||||
|
takeInterruptibly()
|
||||||
|
} catch (ignored: InterruptedException) {
|
||||||
|
dummyValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes an object from the pool, Blocks until an item is available in the pool.
|
||||||
|
*
|
||||||
|
* @throws InterruptedException
|
||||||
|
*/
|
||||||
|
override fun takeInterruptibly(): T {
|
||||||
|
return queue.take()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return object to the pool, waking the threads that have blocked during take()
|
||||||
|
*/
|
||||||
|
override fun put(`object`: T) {
|
||||||
|
queue.put(`object`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return a new object instance created by the pool.
|
||||||
|
*/
|
||||||
|
override fun newInstance(): T {
|
||||||
|
return dummyValue
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2020 dorkbox, llc
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
package dorkbox.objectPool.suspending
|
||||||
|
|
||||||
|
import dorkbox.objectPool.Pool
|
||||||
|
import dorkbox.objectPool.SuspendingPool
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A suspending pool of a specific size, where the entire pool is initially filled, and when the pool is empty,
|
||||||
|
* a [Pool.take] will wait for a corresponding [Pool.put].
|
||||||
|
*
|
||||||
|
* @author dorkbox, llc
|
||||||
|
*/
|
||||||
|
internal class SuspendingPoolCollection<T> constructor(
|
||||||
|
private val queue: SuspendingQueue<T>,
|
||||||
|
collection: Collection<T>) : SuspendingPool<T> {
|
||||||
|
|
||||||
|
private val dummyValue: T = collection.elementAt(0)
|
||||||
|
|
||||||
|
init {
|
||||||
|
runBlocking {
|
||||||
|
for (x in collection) {
|
||||||
|
queue.offer(x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes an object from the pool, Suspends until an item is available in the pool.
|
||||||
|
*
|
||||||
|
* This method catches [InterruptedException] and discards it silently.
|
||||||
|
*/
|
||||||
|
override suspend fun take(): T {
|
||||||
|
return try {
|
||||||
|
takeInterruptibly()
|
||||||
|
} catch (e: InterruptedException) {
|
||||||
|
dummyValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes an object from the pool, Suspends until an item is available in the pool.
|
||||||
|
*
|
||||||
|
* @throws InterruptedException
|
||||||
|
*/
|
||||||
|
override suspend fun takeInterruptibly(): T {
|
||||||
|
return queue.take()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return object to the pool, waking the threads that have suspended during take()
|
||||||
|
*/
|
||||||
|
override suspend fun put(`object`: T) {
|
||||||
|
queue.put(`object`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return a new object instance created by the pool.
|
||||||
|
*/
|
||||||
|
override suspend fun newInstance(): T {
|
||||||
|
return dummyValue
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,12 +17,11 @@ package dorkbox.objectPool
|
||||||
|
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import org.junit.Assert
|
import org.junit.Assert
|
||||||
import org.junit.Ignore
|
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
||||||
@Suppress("UNUSED_VARIABLE")
|
@Suppress("UNUSED_VARIABLE")
|
||||||
@Ignore
|
class PoolTest {
|
||||||
class BlockingTest {
|
|
||||||
@Test
|
@Test
|
||||||
fun blockingTest() {
|
fun blockingTest() {
|
||||||
val pobj = object : PoolObject<String>() {
|
val pobj = object : PoolObject<String>() {
|
||||||
|
@ -37,8 +36,12 @@ class BlockingTest {
|
||||||
val take1 = pool.take()
|
val take1 = pool.take()
|
||||||
val take2 = pool.take()
|
val take2 = pool.take()
|
||||||
val take3 = pool.take()
|
val take3 = pool.take()
|
||||||
val take4 = pool.take() // this blocks
|
// val take4 = pool.take() // this blocks
|
||||||
Assert.fail("shouldn't get here")
|
// Assert.fail("shouldn't get here")
|
||||||
|
|
||||||
|
pool.put(take2)
|
||||||
|
val take4 = pool.take()
|
||||||
|
Assert.assertEquals(take2, take4)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -112,4 +115,42 @@ class BlockingTest {
|
||||||
Assert.assertTrue(pool.take() === take3)
|
Assert.assertTrue(pool.take() === take3)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun collectionBlockingTest() {
|
||||||
|
val collection = listOf(1, 2, 3, 4)
|
||||||
|
|
||||||
|
val pool = ObjectPool.blocking(collection)
|
||||||
|
|
||||||
|
val take = pool.take()
|
||||||
|
val take1 = pool.take()
|
||||||
|
val take2 = pool.take()
|
||||||
|
val take3 = pool.take()
|
||||||
|
// val take4 = pool.take() // this suspends
|
||||||
|
// Assert.fail("shouldn't get here")
|
||||||
|
|
||||||
|
pool.put(take2)
|
||||||
|
val take4 = pool.take()
|
||||||
|
Assert.assertEquals(take2, take4)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun collectionSuspendTest() {
|
||||||
|
val collection = listOf(1, 2, 3, 4)
|
||||||
|
|
||||||
|
val pool = ObjectPool.suspending(collection)
|
||||||
|
|
||||||
|
runBlocking {
|
||||||
|
val take = pool.take()
|
||||||
|
val take1 = pool.take()
|
||||||
|
val take2 = pool.take()
|
||||||
|
val take3 = pool.take()
|
||||||
|
// val take4 = pool.take() // this suspends
|
||||||
|
// Assert.fail("shouldn't get here")
|
||||||
|
|
||||||
|
pool.put(take2)
|
||||||
|
val take4 = pool.take()
|
||||||
|
Assert.assertEquals(take2, take4)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue