swiftlang / swift-java

Apache License 2.0
724 stars 27 forks source link

Detach from any threads we have attached to. #173

Open lhoward opened 1 week ago

lhoward commented 1 week ago

Fixes: #172

ktoso commented 1 week ago

Oh boy, this will be quite some trouble outside of simple examples (where we'll have to deal with the fact that Task{} and friends exist)... because if we can't make JNI calls from a thread that did not register, technically after every suspension point we may be on a different thread, so we may need to keep trying to register threads.

We also have no control over dispatch deciding to start/stop threads, so we may leave lingering resources since we'd never detach some of the threads hm.

I think in the long term we're going to need to write an executor, make it the global and main executor and there we'd have to control threads and this way we'd be able to detach them consistently... 🤔 We are considering offering APIs to customize the global default executors so this may be possible nicely in the future, but that's not until next Swift release hm.

Meanwhile I'll think about your idea here.

lhoward commented 1 week ago

This relates to #157 – if we remove the affinity between projected objects and environments, then we can always attach a thread on demand (and register a detach destructor). I'm not sure what the right thing to do is when there are multiple VMs, but the JNI spec is quite clear that environments are pre-thread (FWIW, Android only ever has a single VM).

lhoward commented 1 week ago

Given the necessary hooks in the Swift runtime (I haven't looked too closely), we could definitely extend our AExecutor to be a concurrent executor. But whilst idiomatic, a platform-agnostic approach that uses a thread pool which attaches/detaches should be fine.

The elephant in the room here though is going to be third-party libraries that use libdispatch instead of Swift concurrency. (I've already had to carefully audit the dependencies in my application to ensure that nothing calls DispatchQueue.main.async() as the main queue is never started on Android.) As long as it is not too expensive, I think a solution that attaches on demand and supports the continued use of libdispatch is desirable.

ktoso commented 6 days ago

Heh main.async() is a pretty funny wrinkle. Having that said, if we had proper Swift concurrency adopting libraries they'd do MainActor and then we're in the middle of allowing the executor for that actor -- so we'd be able to then replace it with some more meaningful thread on android, and not have to ban using it. This won't be in Swift 6.0 though, but is something we're investigating

lhoward commented 2 hours ago

CI error appears unrelated

<unknown>:0: error: unknown argument: '-abi-comments-in-module-interface'
<unknown>:0: error: unknown argument: '-abi-comments-in-module-interface'