Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 8177a0a

Browse files
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

‎kotlinx-coroutines-core/jvm/src/ThreadPoolDispatcher.kt

Lines changed: 1 addition & 1 deletion
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
}
Lines changed: 30 additions & 0 deletions
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+
}

‎kotlinx-coroutines-core/jvm/test/knit/ClosedAfterGuideTestExecutor.kt

Lines changed: 0 additions & 3 deletions
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

‎test-utils/jvm/src/FieldWalker.kt

Lines changed: 3 additions & 0 deletions
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)

AltStyle によって変換されたページ (->オリジナル) /