From c061240dfcf87bc5d3a1b588977bc4c270746b82 Mon Sep 17 00:00:00 2001 From: AzureMarker Date: Wed, 2 Feb 2022 21:41:33 -0800 Subject: [PATCH 1/7] Add futures-tokio-basic example (not working yet) There's some issues with the example right now. See https://github.com/Meziu/ctru-rs/pull/36#issuecomment-1028617954 --- ctru-rs/Cargo.toml | 1 + ctru-rs/examples/futures-tokio-basic.rs | 65 +++++++++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 ctru-rs/examples/futures-tokio-basic.rs diff --git a/ctru-rs/Cargo.toml b/ctru-rs/Cargo.toml index 1b303f3..ff327f5 100644 --- a/ctru-rs/Cargo.toml +++ b/ctru-rs/Cargo.toml @@ -26,6 +26,7 @@ toml = "0.5" [dev-dependencies] ferris-says = "0.2.1" time = "0.3.7" +tokio = { version = "1.16", features = ["rt", "time", "sync", "macros"] } [features] default = ["romfs"] diff --git a/ctru-rs/examples/futures-tokio-basic.rs b/ctru-rs/examples/futures-tokio-basic.rs new file mode 100644 index 0000000..e8e8775 --- /dev/null +++ b/ctru-rs/examples/futures-tokio-basic.rs @@ -0,0 +1,65 @@ +use ctru::console::Console; +use ctru::services::hid::KeyPad; +use ctru::services::{Apt, Hid}; +use ctru::Gfx; +use std::time::Duration; + +fn main() { + ctru::init(); + let gfx = Gfx::default(); + let hid = Hid::init().expect("Couldn't obtain HID controller"); + let apt = Apt::init().expect("Couldn't obtain APT controller"); + let _console = Console::init(gfx.top_screen.borrow_mut()); + + // FIXME: replace this with `Ps` when #39 merges + assert!(unsafe { ctru_sys::psInit() } >= 0); + + // Give ourselves up to 30% of the system core's time + apt.set_app_cpu_time_limit(30) + .expect("Failed to enable system core"); + + println!("Starting runtime..."); + + let (exit_sender, mut exit_receiver) = tokio::sync::oneshot::channel(); + let runtime = tokio::runtime::Builder::new_current_thread() + .enable_time() + .build() + .expect("Couldn't build runtime"); + + let runtime_thread = ctru::thread::Builder::new() + .affinity(1) + .spawn(move || { + runtime.block_on(async move { + let mut wake_time = tokio::time::Instant::now() + Duration::from_secs(1); + loop { + let sleep_future = tokio::time::sleep_until(wake_time); + + tokio::select! { + _ = &mut exit_receiver => break, + _ = sleep_future => { + println!("Tick"); + wake_time += Duration::from_secs(1); + } + } + } + }); + }) + .expect("Failed to create runtime thread"); + + println!("Runtime started!"); + + while apt.main_loop() { + hid.scan_input(); + + if hid.keys_down().contains(KeyPad::KEY_START) { + println!("Shutting down..."); + let _ = exit_sender.send(()); + let _ = runtime_thread.join(); + break; + } + + gfx.flush_buffers(); + gfx.swap_buffers(); + gfx.wait_for_vblank(); + } +} From 88315aec61a21f5e37d35aaf109b9e259d9cc3f9 Mon Sep 17 00:00:00 2001 From: AzureMarker Date: Fri, 4 Feb 2022 22:27:13 -0800 Subject: [PATCH 2/7] Group together thread info calls --- ctru-rs/src/thread.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ctru-rs/src/thread.rs b/ctru-rs/src/thread.rs index 225b1f8..14d7a56 100644 --- a/ctru-rs/src/thread.rs +++ b/ctru-rs/src/thread.rs @@ -1040,7 +1040,10 @@ mod thread_info { } pub fn set(thread: Thread) { - CTRU_THREAD_INFO.with(|c| assert!(c.borrow().is_none())); - CTRU_THREAD_INFO.with(move |c| *c.borrow_mut() = Some(ThreadInfo { thread })); + CTRU_THREAD_INFO.with(move |c| { + let mut thread_info = c.borrow_mut(); + assert!(thread_info.is_none()); + *thread_info = Some(ThreadInfo { thread }); + }); } } From 133df927c0602ef5ca622dc07463812f060d219d Mon Sep 17 00:00:00 2001 From: AzureMarker Date: Sat, 5 Feb 2022 12:02:00 -0800 Subject: [PATCH 3/7] Add thread locals example to make sure they work --- ctru-rs/examples/thread_locals.rs | 66 +++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 ctru-rs/examples/thread_locals.rs diff --git a/ctru-rs/examples/thread_locals.rs b/ctru-rs/examples/thread_locals.rs new file mode 100644 index 0000000..937855a --- /dev/null +++ b/ctru-rs/examples/thread_locals.rs @@ -0,0 +1,66 @@ +use ctru::console::Console; +use ctru::services::hid::KeyPad; +use ctru::services::{Apt, Hid}; +use ctru::Gfx; +use std::cell::RefCell; + +std::thread_local! { + static MY_LOCAL: RefCell<&'static str> = RefCell::new("initial value"); +} + +fn main() { + ctru::init(); + let gfx = Gfx::default(); + gfx.top_screen.borrow_mut().set_wide_mode(true); + let hid = Hid::init().expect("Couldn't obtain HID controller"); + let apt = Apt::init().expect("Couldn't obtain APT controller"); + let _console = Console::init(gfx.top_screen.borrow_mut()); + + // Give ourselves up to 30% of the system core's time + apt.set_app_cpu_time_limit(30) + .expect("Failed to enable system core"); + + MY_LOCAL.with(|local| { + println!("Initial value on main thread: {}", local.borrow()); + *local.borrow_mut() = "second value"; + }); + MY_LOCAL.with(|local| { + println!("Value on main thread after mutation: {}", local.borrow()); + }); + + ctru::thread::Builder::new() + .affinity(1) + .spawn(move || { + MY_LOCAL.with(|local| { + println!("Initial value on second thread: {}", local.borrow()); + *local.borrow_mut() = "third value"; + }); + MY_LOCAL.with(|local| { + println!("Value on second thread after mutation: {}", local.borrow()); + }); + }) + .expect("Failed to create thread") + .join() + .expect("Failed to join on thread"); + + MY_LOCAL.with(|local| { + println!( + "Value on main thread after second thread exits: {}", + local.borrow() + ); + }); + + println!("Press Start to exit"); + + while apt.main_loop() { + hid.scan_input(); + + if hid.keys_down().contains(KeyPad::KEY_START) { + break; + } + + gfx.flush_buffers(); + gfx.swap_buffers(); + gfx.wait_for_vblank(); + } +} From 368cf8bc0c4e79ef13ce73796f82ac84cddb33be Mon Sep 17 00:00:00 2001 From: AzureMarker Date: Sat, 5 Feb 2022 17:29:19 -0800 Subject: [PATCH 4/7] Increase stack size and add more detailed logs The default stack size is super small: 4096 bytes. This was causing the stack to overflow during normal processing. This was also not detected, so it corrupted the heap and led to odd behavior or aborts. See this long comment for more details and explanation: https://github.com/Meziu/ctru-rs/pull/42#issuecomment-1030724973 I bumped the stack size to 2MiB, which is the default for Rust's std threads. I have a few ideas to fix the underlying issues here (add a guard struct like std does for stack overflows, move 3ds-specific stuff like affinity into std but keep the impl in ctru-rs via linker hacks). Also added some more logging. --- ctru-rs/examples/futures-tokio-basic.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ctru-rs/examples/futures-tokio-basic.rs b/ctru-rs/examples/futures-tokio-basic.rs index e8e8775..cf70a85 100644 --- a/ctru-rs/examples/futures-tokio-basic.rs +++ b/ctru-rs/examples/futures-tokio-basic.rs @@ -28,16 +28,21 @@ fn main() { let runtime_thread = ctru::thread::Builder::new() .affinity(1) + .stack_size(0x200000) .spawn(move || { runtime.block_on(async move { + println!("Start of future"); let mut wake_time = tokio::time::Instant::now() + Duration::from_secs(1); + let mut iteration = 0; loop { + println!("Start of loop"); let sleep_future = tokio::time::sleep_until(wake_time); tokio::select! { _ = &mut exit_receiver => break, _ = sleep_future => { - println!("Tick"); + println!("Tick {}", iteration); + iteration += 1; wake_time += Duration::from_secs(1); } } From 282044068b38a771f32cda7839ff1aef435a9798 Mon Sep 17 00:00:00 2001 From: AzureMarker Date: Sat, 5 Feb 2022 17:38:33 -0800 Subject: [PATCH 5/7] Use the new Ps struct --- ctru-rs/examples/futures-tokio-basic.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ctru-rs/examples/futures-tokio-basic.rs b/ctru-rs/examples/futures-tokio-basic.rs index cf70a85..5260e7d 100644 --- a/ctru-rs/examples/futures-tokio-basic.rs +++ b/ctru-rs/examples/futures-tokio-basic.rs @@ -1,5 +1,6 @@ use ctru::console::Console; use ctru::services::hid::KeyPad; +use ctru::services::ps::Ps; use ctru::services::{Apt, Hid}; use ctru::Gfx; use std::time::Duration; @@ -9,11 +10,9 @@ fn main() { let gfx = Gfx::default(); let hid = Hid::init().expect("Couldn't obtain HID controller"); let apt = Apt::init().expect("Couldn't obtain APT controller"); + let _ps = Ps::init().expect("Couldn't initialize PS service"); let _console = Console::init(gfx.top_screen.borrow_mut()); - // FIXME: replace this with `Ps` when #39 merges - assert!(unsafe { ctru_sys::psInit() } >= 0); - // Give ourselves up to 30% of the system core's time apt.set_app_cpu_time_limit(30) .expect("Failed to enable system core"); From 8bf2b276cf15dad83b5ae280b83a373392088a26 Mon Sep 17 00:00:00 2001 From: AzureMarker Date: Sat, 5 Feb 2022 18:07:45 -0800 Subject: [PATCH 6/7] Turn off randomness in tokio::select when more than one future is ready --- ctru-rs/examples/futures-tokio-basic.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ctru-rs/examples/futures-tokio-basic.rs b/ctru-rs/examples/futures-tokio-basic.rs index 5260e7d..423a45f 100644 --- a/ctru-rs/examples/futures-tokio-basic.rs +++ b/ctru-rs/examples/futures-tokio-basic.rs @@ -38,12 +38,15 @@ fn main() { let sleep_future = tokio::time::sleep_until(wake_time); tokio::select! { - _ = &mut exit_receiver => break, + // Use the first available future instead of randomizing + biased; + _ = sleep_future => { println!("Tick {}", iteration); iteration += 1; wake_time += Duration::from_secs(1); } + _ = &mut exit_receiver => break, } } }); From fd9b58498d2a61ea7959b5747fdae9557b12a189 Mon Sep 17 00:00:00 2001 From: AzureMarker Date: Sat, 5 Feb 2022 22:23:00 -0800 Subject: [PATCH 7/7] IT WORKS Needs some fixes in pthread-3ds. Made the log less noisy and added some comments. --- ctru-rs/examples/futures-tokio-basic.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ctru-rs/examples/futures-tokio-basic.rs b/ctru-rs/examples/futures-tokio-basic.rs index 423a45f..60753a2 100644 --- a/ctru-rs/examples/futures-tokio-basic.rs +++ b/ctru-rs/examples/futures-tokio-basic.rs @@ -26,15 +26,15 @@ fn main() { .expect("Couldn't build runtime"); let runtime_thread = ctru::thread::Builder::new() + // Run on the system core .affinity(1) + // Use a bigger stack size. Default is 0x1000 but we'd easily overflow that. .stack_size(0x200000) .spawn(move || { runtime.block_on(async move { - println!("Start of future"); let mut wake_time = tokio::time::Instant::now() + Duration::from_secs(1); let mut iteration = 0; loop { - println!("Start of loop"); let sleep_future = tokio::time::sleep_until(wake_time); tokio::select! {