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

Error: *const u8 cannot be sent between threads safely #261

Open
mwitz8 opened this issue May 7, 2021 · 4 comments
Open

Error: *const u8 cannot be sent between threads safely #261

mwitz8 opened this issue May 7, 2021 · 4 comments

Comments

@mwitz8
Copy link

mwitz8 commented May 7, 2021

I'm new to Rust and Legion, and I'm trying to get my solve_sudoku system running on a schedule. This is my code.

use legion::*;
use std::time::{Instant};

fn main() {
    let entities_to_add = parse_data();
    let mut world = World::default();
    let mut resources = Resources::default();
    let entities: &[Entity] = world.extend(entities_to_add);
    let mut schedule = Schedule::builder()
        .add_system(solve_sudoku_system())
        .build();
    schedule.execute(&mut world, &mut resources);
}

#[derive(Clone, Copy, Debug, PartialEq)]
struct Sudoku {
    matrix: [[char; 9]; 9],
    solution: Option<[[char; 9]; 9]>,
}

#[system(for_each)]
fn solve_sudoku(sudoku: &mut Sudoku, #[resource] res: &mut Resources) {
}

fn parse_data() -> Vec<(Sudoku,)> {
    ...
}

But I'm getting the error message:

   Compiling rust v0.1.0 (C:\Users\me:)\...)     
error[E0277]: `*const u8` cannot be sent between threads safely
  --> src\main.rs:15:10
   |
15 |         .add_system(solve_sudoku_system())
   |          ^^^^^^^^^^ `*const u8` cannot be sent between threads safely
   |
   = help: within `legion::Resources`, the trait `Send` is not implemented for `*const u8`
   = note: required because it appears within the type `PhantomData<*const u8>`
   = note: required because it appears within the type `legion::Resources`
   = note: required because of the requirements on the impl of `Send` for `legion::Write<legion::Resources>`
   = note: required because of the requirements on the impl of `Send` for `legion::internals::systems::system::ResourceMarker<legion::Write<legion::Resources>>`
   = note: required because it appears within the type `legion::systems::System<legion::Write<legion::Resources>, Query<legion::Write<Sudoku>, EntityFilterTuple<ComponentFilter<Sudoku>, Passthrough>>, [closure@src\main.rs:26:1: 26:20]>`
   = note: required because it appears within the type `impl Runnable`
   = note: required because of the requirements on the impl of `ParallelRunnable` for `impl Runnable`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0277`.      
error: could not compile `rust`

To learn more, run the command again with --verbose.

I can't for the life of me find where it's even getting const u8 from -- I don't have any u8's in my code at all. Could you guys help me figure out what I'm missing?

@zedrian
Copy link

zedrian commented May 7, 2021

The compiler is talking about the PhantomData<*const u8> field that is stored in legion::Resources. As I can see, that was made to prevent the Resources object itself from implementing Send and Sync - core Rust traits that are responsible for multithreaded access to some data (you can read more about them here: https://doc.rust-lang.org/nomicon/send-and-sync.html). Note that having the Resources object itself not thread-safe does not mean that resources actually stored there can't be used across multiple threads (it's quite opposite actually, but is done by the internal machinery of legion).

The cause of the error is that you are trying to access the resources from your system in an incorrect way - instead of trying to pass the whole legion::Resources object to the system, you should explicitly specify in the signature of the system function which exactly resources are needed by the system and whether you need them mutable or not (specified for each resource separately). Let me show you:

#[system]
fn update_logic(#[resource] dt: &DeltaTime, #[resource] logic_timer: &mut LogicUpdateTimer) {
    logic_timer.update(dt);
    // do some logic update stuff...
}

#{system]
fn update_physics(#[resource] dt: &DeltaTime, #[resource] physics_timer: &mut PhysicsUpdateTimer) {
    physics_timer.update(dt);
    // do some physics update stuff...
}

let mut resources = Resources::default();
resources.insert(DeltaTime::default());
resources.insert(LogicUpdateTimer::default());
resources.insert(PhysicsUpdateTimer::default());

let mut schedule = Schedule::builder()
    .add_system(update_logic_system())
    .add_system(update_physics_system())
    .build();

schedule.execute(&mut world, &mut resources);

In the example above, both systems request read access to the same resource (DeltaTime), and a write access to different resources (LogicUpdateTimer and PhysicsUpdateTimer respectively), and legion is smart enough to execute these two systems potentially in parallel since they write to different resources, and the shared resource is only accessed by reading. This trick won't be possible if all systems accepted &mut Resources.

In your case, it's not yet clear to me how exactly to modify the signature of your solve_sudoku() function since I can't see which exact resources it should access (should it?). If it does not require any resources for execution, then you can leave sudoku: &mut Sudoku as the only argument.

@mwitz8
Copy link
Author

mwitz8 commented May 7, 2021

Thanks a ton Zedrian. That solved my issue. But what if I have multiple variables of the same type that I want to access in my systems? For example, if I have two u8's, and I insert them both into the same legion::Resources, won't the second one I insert replace the first one? How could I make them both accessible in the same schedule and system?

@zedrian
Copy link

zedrian commented May 7, 2021

If you have several resources of the same type, but of different usage, you can differ them by creating new wrapper types.

Let's assume that in the code snippet that I showed above, both systems are using their own timers that originally were objects of the same type Timer. This can be solved by wrapper types:

struct LogicUpdateTimer(pub Timer);
struct PhysicsUpdateTimer(pub Timer);

// so instead of this:
resources.insert(Timer::new());
resources.insert(Timer::new()); // that is not okay, you cannot have two resources of the same type

// you can have this:
resources.insert(LogicUpdateTimer(Timer::new());
resources.insert(PhysicsUpdateTimer(Timer::new()); // that is okay, new resource has a different type

In this example, I made Timer a public field, so you can work with it like logic_update_timer.0.do_stuff(). As for me, it's the most simple way, but not the only one - you can also make the field private and add corresponding methods to the wrapper types, or implement Deref and DerefMut traits for them and use implicit Deref coercions like you are working with Timer objects directly (see https://doc.rust-lang.org/book/ch15-02-deref.html)...

If you have several resources of the same type and of the same usage, then you should check if something is wrong with the architecture of the solution in general.

@Itsnotdone
Copy link

@zedrian
It's possible to access Resources in Systems?

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

3 participants