-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Don't allocate threads on every dispatch in Native's thread pools (#3595
) Related to #3576
- Loading branch information
1 parent
32af157
commit e946cd7
Showing
4 changed files
with
168 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
35 changes: 35 additions & 0 deletions
35
kotlinx-coroutines-core/concurrent/test/MultithreadedDispatcherStressTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
/* | ||
* Copyright 2016-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. | ||
*/ | ||
|
||
package kotlinx.coroutines | ||
|
||
import kotlinx.atomicfu.* | ||
import kotlin.coroutines.* | ||
import kotlin.test.* | ||
|
||
class MultithreadedDispatcherStressTest { | ||
val shared = atomic(0) | ||
|
||
/** | ||
* Tests that [newFixedThreadPoolContext] will not drop tasks when closed. | ||
*/ | ||
@Test | ||
fun testClosingNotDroppingTasks() { | ||
repeat(7) { | ||
shared.value = 0 | ||
val nThreads = it + 1 | ||
val dispatcher = newFixedThreadPoolContext(nThreads, "testMultiThreadedContext") | ||
repeat(1_000) { | ||
dispatcher.dispatch(EmptyCoroutineContext, Runnable { | ||
shared.incrementAndGet() | ||
}) | ||
} | ||
dispatcher.close() | ||
while (shared.value < 1_000) { | ||
// spin. | ||
// the test will hang here if the dispatcher drops tasks. | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
66 changes: 66 additions & 0 deletions
66
kotlinx-coroutines-core/native/test/MultithreadedDispatchersTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
/* | ||
* Copyright 2016-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. | ||
*/ | ||
|
||
package kotlinx.coroutines | ||
|
||
import kotlinx.atomicfu.* | ||
import kotlinx.coroutines.channels.* | ||
import kotlinx.coroutines.internal.* | ||
import kotlin.native.concurrent.* | ||
import kotlin.test.* | ||
|
||
private class BlockingBarrier(val n: Int) { | ||
val counter = atomic(0) | ||
val wakeUp = Channel<Unit>(n - 1) | ||
fun await() { | ||
val count = counter.addAndGet(1) | ||
if (count == n) { | ||
repeat(n - 1) { | ||
runBlocking { | ||
wakeUp.send(Unit) | ||
} | ||
} | ||
} else if (count < n) { | ||
runBlocking { | ||
wakeUp.receive() | ||
} | ||
} | ||
} | ||
} | ||
|
||
class MultithreadedDispatchersTest { | ||
/** | ||
* Test that [newFixedThreadPoolContext] does not allocate more dispatchers than it needs to. | ||
* Incidentally also tests that it will allocate enough workers for its needs. Otherwise, the test will hang. | ||
*/ | ||
@Test | ||
fun testNotAllocatingExtraDispatchers() { | ||
val barrier = BlockingBarrier(2) | ||
val lock = SynchronizedObject() | ||
suspend fun spin(set: MutableSet<Worker>) { | ||
repeat(100) { | ||
synchronized(lock) { set.add(Worker.current) } | ||
delay(1) | ||
} | ||
} | ||
val dispatcher = newFixedThreadPoolContext(64, "test") | ||
try { | ||
runBlocking { | ||
val encounteredWorkers = mutableSetOf<Worker>() | ||
val coroutine1 = launch(dispatcher) { | ||
barrier.await() | ||
spin(encounteredWorkers) | ||
} | ||
val coroutine2 = launch(dispatcher) { | ||
barrier.await() | ||
spin(encounteredWorkers) | ||
} | ||
listOf(coroutine1, coroutine2).joinAll() | ||
assertEquals(2, encounteredWorkers.size) | ||
} | ||
} finally { | ||
dispatcher.close() | ||
} | ||
} | ||
} |