Skip to content

Commit

Permalink
Fix timeout detection when withTimeout wraps a blocking job (kotest#3675
Browse files Browse the repository at this point in the history
)

* Fix timeout detection when withTimeout wraps a blocking job

This is a workaround for kotlinx.coroutines/issues/3875.

Fixes kotest#3672

* Add timeout tests

---------

Co-authored-by: Sam <sam@sksamuel.com>
  • Loading branch information
OliverO2 and sksamuel authored Sep 3, 2023
1 parent 5f003fa commit 3d34c11
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ package io.kotest.engine.concurrency
import io.kotest.core.concurrency.CoroutineDispatcherFactory
import io.kotest.core.test.TestCase
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.newSingleThreadContext
import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.withContext
import java.util.concurrent.Executors

/**
* A [CoroutineDispatcherFactory] that creates a dedicated thread for each test case.
Expand All @@ -14,7 +15,7 @@ object DedicatedThreadCoroutineDispatcherFactory : CoroutineDispatcherFactory {

@OptIn(DelicateCoroutinesApi::class)
override suspend fun <T> withDispatcher(testCase: TestCase, f: suspend () -> T): T =
newSingleThreadContext("dedicated").use { dispatcher ->
Executors.newSingleThreadExecutor().asCoroutineDispatcher().use { dispatcher ->
withContext(dispatcher) {
f()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import io.kotest.core.concurrency.CoroutineDispatcherFactory
import io.kotest.core.test.TestCase
import io.kotest.mpp.Logger
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.newSingleThreadContext
import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.withContext
import java.io.Closeable
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.Executors
import java.util.concurrent.atomic.AtomicInteger
import kotlin.reflect.KClass

Expand Down Expand Up @@ -36,8 +36,7 @@ class FixedThreadCoroutineDispatcherFactory(

private val logger = Logger(FixedThreadCoroutineDispatcherFactory::class)

@OptIn(DelicateCoroutinesApi::class)
private val dispatcherPool = List(threads) { newSingleThreadContext("fixed-${it + 1}/$threads") }
private val dispatcherPool = List(threads) { Executors.newSingleThreadExecutor().asCoroutineDispatcher() }

private val requestCount = AtomicInteger(0)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package io.kotest.engine.concurrency

import io.kotest.assertions.throwables.shouldThrow
import io.kotest.core.spec.style.FunSpec
import kotlinx.coroutines.TimeoutCancellationException
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withTimeout

class TimeoutTest : FunSpec({
test("detection with blocking job") {
shouldThrow<TimeoutCancellationException> {
withTimeout(50) {
launch {
Thread.sleep(100)
}
}
println("no timeout detected")
}
}

test("detection with non-blocking job") {
shouldThrow<TimeoutCancellationException> {
withTimeout(50) {
launch {
delay(100)
}
}
println("no timeout detected")
}
}
})

0 comments on commit 3d34c11

Please sign in to comment.