Skip to content

Commit 8177a0a

Browse files
authored
Disallow reconfiguring the executor in newFixedThreadPoolContext (#4364)
1 parent 7d9467c commit 8177a0a

File tree

4 files changed

+34
-4
lines changed

4 files changed

+34
-4
lines changed

Diff for: kotlinx-coroutines-core/jvm/src/ThreadPoolDispatcher.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@ public actual fun newFixedThreadPoolContext(nThreads: Int, name: String): Execut
1414
t.isDaemon = true
1515
t
1616
}
17-
return executor.asCoroutineDispatcher()
17+
return Executors.unconfigurableExecutorService(executor).asCoroutineDispatcher()
1818
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package kotlinx.coroutines
2+
3+
import kotlinx.coroutines.internal.LocalAtomicInt
4+
import kotlinx.coroutines.testing.*
5+
import java.util.concurrent.ScheduledThreadPoolExecutor
6+
import kotlin.coroutines.EmptyCoroutineContext
7+
import kotlin.test.*
8+
9+
class MultithreadedDispatchersJvmTest: TestBase() {
10+
/** Tests that the executor created in [newFixedThreadPoolContext] can not leak and be reconfigured. */
11+
@Test
12+
fun testExecutorReconfiguration() {
13+
newFixedThreadPoolContext(1, "test").apply {
14+
(executor as? ScheduledThreadPoolExecutor)?.corePoolSize = 2
15+
}.use { ctx ->
16+
val atomicInt = LocalAtomicInt(0)
17+
repeat(100) {
18+
ctx.dispatch(EmptyCoroutineContext, Runnable {
19+
val entered = atomicInt.incrementAndGet()
20+
Thread.yield() // allow other tasks to run
21+
try {
22+
check(entered == 1) { "Expected only one thread to be used, observed $entered" }
23+
} finally {
24+
atomicInt.decrementAndGet()
25+
}
26+
})
27+
}
28+
}
29+
}
30+
}

Diff for: kotlinx-coroutines-core/jvm/test/knit/ClosedAfterGuideTestExecutor.kt

-3
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,6 @@ import kotlin.coroutines.*
77

88
internal fun newSingleThreadContext(name: String): ExecutorCoroutineDispatcher = ClosedAfterGuideTestDispatcher(1, name)
99

10-
internal fun newFixedThreadPoolContext(nThreads: Int, name: String): ExecutorCoroutineDispatcher =
11-
ClosedAfterGuideTestDispatcher(nThreads, name)
12-
1310
private class ClosedAfterGuideTestDispatcher(
1411
private val nThreads: Int,
1512
private val name: String

Diff for: test-utils/jvm/src/FieldWalker.kt

+3
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,9 @@ object FieldWalker {
127127
element is AtomicLongFieldUpdater<*> -> {
128128
/* filter it out here to suppress its subclasses too */
129129
}
130+
element is ExecutorService && type.name == "java.util.concurrent.Executors\$DelegatedExecutorService" -> {
131+
/* can't access anything in the executor */
132+
}
130133
// All the other classes are reflectively scanned
131134
else -> fields(type, statics).forEach { field ->
132135
push(field.get(element), visited, stack) { Ref.FieldRef(element, field.name) }

0 commit comments

Comments
 (0)