Skip to content
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

Merged
merged 26 commits into from Nov 15, 2021

Conversation

qwwdfsad
Copy link
Member

@qwwdfsad qwwdfsad commented Jul 16, 2021

Changeset:

  • When the new memory model is disabled, behave the same way as before

For the new memory model:

  • Concurrent tests shared with JVM (disabled for old MM)
  • Dispatchers.Default backed by multiple workers on Linux/Windows and global_queue on OS X
  • Dispatchers.Main on OS X and iOS
  • newSingleThreadContext and newFixedThreadPoolContext on Native, shared declaration with JVM

Fixes #2914

@qwwdfsad qwwdfsad force-pushed the new-native-memory-model branch 2 times, most recently from 91289bd to f96de23 Compare August 29, 2021 11:27
@qwwdfsad qwwdfsad force-pushed the new-native-memory-model branch 2 times, most recently from f96de23 to 407e0b6 Compare October 26, 2021 14:22
* 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
@qwwdfsad qwwdfsad changed the title [BASE BRANCH] Enable new Native memory model Incorporate new Native memory model into kotlinx-coroutines mainline Oct 27, 2021
  * Dispatchers.Default cannot be closed
  * Throwins Dispatchers.Main on Linux and Windows
   * SharedImmutable where necessary
   * Disable MainDispatcherTest with old MM
Copy link
Collaborator

@dkhalanskyjb dkhalanskyjb left a 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.

gradle.properties Outdated Show resolved Hide resolved
kotlinx-coroutines-core/common/src/CoroutineDispatcher.kt Outdated Show resolved Hide resolved
@@ -85,7 +85,7 @@ class AtomicCancellationTest : TestBase() {
}

@Test
fun testSelectDeferredAwaitCancellable() = runBlocking {
fun testSelectDeferredAwaitCancellable() = runMtTest {
Copy link
Collaborator

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?

Copy link
Member Author

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

Copy link
Collaborator

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?

Copy link
Member Author

@qwwdfsad qwwdfsad Nov 11, 2021

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> {
Copy link
Collaborator

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.

Copy link
Collaborator

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.

Copy link
Member Author

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")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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?

Copy link
Member Author

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

kotlinx-coroutines-core/native/src/Builders.kt Outdated Show resolved Hide resolved
kotlinx-coroutines-core/native/src/CoroutineContext.kt Outdated Show resolved Hide resolved
@dkhalanskyjb dkhalanskyjb self-requested a review November 10, 2021 11:26
Copy link
Collaborator

@dkhalanskyjb dkhalanskyjb left a 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.

kotlinx-coroutines-core/jvm/src/Builders.kt Show resolved Hide resolved
@@ -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
Copy link
Collaborator

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.

Copy link
Member Author

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

Copy link
Collaborator

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.

Copy link
Member Author

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/test/TestBase.common.kt Outdated Show resolved Hide resolved
kotlinx-coroutines-core/native/src/Dispatchers.kt Outdated Show resolved Hide resolved
kotlinx-coroutines-core/common/test/TestBase.common.kt Outdated Show resolved Hide resolved
Co-authored-by: dkhalanskyjb <52952525+dkhalanskyjb@users.noreply.github.com>
Copy link
Collaborator

@dkhalanskyjb dkhalanskyjb left a 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.

Co-authored-by: dkhalanskyjb <52952525+dkhalanskyjb@users.noreply.github.com>
qwwdfsad and others added 7 commits November 10, 2021 18:33
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>
kotlinx-coroutines-core/nativeDarwin/src/Dispatchers.kt Outdated Show resolved Hide resolved

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> {
Copy link
Collaborator

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/RunBlockingTest.kt Outdated Show resolved Hide resolved
kotlinx-coroutines-core/common/test/TestBase.common.kt Outdated Show resolved Hide resolved
kotlinx-coroutines-core/common/test/TestBase.common.kt Outdated Show resolved Hide resolved
kotlinx-coroutines-core/common/src/EventLoop.common.kt Outdated Show resolved Hide resolved
qwwdfsad and others added 7 commits November 11, 2021 15:48
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>
Copy link
Collaborator

@dkhalanskyjb dkhalanskyjb left a 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!

@qwwdfsad qwwdfsad merged commit 0427205 into develop Nov 15, 2021
@qwwdfsad qwwdfsad deleted the new-native-memory-model branch November 15, 2021 09:09
yorickhenning pushed a commit to yorickhenning/kotlinx.coroutines that referenced this pull request Jan 28, 2022
…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>
pablobaxter pushed a commit to pablobaxter/kotlinx.coroutines that referenced this pull request Sep 14, 2022
…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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants