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

Support multi-threaded coroutines on Kotlin/Native #462

Closed
elizarov opened this issue Jul 27, 2018 · 155 comments
Closed

Support multi-threaded coroutines on Kotlin/Native #462

elizarov opened this issue Jul 27, 2018 · 155 comments

Comments

@elizarov
Copy link
Contributor

elizarov commented Jul 27, 2018

You can have multiple threads in Kotlin/Native. Each thread can have its own event loop with runBlocking and have number of coroutines running there. Currently communication between those threads via coroutine primitives (like channels) is not supported. This issue it to track enhancement of Kotlin/Native in kotlinx.coroutines library so that all the following becomes possible:

  • Launching coroutines from one thread with a dispatcher on another thread
  • Await/join coroutine running on another thread
  • Send/Receive elements to/from coroutines on other threads

UPDATE: Currently, coroutines are supported only on the main thread. You cannot have coroutines off the main thread due to the way the library is currently structured.

UPDATE 2: the separate library version that supports Kotlin/Native multithreading is released on a regular basis.
For the details and limitations, please follow kotlin-native-sharing.md document.
The latest version: 1.5.2-native-mt

@brettwillis
Copy link

Do you have a ballpark time frame for implementing this (days, weeks, months, ...)? This will help me plan how to implement the first revision of our project. Thanks!

@mohit-gurumukhani
Copy link

Second that. Can we please get a rough estimate?

@elizarov
Copy link
Contributor Author

elizarov commented Aug 3, 2018

We're in the design phase now. I'll update you on the status in couple of weeks.

@Alex009
Copy link

Alex009 commented Oct 11, 2018

Have any progress?

@elizarov
Copy link
Contributor Author

We have a work-in-progress branch in this repo with some of the code that is implemented, but it is way too complex a change, so the work there was stopped. It is hard to get it done in the current state. We've refocused our efforts on delivering high-quality single-threaded coroutines which work really well for sharing logic between Android and iOS UI apps (I highly recommend to checkout the code of KotlinConf app here https://github.com/JetBrains/kotlinconf-app). With respect to multithreading, we'll be back to drawing board to see how this story can be made easier to code with. Don't expect results soon, though.

@LouisCAD
Copy link
Contributor

LouisCAD commented Oct 11, 2018 via email

@elizarov
Copy link
Contributor Author

elizarov commented Oct 12, 2018

Yes, it works without runBlocking. The only extra effort you have to make, is you have to write a trivial UI CoroutineDispatcher for iOS. We don't include it in the library yet (that's issue #470), but you can copy-and-paste code from KotlinConf app (swift version here https://github.com/JetBrains/kotlinconf-app/blob/master/konfios/konfswift/ui/UI.swift) of from discussion in #470 (Kotlin version here #470 (comment))

@luca992
Copy link

luca992 commented Oct 16, 2018

