Skip to content

Unknown feature horizon_thread_ext and related thread errors #82

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

Closed
starptr opened this issue Dec 18, 2022 · 7 comments
Closed

Unknown feature horizon_thread_ext and related thread errors #82

starptr opened this issue Dec 18, 2022 · 7 comments

Comments

@starptr
Copy link

starptr commented Dec 18, 2022

Ran cargo 3ds run --address 192.168.1.2 --example thread-basic --features="std-threads" and there are 4 errors:

No pre-build std found, using build-std
   Compiling ctru-rs v0.7.1 (/home/ctru-rs/ctru-rs)
error[E0432]: unresolved import `std::os::horizon::thread`
 --> ctru-rs/examples/thread-basic.rs:5:23
  |
5 | use std::os::horizon::thread::BuilderExt;
  |                       ^^^^^^ could not find `thread` in `horizon`

error[E0433]: failed to resolve: could not find `thread` in `horizon`
  --> ctru-rs/examples/thread-basic.rs:16:34
   |
16 |     let prio = std::os::horizon::thread::current_priority();
   |                                  ^^^^^^ could not find `thread` in `horizon`

error[E0635]: unknown feature `horizon_thread_ext`
 --> ctru-rs/examples/thread-basic.rs:1:12
  |
1 | #![feature(horizon_thread_ext)]
  |            ^^^^^^^^^^^^^^^^^^

error[E0599]: no method named `priority` found for struct `Builder` in the current scope
  --> ctru-rs/examples/thread-basic.rs:21:14
   |
21 |             .priority(prio - 1)
   |              ^^^^^^^^ method not found in `Builder`

Any ideas why? Pointers appreciated :)

@Meziu
Copy link
Member

Meziu commented Dec 18, 2022

We are still working towards merging threading support in the Rust’s ecosystem, but there are some issues we must first resolve with the API team. As such, the feature you wanted to use isn’t available yet, but you can test it out regardless by using this branch: https://github.com/AzureMarker/rust-horizon/tree/feature/horizon-threads

I suggest not trying any long-term projects with this API in mind since it could very easily change.

@Meziu Meziu closed this as completed Dec 18, 2022
@Jasmin68k
Copy link

Jasmin68k commented Oct 19, 2024

I just stumbled upon the same problem and implemented the thread-basic example like this:

use ctru::prelude::*;

use std::time::Duration;
use std::ffi::c_void;

fn get_thread_priority(thread_handle: ctru_sys::Handle) -> i32 {
    unsafe {
        let mut prio = 0;
        let _ = ctru_sys::svcGetThreadPriority(&mut prio, thread_handle);
        prio
    }
}

extern "C" fn thread_function(arg: *mut c_void) { 
    let ix = unsafe { Box::from_raw(arg as *mut usize) }; 
    let sleep_duration = Duration::from_millis(1000 + (*ix as u64) * 250);
    let mut i = 0;

    let child_prio = get_thread_priority(ctru_sys::CUR_THREAD_HANDLE);
    println!("\nThread {} priority: {}\n", *ix, child_prio);

    loop {
        println!("Thread {} says {}", *ix, i);
        i += 1;
        std::thread::sleep(sleep_duration);
    }
}

fn main() {
    let apt = Apt::new().unwrap();
    let mut hid = Hid::new().unwrap();
    let gfx = Gfx::new().unwrap();
    let _console = Console::new(gfx.top_screen.borrow_mut());

    let main_prio = get_thread_priority(ctru_sys::CUR_THREAD_HANDLE);
    println!("Main thread priority: {}\n", main_prio);

    for ix in 0..3 {
        let ix_ptr = Box::into_raw(Box::new(ix)) as *mut c_void;

        let thread = unsafe {
            ctru_sys::threadCreate(
                Some(thread_function),
                ix_ptr,
                8 * 1024, 
                main_prio - 1,
                -2,
                true,
            )
        };

        if !thread.is_null() {
            println!("Created thread {ix}");
        } else {
            println!("Failed to create thread {ix}");
        }
    }

    println!("\x1b[29;17HPress Start to exit");

    while apt.main_loop() {
        gfx.wait_for_vblank();

        hid.scan_input();
        if hid.keys_down().contains(KeyPad::START) {
            break;
        }
    }
}

Reference:

