Downcasting MyStruct: !Sync + !Send but Arc<Mutex<MyStruct>> is Send + Sync

I'm using the downcast_rs crate to do downcast from Arc<Mutex<dyn MyTrait>> to Arc<Mutex<MyConcreteObject>>

Normally when we have a non Sync + Send type like MyStruct, let's suppose here it's because it holds a pointer *mut u8, we actually can think of Arc<Mutex<MyStruct>> as being Sync + Send, and do something like this:

struct SafeMyStruct(Arc<Mutex<MyStruct>>);
unsafe impl Send for SafeMyStruct {}
unsafe impl Sync for SafeMyStruct {}

so the compiler knows it's safe, because we'll only access those pointers one at a time because they are protected by a Mutex.

Is this possible in downcast_rs? Here's what I tried:

use downcast_rs::{DowncastSync, impl_downcast};
use std::sync::{Arc, Mutex};

trait Base: DowncastSync {}
impl_downcast!(sync Base);

#[derive(Debug)]
struct Foo {
    x: *mut u8
}

//Make Arc<Mutex<Foo>>: Send + Sync
struct SafeFoo(Arc<Mutex<Foo>>);
unsafe impl Send for SafeFoo {}
unsafe impl Sync for SafeFoo {}

impl Base for Foo {}

fn main() {
    let mut x = 0;
    let base: Arc<Mutex<dyn Base>> = Arc::new(Mutex::new(Foo{x: &mut x}));
    let base_ = base.clone();
    std::thread::spawn(move ||{
        base_;
    });

}

Error:

error[E0277]: `*mut u8` cannot be shared between threads safely
  --> src/main.rs:16:6
   |
4  | trait Base: DowncastSync {}
   |             ------------ required by this bound in `Base`
...
16 | impl Base for Foo {}
   |      ^^^^ `*mut u8` cannot be shared between threads safely
   |
   = help: within `Foo`, the trait `Sync` is not implemented for `*mut u8`
   = note: required because it appears within the type `Foo`

error[E0277]: `*mut u8` cannot be sent between threads safely
  --> src/main.rs:16:6
   |
4  | trait Base: DowncastSync {}
   |             ------------ required by this bound in `Base`
...
16 | impl Base for Foo {}
   |      ^^^^ `*mut u8` cannot be sent between threads safely
   |
   = help: within `Foo`, the trait `Send` is not implemented for `*mut u8`
   = note: required because it appears within the type `Foo`

error: aborting due to 2 previous errors; 1 warning emitted

Even though Arc<Mutex<Foo>> is Send + Sync, it looks like it's forcing Foo to be Send + Sync, which would generate undefined behaviour if we implemented.

Is there a way to fix this?

No, we can't. Mutex can make Send + !Sync type into Send + Sync one, but there's nothing you can do if for some reason the type is !Send.

Shared Mutex<T> can be used as a channel between threads to send T using std::mem::replace() etc. If T: !Send, Arc<Mutex<T>> is next to useless. Use Rc<RefCell<T>> instead. If effectively disable normal mutation from multiple threads, but that's what T: !Send implies.

1 Like

I think I made a mistake. I simply meant the type being not Send neither Sync, for example

struct MyStruct{
   x: *mut u8
}

I thought this was equivalent to !Send + !Sync but apprently not.

How can I make Arc<Mutex<MyStruct>> be Send + Sync without making MyStruct be Send + Sync?

If you can make MyStruct be Send, by enforcing all necessary guarantees in every unsafe block you use and then adding unsafe impl Send, than Arc<Mutex<MyStruct>> would automaically be Send + Sync. If MyStruct has some properties which make it non-Send, then, well, it's impossible to send it in another thread, period - no matter what wrapper do you use.

It just holds pointers to some C things. I just need to access it in a lockable way. What unsafe blocks I'd do in this case? I guess in this case it should be ok to do unsafe impl Send for this struct.

I remember someone telling me that

struct SafeMyStruct(Arc<Mutex<MyStruct>>);
unsafe impl Send for SafeMyStruct {}
unsafe impl Sync for SafeMyStruct {}

is valid.

In order to implement Send, I should just assume that the pointers MyStruct holds can be called from any thread, am I right? What about Sync? Will it be implemented automatically for Arc<Mutex<MyStruct>>?

What are the requirements of underlying C api? Can it be accessed from any thread in a program? That's the "guarantees" I'm talking about.

It's not. If MyStruct is not Send, these implementations are unsound.

Yes. Mutex is Send + Sync, when the underlying value is Send. Arc inherits Send + Sync from Mutex.

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.