@elizarov Trying to convert Waiting for a job example to work without run blocking using (#470 (comment)) in a native macOs program. But I am still getting There is no event loop. Use runBlocking { ... } to start one. I think it probably is because my native program isn't starting NSRunLoop's mainloop. I can't quite figure it out.

I've tried starting the loop like:

fun main(args: Array<String>) {
    val job = GlobalScope.launch(MainLoopDispatcher) { // launch new coroutine and keep a reference to its Job
        delay(1000L)
        println("World!")
    }
    println("Hello,")
    NSRunLoop.currentRunLoop().runUntilDate(NSDate().dateByAddingTimeInterval(3.toDouble()))
}

But I don't think I'm doing that correctly, any ideas?

@qwwdfsad
Copy link
Member

qwwdfsad commented Oct 16, 2018

@luca992 please use runBlocking:

fun main(args: Array<String>) = runBlocking<Unit> { // <- This line
    val job = GlobalScope.launch(MainLoopDispatcher) {
        delay(1000L)
        println("World!")
    }
    println("Hello,")
}

@luca992
Copy link

luca992 commented Oct 16, 2018

@qwwdfsad I know that it works with run blocking... Are you saying it is only possible to run without runBlocking on iOS for some reason?

Edit:
I'm using Qt for the UI in my native kotlin desktop app. Didn't figure out NSRunLoop. But, I figured out that if I run QGuiApplication.exec() inside runBlocking, I can call coroutines with Dispatchers.Unconfined. (And not have to wrap each one in runBlocking) .... Which is great beacuse now I can share presenters between the android app and the native desktop apps 👍

@LanderlYoung
Copy link

Is there any solution to support MultiThreaded coroutines yet?
As far as I know, under current Kotlin/Native threading model, if an object is going to be shared between workers/threads, it must be either frozen or use a DetachedObjectGraph, while none of which works with CouroutineDispatcher, because we have a Continuation to pass through. Sadly the Continuation captures coroutine context (maybe more othre objects), which makes it impossible to froze or detach a Continuation.

IMHO, It's nearly impossible to implement a multi-threading coroutine dispatcher under current Kotlin/Native threading model. Should we redesign the threading model?

Maybe it is good for writing rebust code that, Kotlin/Native implement Lock/Thread/ThreadPool, and use those tools to implement coroutine. For those just want to offload jobs to different thread, it is good enough to use coroutine. And for those who cares very much about performace, give them the ability to use raw thread/thread pool. For example, to write a high performance low latency audio app, one usually create threads with the hieghest scheduling proiorty, and event bind those threads to a certain cpu core to eliminate context switch.

@LanderlYoung
Copy link

Current my solution is to totally move the threading part into native ios code. like this.

private val requestingHashMap = hashMapOf<String, IosHttpGetAgent.Callback>()

fun notifyHttpGetResponse(url: String, result: String?, error: String) {
    requestingHashMap.remove(url)?.onGetResult(url, result, error)
}

@Throws(IOException::class)
actual suspend fun httpGet(url: String): String {
    return suspendCoroutine { continuation ->
        val cb = object : IosHttpGetAgent.Callback {
            override fun onGetResult(url: String, result: String?, error: String) {
                if (result != null) {
                    continuation.resume(result)
                } else {
                    continuation.resumeWith(Result.failure(IOException(error)))
                }
            }
        }
        requestingHashMap[url] = cb
        iosHttpGetAgent.value!!.httpGet(url)
    }
}

While on the swift code.

    func httpGet(url: String) {
        
        let task = URLSession.shared.dataTask(with: URL(string: url)!) { (data, response, error) in
            if let resultData = data {
                DispatchQueue.main.async {
                    ActualKt.notifyHttpGetResponse(
                        url:url,
                        result: String(data: resultData, encoding: .utf8)!,
                        error: "success")
                }
            } else {
                DispatchQueue.main.async {
                    ActualKt.notifyHttpGetResponse(
                        url:url,
                        result: nil,
                        error: "success")
                }
            }
        }
        task.resume()
        
    }

So kotlin/native code runs totally on the main thread.

@elizarov
Copy link
Contributor Author

Running totally on the main is the only solution for now. You can track #829 which will slightly expand your options and you'll be able to run coroutines separately on each threads (no easy way to communicate, though).

@LanderlYoung
Copy link

Hi, any progress on this issue? Or any possible solution for this issue? @elizarov
I'll be glad to know about this. thanks.

@horita-yuya
Copy link

I'm also very concerned about this.

@SeekDaSky
Copy link

Well, the current state of multithreading in K/N is not really suitable for coroutines, the simple fact of giving a Continuation to a worker freeze the continuation, thus freezing the captured state and making it immutable (and pretty much unsuable).

For me multithreded coroutines is simply impossible with the current model.

@sellmair
Copy link
Member

@SeekDaSky I am not very experienced with K/N's concurrency model nor with the way, coroutines work under the hood, but I would also want to see support for multi-threaded coroutines in K/N.
Isn't there a way to make the continuation an immutable object that passes a new copy with the new state to the next K/N worker?

@SeekDaSky
Copy link

If I understand correctly you could detach the continuation and keep it mutable, but the state would not be accessible from another worker, so we still can't share values between threads.

We could heavily use channels and the actor paradigm to avoid any variable being shared, but this could lead to some performance degradation.

And this is just the developper side, developing a dispatcher with the limitations of the current concurrency model probably is daunting.

@sellmair
Copy link
Member

No, this might be pretty naive, but wouldn't it be possible to capture the whole state of the coroutine in some structure (let's say a data class) and just pass a new copy to the next worker?

