Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't I send Mutex<*mut c_void> between threads?

Tags:

rust

I need to share an object created from C++ between Rust threads. I have wrapped it in a Mutex construct, so now it is safe to be sent between threads. However the compiler won't let me do what.

error[E0277]: `*mut std::ffi::c_void` cannot be sent between threads safely
   --> sendsync.rs:14:2
    |
14  |     thread::spawn(move || {
    |     ^^^^^^^^^^^^^ `*mut std::ffi::c_void` cannot be sent between threads safely
    |
    = help: within `Api`, the trait `std::marker::Send` is not implemented for `*mut std::ffi::c_void`
    = note: required because it appears within the type `OpaqWrapper`
    = note: required because it appears within the type `Api`
    = note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Mutex<Api>`
    = note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Arc<std::sync::Mutex<Api>>`
    = note: required because it appears within the type `[[email protected]:14:16: 19:3 safe_obj:std::sync::Arc<std::sync::Mutex<Api>>]`

How should I implement this according to Rust rules? Here is the code:

use std::{
    sync::{
        Arc,Mutex,
    },
    ptr,
    thread::{self},
};
pub struct OpaqWrapper {
    pub obj_ptr: *mut ::std::os::raw::c_void,
}
pub struct Api(OpaqWrapper);

fn spawn_api_thread(safe_obj: Arc<Mutex<Api>>) {
    thread::spawn(move || {
        {
            let my_api = safe_obj.lock().unwrap();
            // my_api.whatever();
        }
    });
}

fn main() {
    let api = Api(
        OpaqWrapper {
            obj_ptr: ptr::null_mut(),
        }
    );
    let shared_api= Arc::new(Mutex::new(api));
    for _ in 0..10 {
        spawn_api_thread(shared_api.clone());
    }
}
like image 275
Nulik Avatar asked Feb 19 '20 04:02

Nulik


People also ask

Is Lazy_static thread safe?

lazy_static and once_cell serve the same purpose. The error isn't crate-specific. Unless the title is wrong, once_cell::sync::Lazy is not threadsafe, and lazy_static definitely is. In addition, that same title suggests mutability because thread safety is not an issue if there are only readers.

Is mutex send?

Mutex is in fact a “ Sync -maker”. It makes a Sync wrapper of a sendable type that is otherwise not Sync, like for example Sender (sender end of a channel).

Why is RC not send?

If you need mutability, put a Cell or RefCell inside the Rc ; see an example of mutability inside an Rc . Rc uses non-atomic reference counting. This means that overhead is very low, but an Rc cannot be sent between threads, and consequently Rc does not implement Send .


1 Answers

Short version

Passing something to a thread requires that it be Send.

T:Send => Mutex<T>:Send+Sync =>  Arc<Mutex<T>>:Send

so it is enough to mark Api as Send

Long version

Passing something to a thread requires that it be Send.

Arc<T> only automatically gets Send (and Sync) if T is Send and Sync. The source contains something like this:

unsafe impl<T: ?Sized + Sync + Send> Send for Arc<T> {}
unsafe impl<T: ?Sized + Sync + Send> Sync for Arc<T> {}

However, Mutex only requires Send for it to be both Sync and Send, its code contains:

unsafe impl<T: ?Sized + Send> Send for Mutex<T> { }
unsafe impl<T: ?Sized + Send> Sync for Mutex<T> { }

This means for Arc<Mutex<Api>> to be Sync, you need Mutex<Api> to be Sync+Send which will happen if Api is Send. To get this you need to mark either Api or OpaqWrapper as Send.

unsafe impl Send for Api {}

Note you dont need to mark them as Sync as the Mutex gets that automatically.

like image 124
Michael Anderson Avatar answered Nov 15 '22 10:11

Michael Anderson