`unsafe impl Send` not working on raw pointer

I want to pass an SDL window's GL context to another thread and then make it current and use it exclusively on that thread, which means that I need to send the GL context, which is a raw pointer, to another thread permanently, and additionally, making a context current using SDL requires a reference to the window, so I also need to temporarily send (so, share) a raw pointer of the window to the other thread (and the lock on a mutex on the main thread till that one operation is finished and I can use the window on the main thread again). I've done my research and I'm fairly certain this will work on Linux, and I'm willing to do what I need to to make this safe, so I decided to use the standard wrapper struct pattern:

struct ShareablePtr<T>(*mut T);
unsafe impl<T> Sync for ShareablePtr<T> {}
unsafe impl<T> Send for ShareablePtr<T> {}

and wrote my code:

let using_window = Mutex::new(());
let shareable_window = ShareablePtr(window.raw());
let shareable_gl_context;
unsafe {
    shareable_gl_context = ShareablePtr(_gl_context.raw());
}

std::thread::spawn(move ||
    {
        using_window.lock();
        unsafe {
            let window = shareable_window.0;
            let gl_context = shareable_gl_context.0;
            sdl2::sys::SDL_GL_MakeCurrent(
                window as *mut sdl2::sys::SDL_Window,
                gl_context as *mut std::ffi::c_void,
            );
        }
    }
    // ... use gl context
});

using_window.lock();

However, while this works for another pointer I'm sending this way, it doesn't work at all for the window and gl context pointers:

  Compiling embryo v0.1.0 (/var/home/alexispurslane/Development/embryo-engine)
error[E0277]: `*mut SDL_Window` cannot be sent between threads safely
   --> src/main.rs:232:24
    |
232 |       std::thread::spawn(move || {
    |       ------------------ ^------
    |       |                  |
    |  _____|__________________within this `{closure@src/main.rs:232:24: 232:31}`
    | |     |
    | |     required by a bound introduced by this call
233 | |         {
234 | |             safe_to_continue.lock();
235 | |             unsafe {
...   |
246 | |         renderer_state.render_loop(render_state_dead_drop, event_sender, running);
247 | |     });
    | |_____^ `*mut SDL_Window` cannot be sent between threads safely
    |
    = help: within `{closure@src/main.rs:232:24: 232:31}`, the trait `Send` is not implemented for `*mut SDL_Window`, which is required by `{closure@src/main.rs:232:24: 232:31}: Send`
note: required because it's used within this closure
   --> src/main.rs:232:24
    |
232 |     std::thread::spawn(move || {
    |                        ^^^^^^^
note: required by a bound in `std::thread::spawn`
   --> /var/home/alexispurslane/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/thread/mod.rs:681:8
    |
678 | pub fn spawn<F, T>(f: F) -> JoinHandle<T>
    |        ----- required by a bound in this function
...
681 |     F: Send + 'static,
    |        ^^^^ required by this bound in `spawn`

error[E0277]: `*mut c_void` cannot be sent between threads safely
   --> src/main.rs:232:24
    |
232 |       std::thread::spawn(move || {
    |       ------------------ ^------
    |       |                  |
    |  _____|__________________within this `{closure@src/main.rs:232:24: 232:31}`
    | |     |
    | |     required by a bound introduced by this call
233 | |         {
234 | |             safe_to_continue.lock();
235 | |             unsafe {
...   |
246 | |         renderer_state.render_loop(render_state_dead_drop, event_sender, running);
247 | |     });
    | |_____^ `*mut c_void` cannot be sent between threads safely
    |
    = help: within `{closure@src/main.rs:232:24: 232:31}`, the trait `Send` is not implemented for `*mut c_void`, which is required by `{closure@src/main.rs:232:24: 232:31}: Send`
note: required because it's used within this closure
   --> src/main.rs:232:24
    |
232 |     std::thread::spawn(move || {
    |                        ^^^^^^^
note: required by a bound in `std::thread::spawn`
   --> /var/home/alexispurslane/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/thread/mod.rs:681:8
    |
678 | pub fn spawn<F, T>(f: F) -> JoinHandle<T>
    |        ----- required by a bound in this function
...
681 |     F: Send + 'static,
    |        ^^^^ required by this bound in `spawn`

For more information about this error, try `rustc --explain E0277`.
error: could not compile `embryo` (bin "embryo") due to 2 previous errors

What am I missing? Apologies if it's something stupid, I've been using Rust for awhile now but I'm very tired today.

We'd need to sure your full code, but the error message suggests that you're not, in fact, using ShareablePtr, but actually using the raw pointer directly.

This kind of syntax causes Rust to destructure the ShareablePtr and only move the raw pointer into the closure. To move the ShareablePtr struct instead, you have to do something like this:

let window = shareable_window;
let window = window.0;
1 Like

Which is a consequence of the disjoint capture in closures introduced in the 2021 edition:

This feature adjusts the rules for closure captures starting in Rust 2021. In Rust 2018 and before, closures always captured entire local variables. Under these new rules, they capture more precise paths. These paths are always some prefix of the precise paths that are references within the closure body.

As a simple example, in Rust 2018, || println!("{}", a.y) captures a reference to a and not just a.y. Capturing a in its entirety prevents mutation or moves from other fields of a, so that code like this does not compile:

let a = SomeStruct::new();
drop(a.x); // Move out of one field of the struct
println!("{}", a.y); // Ok: Still use another field of the struct
let c = || println!("{}", a.y); // Error: Tries to capture all of `a`
c();
1 Like

that's why I'm lost, because this is copied pretty directly from my codebase:

OHH!!! Thank you!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.