@SeekDaSky
Copy link

To achieve this I think you just have to detach the continuation and re-attach it inside the next worker but you still can't have two threads accessing the data at the same time. And this could lead to some weird side effect if you share a value between two continuations by mistake

@sellmair
Copy link
Member

And this could lead to some weird side effect if you share a value between two continuations by mistake

Would you mind explaining this a little? I would be super interested ☺️

qwwdfsad pushed a commit that referenced this issue Dec 14, 2021
* Provides newSingleThreadedContext.
* Provides Dispatchers.Main on iOS, Dispatchers.Default everywhere.
* Coroutine references (Job), all kinds of channels and StateFlow are shareable across workers.
* Each individual coroutine is confined to a single worker.
* Update Dispatchers docs to account for native-mt changes.
* Multithreaded support in select expression.
* Fix ObjC autorelease object leaks with native-mt dispatchers (#2477)

Additional fixes:
* Fixed broadcast builder with different thread
* Fixed adding a child to a frozen parent job

Fixes #462
Fixes #470
Fixes #765
Fixes #1645
Fixes #1751
Fixes #1828
Fixes #1831
Fixes #1764
Fixes #2064
Fixes #2025
Fixes #2226
Fixes #2138
Fixes #2263
Fixes #2322
Fixes #2283
Fixes #2688
Fixes #2398
qwwdfsad pushed a commit that referenced this issue Dec 15, 2021
* Provides newSingleThreadedContext.
* Provides Dispatchers.Main on iOS, Dispatchers.Default everywhere.
* Coroutine references (Job), all kinds of channels and StateFlow are shareable across workers.
* Each individual coroutine is confined to a single worker.
* Update Dispatchers docs to account for native-mt changes.
* Multithreaded support in select expression.
* Fix ObjC autorelease object leaks with native-mt dispatchers (#2477)

Additional fixes:
* Fixed broadcast builder with different thread
* Fixed adding a child to a frozen parent job

Fixes #462
Fixes #470
Fixes #765
Fixes #1645
Fixes #1751
Fixes #1828
Fixes #1831
Fixes #1764
Fixes #2064
Fixes #2025
Fixes #2226
Fixes #2138
Fixes #2263
Fixes #2322
Fixes #2283
Fixes #2688
Fixes #2398
qwwdfsad pushed a commit that referenced this issue Dec 15, 2021
* Provides newSingleThreadedContext.
* Provides Dispatchers.Main on iOS, Dispatchers.Default everywhere.
* Coroutine references (Job), all kinds of channels and StateFlow are shareable across workers.
* Each individual coroutine is confined to a single worker.
* Update Dispatchers docs to account for native-mt changes.
* Multithreaded support in select expression.
* Fix ObjC autorelease object leaks with native-mt dispatchers (#2477)

Additional fixes:
* Fixed broadcast builder with different thread
* Fixed adding a child to a frozen parent job

Fixes #462
Fixes #470
Fixes #765
Fixes #1645
Fixes #1751
Fixes #1828
Fixes #1831
Fixes #1764
Fixes #2064
Fixes #2025
Fixes #2226
Fixes #2138
Fixes #2263
Fixes #2322
Fixes #2283
Fixes #2688
Fixes #2398
qwwdfsad pushed a commit that referenced this issue Dec 15, 2021
* Provides newSingleThreadedContext.
* Provides Dispatchers.Main on iOS, Dispatchers.Default everywhere.
* Coroutine references (Job), all kinds of channels and StateFlow are shareable across workers.
* Each individual coroutine is confined to a single worker.
* Update Dispatchers docs to account for native-mt changes.
* Multithreaded support in select expression.
* Fix ObjC autorelease object leaks with native-mt dispatchers (#2477)

Additional fixes:
* Fixed broadcast builder with different thread
* Fixed adding a child to a frozen parent job

Fixes #462
Fixes #470
Fixes #765
Fixes #1645
Fixes #1751
Fixes #1828
Fixes #1831
Fixes #1764
Fixes #2064
Fixes #2025
Fixes #2226
Fixes #2138
Fixes #2263
Fixes #2322
Fixes #2283
Fixes #2688
Fixes #2398
qwwdfsad pushed a commit that referenced this issue Dec 15, 2021
* Provides newSingleThreadedContext.
* Provides Dispatchers.Main on iOS, Dispatchers.Default everywhere.
* Coroutine references (Job), all kinds of channels and StateFlow are shareable across workers.
* Each individual coroutine is confined to a single worker.
* Update Dispatchers docs to account for native-mt changes.
* Multithreaded support in select expression.
* Fix ObjC autorelease object leaks with native-mt dispatchers (#2477)

Additional fixes:
* Fixed broadcast builder with different thread
* Fixed adding a child to a frozen parent job

Fixes #462
Fixes #470
Fixes #765
Fixes #1645
Fixes #1751
Fixes #1828
Fixes #1831
Fixes #1764
Fixes #2064
Fixes #2025
Fixes #2226
Fixes #2138
Fixes #2263
Fixes #2322
Fixes #2283
Fixes #2688
Fixes #2398
qwwdfsad pushed a commit that referenced this issue Dec 15, 2021
* Provides newSingleThreadedContext.
* Provides Dispatchers.Main on iOS, Dispatchers.Default everywhere.
* Coroutine references (Job), all kinds of channels and StateFlow are shareable across workers.
* Each individual coroutine is confined to a single worker.
* Update Dispatchers docs to account for native-mt changes.
* Multithreaded support in select expression.
* Fix ObjC autorelease object leaks with native-mt dispatchers (#2477)

Additional fixes:
* Fixed broadcast builder with different thread
* Fixed adding a child to a frozen parent job

Fixes #462
Fixes #470
Fixes #765
Fixes #1645
Fixes #1751
Fixes #1828
Fixes #1831
Fixes #1764
Fixes #2064
Fixes #2025
Fixes #2226
Fixes #2138
Fixes #2263
Fixes #2322
Fixes #2283
Fixes #2688
Fixes #2398
qwwdfsad pushed a commit that referenced this issue Dec 16, 2021
* Provides newSingleThreadedContext.
* Provides Dispatchers.Main on iOS, Dispatchers.Default everywhere.
* Coroutine references (Job), all kinds of channels and StateFlow are shareable across workers.
* Each individual coroutine is confined to a single worker.
* Update Dispatchers docs to account for native-mt changes.
* Multithreaded support in select expression.
* Fix ObjC autorelease object leaks with native-mt dispatchers (#2477)

Additional fixes:
* Fixed broadcast builder with different thread
* Fixed adding a child to a frozen parent job

Fixes #462
Fixes #470
Fixes #765
Fixes #1645
Fixes #1751
Fixes #1828
Fixes #1831
Fixes #1764
Fixes #2064
Fixes #2025
Fixes #2226
Fixes #2138
Fixes #2263
Fixes #2322
Fixes #2283
Fixes #2688
Fixes #2398
qwwdfsad pushed a commit that referenced this issue Dec 22, 2021
* Provides newSingleThreadedContext.
* Provides Dispatchers.Main on iOS, Dispatchers.Default everywhere.
* Coroutine references (Job), all kinds of channels and StateFlow are shareable across workers.
* Each individual coroutine is confined to a single worker.
* Update Dispatchers docs to account for native-mt changes.
* Multithreaded support in select expression.
* Fix ObjC autorelease object leaks with native-mt dispatchers (#2477)

Additional fixes:
* Fixed broadcast builder with different thread
* Fixed adding a child to a frozen parent job

Fixes #462
Fixes #470
Fixes #765
Fixes #1645
Fixes #1751
Fixes #1828
Fixes #1831
Fixes #1764
Fixes #2064
Fixes #2025
Fixes #2226
Fixes #2138
Fixes #2263
Fixes #2322
Fixes #2283
Fixes #2688
Fixes #2398
@qwwdfsad
Copy link
Member

1.6.0-native-mt has been released.

1.6.x will be the last series of native-mt releases and we are effectively decommissioning native-mt in the favour of the new memory model that is supported out-of-the-box in 1.6.0 coroutines release.

Coroutines 1.7.0 will be released without native-mt companion and we are gradually stopping the support of native-mt during the course of 1.6.x lifecycle. We still are ready to accept PRs or release versions with the new Kotlin if there is a need to, but new coroutine features won't get native-mt support.

We encourage developers to try out and evaluate the new MM: that's our bet, we are investing our resources, development and testing in it and the quality of regular coroutine releases (including pre-release testing, a priority of the bug-fixes, extensive support of existing features and so on) is much higher than of native-mt builds.

The known limitations of the new memory model are described in the corresponding document (NB: it's being updated each release)

@LouisCAD
Copy link
Contributor

@qwwdfsad the versions in the "Update the libraries" of the New memory model document doesn't reference the newer versions of kotlinx.coroutines and Kotlin yet.

@dkhalanskyjb
Copy link
Collaborator

Thanks for the heads-up! JetBrains/kotlin#4687

@TrevorStoneEpic
Copy link

I know it is starting to be phased out, but can we expect a 1.6.1-native-mt release?

qwwdfsad pushed a commit that referenced this issue Apr 5, 2022
* Provides newSingleThreadedContext.
* Provides Dispatchers.Main on iOS, Dispatchers.Default everywhere.
* Coroutine references (Job), all kinds of channels and StateFlow are shareable across workers.
* Each individual coroutine is confined to a single worker.
* Update Dispatchers docs to account for native-mt changes.
* Multithreaded support in select expression.
* Fix ObjC autorelease object leaks with native-mt dispatchers (#2477)

Additional fixes:
* Fixed broadcast builder with different thread
* Fixed adding a child to a frozen parent job

Fixes #462
Fixes #470
Fixes #765
Fixes #1645
Fixes #1751
Fixes #1828
Fixes #1831
Fixes #1764
Fixes #2064
Fixes #2025
Fixes #2226
Fixes #2138
Fixes #2263
Fixes #2322
Fixes #2283
Fixes #2688
Fixes #2398
qwwdfsad pushed a commit that referenced this issue Apr 5, 2022
* Provides newSingleThreadedContext.
* Provides Dispatchers.Main on iOS, Dispatchers.Default everywhere.
* Coroutine references (Job), all kinds of channels and StateFlow are shareable across workers.
* Each individual coroutine is confined to a single worker.
* Update Dispatchers docs to account for native-mt changes.
* Multithreaded support in select expression.
* Fix ObjC autorelease object leaks with native-mt dispatchers (#2477)

Additional fixes:
* Fixed broadcast builder with different thread
* Fixed adding a child to a frozen parent job

Fixes #462
Fixes #470
Fixes #765
Fixes #1645
Fixes #1751
Fixes #1828
Fixes #1831
Fixes #1764
Fixes #2064
Fixes #2025
Fixes #2226
Fixes #2138
Fixes #2263
Fixes #2322
Fixes #2283
Fixes #2688
Fixes #2398
Fixes #3136
@TrevorStoneEpic
Copy link

For anyone following this thread 1.6.1-native-mt has been released!

qwwdfsad pushed a commit that referenced this issue Jun 22, 2022
* Provides newSingleThreadedContext.
* Provides Dispatchers.Main on iOS, Dispatchers.Default everywhere.
* Coroutine references (Job), all kinds of channels and StateFlow are shareable across workers.
* Each individual coroutine is confined to a single worker.
* Update Dispatchers docs to account for native-mt changes.
* Multithreaded support in select expression.
* Fix ObjC autorelease object leaks with native-mt dispatchers (#2477)

Additional fixes:
* Fixed broadcast builder with different thread
* Fixed adding a child to a frozen parent job

Fixes #462
Fixes #470
Fixes #765
Fixes #1645
Fixes #1751
Fixes #1828
Fixes #1831
Fixes #1764
Fixes #2064
Fixes #2025
Fixes #2226
Fixes #2138
Fixes #2263
Fixes #2322
Fixes #2283
Fixes #2688
Fixes #2398
Fixes #3136
@qwwdfsad qwwdfsad mentioned this issue Aug 2, 2022
1 task
@elizarov elizarov unpinned this issue Sep 5, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests