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

Mixing runtimes #96

Open
agg23 opened this issue Jul 17, 2023 · 4 comments
Open

Mixing runtimes #96

agg23 opened this issue Jul 17, 2023 · 4 comments

Comments

@agg23
Copy link
Contributor

agg23 commented Jul 17, 2023

I'm interested in how others may have mixed the AppKit runtime with something Rust-centric, whether async-std, tokio, or something else. Use cases are primarily in mixed platform systems, the core Rust code can't be designed around the standard Apple runtime, and needs something from the Rust ecosystem to actually be usable.

I have found that naively calling NSApplication.run() within tokio::task::spawn_blocking results in the event loop hanging.

@ryanmcgrath
Copy link
Owner

Hmmm, can you throw together a repo or test case? It's probably a straightforward (as you say) case, but I've run async-std in the background of a cacao app without any issues. It should work so I'm wondering if there's something I'm missing.

(I was building a magic-wormhole for macOS app before I got... sidetracked... by life, lol: https://twitter.com/ryanmcgrath/status/1657967789142069249)

@agg23
Copy link
Contributor Author

agg23 commented Jul 18, 2023

Most likely it's my inexperience with Rust runtimes (including Tokio), but here's a simple example of Tokio functioning normally, but nothing happening from AppKit:

use std::time::Duration;

use cacao::appkit::{App, AppDelegate};

struct MacApp;

impl AppDelegate for MacApp {
    fn did_finish_launching(&self) {
        println!("AppKit running");
    }
}

#[tokio::main]
async fn main() {
    tokio::task::spawn(async {
        loop {
            tokio::time::sleep(Duration::from_secs(1)).await;

            println!("Timer");
        }
    });

    println!("Starting AppKit");

    tokio::task::spawn_blocking(|| {
        App::new("com.example.cacao_runtime", MacApp).run();
    });

    println!("After AppKit");

    // We await this to keep the runtime alive/in scope
    let _ = tokio::task::spawn_blocking(|| {
        let mut spin_count: u128 = 0;

        loop {
            if spin_count % 100_000_000 == 0 {
                println!("Tick");
            }

            spin_count += 1;
        }
    })
    .await;
}
[package]
name = "cacao_runtime"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
cacao = "0.3.2"
tokio = { version = "1.29.1", features = ["rt-multi-thread", "macros", "time"] }

Expected output would be something like:

Starting AppKit
After AppKit
AppKit running
Tick
Tick
Tick
Tick
Tick
Tick
Timer
Tick
Tick

but what we actually get is:

Starting AppKit
After AppKit
Tick
Tick
Tick
Tick
Tick
Tick
Timer
Tick
Tick

That is, just missing the applicationDidFinishLaunching callback. I've tried stepping through to see where it is getting hung up, but as you know debugging Rust and mixing that with weird Obj-C bridging makes it difficult to tell.

@agg23
Copy link
Contributor Author

agg23 commented Jul 18, 2023

I neglected to check Console (it's been a while since I've done native Mac dev) and it has the errors I would expect to see:

nextEventMatchingMask should only be called from the Main Thread!

	0   CoreFoundation                      0x00000001849bf154 __exceptionPreprocess + 176
	1   libobjc.A.dylib                     0x00000001844de4d4 objc_exception_throw + 60
	2   AppKit                              0x0000000187b65730 -[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 2844
	3   AppKit                              0x0000000187b59344 -[NSApplication run] + 464
	4   cacao_runtime                       0x0000000102e17d00 _ZN60_$LT$$LP$$RP$$u20$as$u20$objc..message..MessageArguments$GT$6invoke17h12be76bf795947fcE + 64
	5   cacao_runtime                       0x0000000102e1540c _ZN4objc7message8platform15send_unverified17ha796ec4ed66cc196E + 132
	6   cacao_runtime                       0x0000000102d9cc78 _ZN5cacao6appkit3app12App$LT$T$GT$3run17hf58e0486b6006dd4E + 1128
	7   cacao_runtime                       0x0000000102d9a2e4 _ZN13cacao_runtime4main28_$u7b$$u7b$closure$u7d$$u7d$28_$u7b$$u7b$closure$u7d$$u7d$17hd8172443011b6a22E + 48
	8   cacao_runtime                       0x0000000102d8f1b8 _ZN102_$LT$tokio..runtime..blocking..task..BlockingTask$LT$T$GT$$u20$as$u20$core..future..future..Future$GT$4poll17hb9c9f029929224eaE + 132
	9   cacao_runtime                       0x0000000102da8304 _ZN5tokio7runtime4task4core17Core$LT$T$C$S$GT$4poll28_$u7b$$u7b$closure$u7d$$u7d$17h7f5a88512ab017caE + 212
	10  cacao_runtime                       0x0000000102dad7f0 _ZN5tokio4loom3std11unsafe_cell19UnsafeCell$LT$T$GT$8with_mut17hde88e3fb917c4a1fE + 116
	11  cacao_runtime                       0x0000000102da7948 _ZN5tokio7runtime4task4core17Core$LT$T$C$S$GT$4poll17hda124c6f292a9904E + 60
	12  cacao_runtime                       0x0000000102d8a8e8 _ZN5tokio7runtime4task7harness11poll_future28_$u7b$$u7b$closure$u7d$$u7d$17h17a69bd3afb875b7E + 52
	13  cacao_runtime                       0x0000000102daa5c4 _ZN115_$LT$core..panic..unwind_safe..AssertUnwindSafe$LT$F$GT$$u20$as$u20$core..ops..function..FnOnce$LT$$LP$$RP$$GT$$GT$9call_once17h377b375a72208ef4E + 32
	14  cacao_runtime                       0x0000000102d84a30 _ZN3std9panicking3try7do_call17h7121ce0bd98866e0E + 80
	15  cacao_runtime                       0x0000000102d86abc __rust_try + 32
	16  cacao_runtime                       0x0000000102d83220 _ZN3std9panicking3try17h50c99e988ef95498E + 88
	17  cacao_runtime                       0x0000000102d9abb4 _ZN3std5panic12catch_unwind17h21c3e78dabca9ef9E + 32
	18  cacao_runtime                       0x0000000102d895ec _ZN5tokio7runtime4task7harness11poll_future17h0920160fd41ff4f6E + 88
	19  cacao_runtime                       0x0000000102d8b37c _ZN5tokio7runtime4task7harness20Harness$LT$T$C$S$GT$10poll_inner17h17b2d0168c33e1b8E + 152
	20  cacao_runtime                       0x0000000102d8cf00 _ZN5tokio7runtime4task7harness20Harness$LT$T$C$S$GT$4poll17h645251064a14bfbaE + 28
	21  cacao_runtime                       0x0000000102d90e7c _ZN5tokio7runtime4task3raw4poll17h0d21aba4197c8f20E + 36
	22  cacao_runtime                       0x0000000102deefec _ZN5tokio7runtime4task3raw7RawTask4poll17h8a889579cb74c1a9E + 52
	23  cacao_runtime                       0x0000000102e144d8 _ZN5tokio7runtime4task20UnownedTask$LT$S$GT$3run17h548292c9c263f839E + 64
	24  cacao_runtime                       0x0000000102de36f4 _ZN5tokio7runtime8blocking4pool4Task3run17h5490f2426d23d212E + 32
	25  cacao_runtime                       0x0000000102de5a20 _ZN5tokio7runtime8blocking4pool5Inner3run17hc7336af942a1a08bE + 2048
	26  cacao_runtime                       0x0000000102de5168 _ZN5tokio7runtime8blocking4pool7Spawner12spawn_thread28_$u7b$$u7b$closure$u7d$$u7d$17he87d18d8647265eeE + 144
	27  cacao_runtime                       0x0000000102df4114 _ZN3std10sys_common9backtrace28__rust_begin_short_backtrace17h969e25d9b1883eefE + 16
	28  cacao_runtime                       0x0000000102dd6944 _ZN3std6thread7Builder16spawn_unchecked_28_$u7b$$u7b$closure$u7d$$u7d$28_$u7b$$u7b$closure$u7d$$u7d$17h99843e47d788cba7E + 40
	29  cacao_runtime                       0x0000000102df40a0 _ZN115_$LT$core..panic..unwind_safe..AssertUnwindSafe$LT$F$GT$$u20$as$u20$core..ops..function..FnOnce$LT$$LP$$RP$$GT$$GT$9call_once17hf6e8443d19a8f93dE + 40
	30  cacao_runtime                       0x0000000102df5328 _ZN3std9panicking3try7do_call17h8a1e22ca148ae7c5E + 80
	31  cacao_runtime                       0x0000000102dfb0f4 __rust_try + 32
	32  cacao_runtime                       0x0000000102df481c _ZN3std9panicking3try17h7145ca67fa1682d1E + 76
	33  cacao_runtime                       0x0000000102dd67d0 _ZN3std6thread7Builder16spawn_unchecked_28_$u7b$$u7b$closure$u7d$$u7d$17hd8478c3e6c7d96a7E + 396
	34  cacao_runtime                       0x0000000102ddb0c8 _ZN4core3ops8function6FnOnce40call_once$u7b$$u7b$vtable.shim$u7d$$u7d$17h28bbec7738f2b412E + 24
	35  cacao_runtime                       0x0000000102e80f00 _ZN3std3sys4unix6thread6Thread3new12thread_start17h999abeaf027c3bbaE + 48
	36  libsystem_pthread.dylib             0x0000000184867fa8 _pthread_start + 148
	37  libsystem_pthread.dylib             0x0000000184862da0 thread_start + 8

I'm not horribly surprised at this, but I am curious how it knows that it is not running in the main thread (is it grabbing the process's thread list and ordering them, instead of just marking the thread it started on as main?). I'm guessing the trivial solution is to start the Tokio runtime from within AppKit's, which would be more traditional for Apple apps.

@ryanmcgrath
Copy link
Owner

Yeah, so what I've done is just start a new thread inside of did_finish_launching for the async stuff to live in, then pass things over to be handled.

The Elm-style of messaging is still optimal for running the UI flow in cacao, so just message pass back when things are done. I can try to clean up and open source my example code for this if it'd be helpful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants