New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Incorporate new Native memory model into kotlinx-coroutines mainline #2833
Conversation
aebc889
to
ca3e621
Compare
7909ee6
to
16b64a8
Compare
91289bd
to
f96de23
Compare
f96de23
to
407e0b6
Compare
* Dispatchers.Default backed by pool of workers on Linux and by global_queue on iOS-like * Implementation of Dispatchers.Main that uses main queue on iOS and default dispatcher on other platforms (#2858) * Introduced newSingleThreadDispatcher and newFixedThreadPoolDispatcher * Use proper reentrant locking and CoW arrays on new memory model, make TestBase _almost_ race-free * More thread-safety in Native counterpart and one more test from native-mt * Source-set sharing for tests shared between JVM and K/N * Wrap Obj-C interop into autorelease pool to avoid memory leaks
407e0b6
to
8cdafed
Compare
ed876b0
to
2c8439f
Compare
* SharedImmutable where necessary * Disable MainDispatcherTest with old MM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is just the first batch of comments so there's something to discuss while I check the rest of this.
kotlinx-coroutines-core/concurrent/test/flow/CombineStressTest.kt
Outdated
Show resolved
Hide resolved
@@ -85,7 +85,7 @@ class AtomicCancellationTest : TestBase() { | |||
} | |||
|
|||
@Test | |||
fun testSelectDeferredAwaitCancellable() = runBlocking { | |||
fun testSelectDeferredAwaitCancellable() = runMtTest { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Below this test, there's another one with runBlocking
. Shouldn't it also be changed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like all these tests should be indeed runBlocking
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean, the one below should be runBlocking
, but the other ones are fine with runMtTest
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All these tests have to be tested with runBlocking
as neither of the tests implies actual multithreading
|
||
private class IteratorImpl<E>(private var array: Array<E>, private val size: Int) : MutableIterator<E> { | ||
|
||
private class IteratorImpl<E>(private val array: Array<E>) : MutableIterator<E> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Historical note: it looks like the deoptimization here was introduced because it would be difficult to support now that this data structure needs to use an atomic
. This is not a huge deal, as this is only ever used to store the list of subscribers for a buffered broadcast channel, so we don't expect to be thousands of subscribers here.
Now, I have some problems with this. CopyOnWriteList
is not properly lock-free. Concurrent add
and remove
operations are not supported. So, this abstraction is leaky, heavily relying on the way it is used without specifying it, and it only works because add
and remove
operations are protected by a lock and it also happens that add
and remove
are never called in the same critical section. If they ever were, the user could observe an inconsistent state via an iterator, which is not created under a lock.
The solution, I think, is to add a comment both in the CopyOnWriteList
documentation, clarifying that it's a very specific implementation, and inside ArrayBroadcastChannel
, warning that each access to subscribers
should be carefully reviewed in tandem with CopyOnWriteList
implementation on Native.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Historical note" only refers to the first paragraph. Probably should have split into several comments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a warning and a deprecation.
Broadcast channels are being decommissioned, so it shouldn't be a problem.
@@ -15,7 +15,7 @@ import kotlin.test.* | |||
* but run only under JDK 1.8 | |||
*/ | |||
@Suppress("ConflictingExtensionProperty") | |||
val Throwable.suppressed: Array<Throwable> get() { | |||
actual val Throwable.suppressed: Array<Throwable> get() { | |||
val method = this::class.java.getMethod("getSuppressed") ?: error("This test can only be run using JDK 1.7") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
val method = this::class.java.getMethod("getSuppressed") ?: error("This test can only be run using JDK 1.7") | |
val method = this::class.java.getMethod("getSuppressed") ?: error("This test can only be run using JDK 1.7+") |
Do we still support 1.6, by the way?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're in limbo here. Let's discuss it separately
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another batch of comments.
@@ -36,3 +36,5 @@ internal fun removeFutureOnCancel(executor: Executor): Boolean { | |||
return false // failed to setRemoveOnCancelPolicy, assume it does not removes future on cancel | |||
} | |||
} | |||
|
|||
internal actual val multithreadingSupported: Boolean = true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion:
expect/actual multithreadingSupported
in the tests.val multithreadingEnabled
on the Native.
Reasoning: these can be thought of as orthogonal, as in the tests, this controls their conditional execution, whereas, in the Native code, it for now controls whether the new memory model is enabled. I think these are separate concerns that we may want to evolve differently. For example, we may remove multithreadingEnabled
in the future if we decide to enable it unconditionally, but in the tests, excluding the JS may still be useful.
Also, I don't know how to force the IDE to show me the source set in the popup listing the uses of something. Because of this, it's difficult to tell immediately if there is some common non-test code that relies on this variable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd suggest doing so when we are going to evolve the code; it seems a bit premature for now
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having searched my feelings for why this bugs me, I think I have a more compelling explanation.
This variable is present in the non-test common code, even though it wasn't used anywhere other than on the Native. This, like not making a variable private
when it can be, is, I think, an anti-pattern: making the scope in which something is present large is easy, reducing the scope is not.
What happened just now, in this PR, is an example of just that: this variable only concerns the behavior on Native, but, due to it being available everywhere, it is used in the common code. If we later make Native multithreaded by default, we may not notice that the variable outlived its usefulness and can be removed everywhere but in tests (and possibly even there).
it seems a bit premature for now
I'd argue the opposite: it's premature to have a broad scope of availability before it's actually needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are good points!
I've made the following: made runMtTest
expect function only in concurrent
source-set, made multithreadingSupported
native-only declaration and moved the only use of multithreadingSupported
in common
to Native source-set
kotlinx-coroutines-core/common/src/internal/Concurrent.common.kt
Outdated
Show resolved
Hide resolved
kotlinx-coroutines-core/concurrent/test/sync/SemaphoreStressTest.kt
Outdated
Show resolved
Hide resolved
kotlinx-coroutines-core/concurrent/test/sync/SemaphoreStressTest.kt
Outdated
Show resolved
Hide resolved
kotlinx-coroutines-core/concurrent/test/sync/MutexStressTest.kt
Outdated
Show resolved
Hide resolved
Co-authored-by: dkhalanskyjb <52952525+dkhalanskyjb@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Second-to-last dump of the initial comments while I brace myself to read the Darwin Dispatchers.Main
implementation and tests.
kotlinx-coroutines-core/jvm/test/channels/RendezvousChannelStressTest.kt
Show resolved
Hide resolved
kotlinx-coroutines-core/concurrent/test/internal/LockFreeLinkedListTest.kt
Outdated
Show resolved
Hide resolved
kotlinx-coroutines-core/concurrent/test/ConcurrentExceptionsStressTest.kt
Outdated
Show resolved
Hide resolved
kotlinx-coroutines-core/concurrent/test/StateFlowUpdateCommonTest.kt
Outdated
Show resolved
Hide resolved
kotlinx-coroutines-core/concurrent/test/StateFlowUpdateCommonTest.kt
Outdated
Show resolved
Hide resolved
kotlinx-coroutines-core/nativeDarwin/test/MainDispatcherTest.kt
Outdated
Show resolved
Hide resolved
Co-authored-by: dkhalanskyjb <52952525+dkhalanskyjb@users.noreply.github.com>
Co-authored-by: dkhalanskyjb <52952525+dkhalanskyjb@users.noreply.github.com>
…urrencyTest.kt Co-authored-by: dkhalanskyjb <52952525+dkhalanskyjb@users.noreply.github.com>
…urrencyTest.kt Co-authored-by: dkhalanskyjb <52952525+dkhalanskyjb@users.noreply.github.com>
Co-authored-by: dkhalanskyjb <52952525+dkhalanskyjb@users.noreply.github.com>
|
||
private class IteratorImpl<E>(private var array: Array<E>, private val size: Int) : MutableIterator<E> { | ||
|
||
private class IteratorImpl<E>(private val array: Array<E>) : MutableIterator<E> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Historical note" only refers to the first paragraph. Probably should have split into several comments.
kotlinx-coroutines-core/concurrent/test/flow/StateFlowCommonStressTest.kt
Outdated
Show resolved
Hide resolved
Co-authored-by: dkhalanskyjb <52952525+dkhalanskyjb@users.noreply.github.com>
Co-authored-by: dkhalanskyjb <52952525+dkhalanskyjb@users.noreply.github.com>
Co-authored-by: dkhalanskyjb <52952525+dkhalanskyjb@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great job ensuring that DefaultExecutor
is safe!
…otlin#2833) * Support of new K/N memory model * Dispatchers.Default backed by a pool of workers on Linux and by global_queue on iOS-like * Implementation of Dispatchers.Main that uses the main queue on iOS and default dispatcher on other platforms (Kotlin#2858) * Introduced newSingleThreadDispatcher and newFixedThreadPoolDispatcher * Use proper reentrant locking and CoW arrays on new memory model, make TestBase _almost_ race-free * More thread-safety in Native counterpart and one more test from native-mt * Source-set sharing for tests shared between JVM and K/N * Wrap Obj-C interop into autorelease pool to avoid memory leaks Fixes Kotlin#2914 Co-authored-by: dkhalanskyjb <52952525+dkhalanskyjb@users.noreply.github.com>
…otlin#2833) * Support of new K/N memory model * Dispatchers.Default backed by a pool of workers on Linux and by global_queue on iOS-like * Implementation of Dispatchers.Main that uses the main queue on iOS and default dispatcher on other platforms (Kotlin#2858) * Introduced newSingleThreadDispatcher and newFixedThreadPoolDispatcher * Use proper reentrant locking and CoW arrays on new memory model, make TestBase _almost_ race-free * More thread-safety in Native counterpart and one more test from native-mt * Source-set sharing for tests shared between JVM and K/N * Wrap Obj-C interop into autorelease pool to avoid memory leaks Fixes Kotlin#2914 Co-authored-by: dkhalanskyjb <52952525+dkhalanskyjb@users.noreply.github.com>
Changeset:
For the new memory model:
Dispatchers.Default
backed by multiple workers on Linux/Windows and global_queue on OS XDispatchers.Main
on OS X and iOSnewSingleThreadContext
andnewFixedThreadPoolContext
on Native, shared declaration with JVMFixes #2914