class BasicWorkerPool
(
val concurrency: Int,
val context: CoroutineContext = Dispatchers.Default,
private val compute: suspend (P) -> Q
) : CoroutineScope {
val job = Job(parent = context[Job])
override val coroutineContext: CoroutineContext = context + job
private val tasks = Channel>>()
init {
require(concurrency > 0) { "concurrency cannot be <= 0" }
}
fun start() {
repeat(concurrency) {
launch {
for (task in tasks) {
process(task)
}
}
}
}
fun shutdown() {
tasks.close()
}
fun shutdownNow() {
tasks.close()
job.cancel()
}
private suspend fun process(task: Pair>) {
val (p, deferredResult) = task
try {
println("processing $p in [$coroutineContext][${Thread.currentThread().name}]")
val q = compute(p)
deferredResult.complete(q)
} catch (e: Throwable) {
deferredResult.completeExceptionally(e)
}
}
suspend fun execute(task: P): CompletableDeferred {
val result = CompletableDeferred()
tasks.send(task to result)
return result
}
suspend fun executeAwait(task: P): Q {
return execute(task).await()
}
}
suspend fun foo1() = withContext(Dispatchers.Default) {
val workerPool = BasicWorkerPool(50, context = Dispatchers.Default) {
delay(1000)
it * 2
}
workerPool.start()
val results = (1..100).mapIndexed { idx, _ ->
workerPool.execute(idx)
}
println("submitted tasks, starting assertions")
results.forEachIndexed { index, result ->
check(index * 2 == result.await()) { "result not matching for $index" }
}
workerPool.shutdown()
println("workerPool: ${workerPool.job.isActive};${workerPool.job.isCancelled};${workerPool.job.isCompleted}")
println("done")
}
suspend fun foo2() = withContext(Dispatchers.Default) {
val workerPool = BasicWorkerPool(50, context = coroutineContext) {
delay(1000)
it * 2
}
workerPool.start()
val results = (1..100).mapIndexed { idx, _ ->
workerPool.execute(idx)
}
println("submitted tasks, starting assertions")
results.forEachIndexed { index, result ->
check(index * 2 == result.await()) { "result not matching for $index" }
}
workerPool.shutdown()
println("workerPool: ${workerPool.job.isActive};${workerPool.job.isCancelled};${workerPool.job.isCompleted}")
println("done")
}
fun main() = runBlocking {
foo1() // works;
foo2() // blocks indefinitely
}