https://rust3ds.github.io/ctru-rs/crates/ctru_sys/fn.threadCreate.html
https://rust3ds.github.io/ctru-rs/crates/ctru_sys/fn.svcGetThreadPriority.html

What's the current status of threading support in the Rust ecosystem?

@Meziu
Copy link
Member

Meziu commented Oct 20, 2024

@Jasmin68k Using the raw API exposed by ctru_sys is the only way to have threading with specific core affinity (priority can be changed at runtime within a thread spawned using std::thread::spawn). What you did is standard when developing for the platform in C, though it voids most of the safety features of Rust when it comes to threading.

Last time this was "discussed" (but the discussion itself doesn't seem to be taken further) was in this proposal for a std::thread::Builder extension trait. Please, show some support for the cause if you wish to see this change 👍

@Jasmin68k
Copy link

@Meziu I read through the discussion and also a few earlier ones it referenced.

I now have a better understanding of the intricacies involved.

Yet, I don't understand your comment.

What you're saying is, currently, I can spawn a thread with std::thread::spawn (thereby having Rust safety for thread creation), instead of ctru_sys::threadCreate or ctru_sys:svcCreateThread, and for setting its priority I can use ctru_sys:setThreadPriority?

But using ctru_sys:svcSetThreadAffinityMask won't work on a thread created with std::thread::spawn for some reason, so for setting affinity, I have to use a thread created with ctru_sys::threadCreate or ctru_sys:svcCreateThread?

How would I get the proper thread handle for ctru_sys::svcSetThreadPriority from a thread created with std::thread::spwan? I would have thought the handle impossible to get. And why wouldn't that handle work with ctru_sys::svcSetThreadAffinityMask then?

On that note, I read in rust-lang/libs-team#195 (comment) that thread affinity can only be set before spawning? Then what does ctru_sys::svcSetThreadAffinityMask do? Was the option to change affinity after creation added later (after that comment)?

Please elaborate.

@FenrirWolf
Copy link
Member

There is no way to get a thread handle with the std API, which is the reason behind the proposal for extending the thread::Builder API in the first place. As for svcSetThreadAffinityMask, iirc the function simply doesn't work and does nothing when called.

@Meziu
Copy link
Member

Meziu commented Oct 21, 2024

@Jasmin68k

What you're saying is, currently, I can spawn a thread with std::thread::spawn (thereby having Rust safety for thread creation), instead of ctru_sys::threadCreate or ctru_sys:svcCreateThread, and for setting its priority I can use ctru_sys:setThreadPriority?

Exactly. The Rust std uses the underlying pthread implementation, which we made for this platform using ctru_sys::threadCreate (have a look at https://github.com/rust3ds/pthread-3ds). This means that effectively, the Rust std uses ctru threads, and the priority of those may be changed at runtime with ctru_sys:setThreadPriority.

But using ctru_sys:svcSetThreadAffinityMask won't work on a thread created with std::thread::spawn for some reason, so for setting affinity, I have to use a thread created with ctru_sys::threadCreate or ctru_sys:svcCreateThread?

That's not a limitation of the Rust std, but one intrinsic of the operating system's scheduler. That function, to set an affinity mask, is effectively stubbed out in the 3DS' kernel, and is likely just a left over of the work made when porting HorizonOS to the Switch (which does support that API, afaik).

How would I get the proper thread handle for ctru_sys::svcSetThreadPriority from a thread created with std::thread::spwan? I would have thought the handle impossible to get.

You can look at pthread_3ds to see how we do it, but it's the same as getting it from the C library.

On that note, I read in rust-lang/libs-team#195 (comment) that thread affinity can only be set before spawning? Then what does ctru_sys::svcSetThreadAffinityMask do? Was the option to change affinity after creation added later (after that comment)?

As said above, that function simply does not work on the system.

Hope that clarifies things a bit. 👍

Edit:
To clear things up further, I must note that there exists a different pthread implementation for the 3DS (mostly back-ported from that of the Switch, and released some years after ours for the 3DS) which is bundled with devkitARM and is used by default with the C toolchain. We keep on linking our own Rust implementation for the time being since it has slightly wider support for the API.

@Jasmin68k
Copy link

Thank you, that clarifies it well :)

I should not have skimped over the pthread-3ds details ;)

It's nice to be able to use std::thread::spawn together with ctru_sys::svcSetThreadPriority, if one does not need to set affinity.

Of course, having all of this abstracted away as in the proposal would be much better even.

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

4 participants