[go: nahoru, domu]

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

[Objective-C] Unable to run blocks in the main thread #463

Closed
renancaraujo opened this issue Apr 13, 2023 · 7 comments
Closed

[Objective-C] Unable to run blocks in the main thread #463

renancaraujo opened this issue Apr 13, 2023 · 7 comments
Labels
lang-objective_c Related to Objective C support package:ffigen

Comments

@renancaraujo
Copy link
renancaraujo commented Apr 13, 2023

Project to illustrate/reproduce this problem is here.

As of right now, the objc/swift interop docs mention:

You can work around this limitation [running stuff on the main thread] by writing some Objective-C code that dispatches your call to the main thread. For more information, see the Objective-C dispatch documentation.

It turns out that doesn't work. I tried to run a Dart function in the main thread, as well as running a swift code in the main thread. None worked.

He swift code:

 @objc public func dispatchToMainAsync(_ block: @escaping () -> Void) {
        print("Hello from Swift")
        if Thread.isMainThread {
            print("Already on main thread")
            block()
        } else {
            print("Dispatching to main thread in an async way!")
            DispatchQueue.main.async {
                block()
            }
        }
    }

The dart coding invoking the bindings:

final block = ObjCBlock21.fromFunction(dismainLib, mainThreadMain);
DispatchToMain.new1(dismainLib).dispatchToMainAsync_(block);

/// ...

void mainThreadMain() {
  print("Hello from main thread!");
}

Unfortunately, that does not work, the mainThreadMain dart code is never called. If i change the swift side to DispatchQueue.main.sync, the dart process hangs.


It also doesnt work even if we change the swift function to run dart code.

...
 DispatchQueue.main.async {
    print("Hello from main thread!") // never called
}
...

@dcharkes
Copy link
Collaborator

cc @liamappelbe

@liamappelbe
Copy link
Contributor

It will be easier to explain what's going on if I first explain how dispatching calls to another thread works. One thread can't simply send functions to run on another thread. The only way this can work is if the other thread is listening for a message, and knows that when it receives a certain kind of message, it should run the attached function. That's what ObjC/Switft's DispatchQueues do (and Dart has Send/ReceivePorts).

So for this to work, there has to be a main thread that is already running a dispatch queue, and is actively listening for your message. If you're running an ordinary ObjC/Swift app, or a Flutter app, that dispatch queue already exists. So your second example, where you send a Swift call to the main thread, should work if you rewrite your project as a flutter app.

However, your first example, where you want to send a Dart function to the main thread, won't work even in a Flutter app. This is because there's no Isolate on that thread. The Dart VM has no presence on the main thread. There's a proposal to support this, but it's still in the early design stage, and it's probably 3-6 months away.

For a pure Dart command line app with only one isolate, I think all the code is probably already running on the "main thread", but it sort of depends what you mean by "main thread". Maybe the "main thread" is internal to the VM? If by "main thread" you mean the thread which you can make certain thread restricted Apple API calls from, then you won't be able to do that in a pure Dart app, regardless of your thread, because they require a whole lot of state that doesn't exist in a pure Dart app. It might be possible to create a thread that you call the "main thread", then set up a DispatchQueue and all the other infra required to make Apple API calls, but I haven't tried and I think it'd probably be pretty involved.

Can you give me some more details about your use case?

@renancaraujo
Copy link
Author

Thanks for your clarification @liamappelbe

Can you give me some more details about your use case?
Not much of a use case yet. I was researching what I could do with dart FFI at this point.

My hypothesis was to use cocoa elements to create a user interface via the FFI, but seems to not be possible at least on a dart-only way via a cli dart app.

If by "main thread" you mean the thread which you can make certain thread restricted Apple API calls from, then you won't be able to do that in a pure Dart app, regardless of your thread, because they require a whole lot of state that doesn't exist in a pure Dart app. It might be possible to create a thread that you call the "main thread", then set up a DispatchQueue and all the other infra required to make Apple API calls, but I haven't tried and I think it'd probably be pretty involved.

Interesting, that is exactly what I mean by "main thread". I will try to run this ona a flutter app and see what can be done

@Zekfad
Copy link
Zekfad commented Apr 20, 2023

Coming from dart-lang/sdk#52106
Have a similar issue but a different use case: In my case I'm trying to setup URL protocol handler.
On windows that's trivial because you add registry key, and OS calls your executable with command line. On OSX you have to use Apple Events and Cocoa APIs that must be run on main thread.
Some sort of workaround would be great (one I can think of is to use custom embedder, but that's overkill and cumbersome for a simple use case, the other is to write wrapper caller app)

@liamappelbe
Copy link
Contributor

We're working on solving this in flutter (flutter/flutter#136314), but for dart command line programs this isn't something we can solve in the VM. As far as macOS is concerned, the "main thread" doesn't exist in pure dart apps, because there is no thread running the OS event loop (there's no DispatchQueue). So even if we did provide something like PlatformIsolates or thread pinning in the Dart VM, it wouldn't help with this use case.

A 3rd party package could solve this problem by writing some native code that initialises all the required OS event loop infrastructure on some thread (which would then become the "main thread" by definition), and they could interact with that native code through FFI. Such a package could also use the Dart C API to spawn an isolate on that thread, and provide a way for users to run code on that isolate, if necessary. This is outside of the scope of what we can do from within the VM though.

@liamappelbe liamappelbe transferred this issue from dart-archive/ffigen Nov 15, 2023
@liamappelbe
Copy link
Contributor

PlatformIsolates are working in Flutter. The request to support this in the Dart VM is not planned.

@Zekfad
Copy link
Zekfad commented Apr 10, 2024

@liamappelbe Won't this be effectively solved by recent shared data proposal's Executor?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
lang-objective_c Related to Objective C support package:ffigen
Projects
Status: Done
Development

No branches or pull requests

4 participants