Is there an Arc type that can be moved (not shared) between threads?

I have the problem of polling an Arc. As you see, the Arc is actually transfered to C. I do not use it anywhere after it is transfered to C. Since C must be Sync+Send, I cannot use a more simple container, so I use Arc:

use std::sync::Arc;
use core::task::{Context, Poll};
use std::pin::Pin;
use tokio::io::{AsyncRead};

trait A{
    
}

impl AsyncRead for A {
    fn poll_read(
        self: Pin<&mut Self>,
        cx: &mut Context<'_>,
        buf: &mut tokio::io::ReadBuf<'_>
    ) -> Poll<std::io::Result<()>> {
        Poll::Ready(Ok(()))
    }
}

pub struct C{
    c: Arc<dyn A + Send + Sync >
}

impl C {
    //Here we actually transfer/move our Arc<dyn A + Send + Sync >, 
    //we don't use it anymore on the other thread
    pub fn new(c: Arc<dyn A + Send + Sync >) -> C {
        C {
            c: c
        }
    }
}

impl AsyncRead for C {
    fn poll_read(
        self: Pin<&mut Self>,
        cx: &mut Context<'_>,
        buf: &mut tokio::io::ReadBuf<'_>
    ) -> Poll<std::io::Result<()>> {
        //since `c` is `Arc`, it has no interior mutability, so I cannot call `poll_read` on it
        self.c.poll_read(cx, buf)
    }
}

Playground

however, by using Arc I cannot do self.c.poll_read(cx, buf) because Arc does not provide interior mutability. In this case, however, I use Arc not for sharing between threads, but actually for sending C to other threads. If I simply did Arc<Mutex<dyn A + Send + Sync>> then it would work and I could poll with self.c.lock().unwrap()poll_read(cx, buf). Since C is the only owner, it would block immediately, so no problem using inside poll_read, but is there a more elegant way of solving this problem?

Is there a type that can be sent to other threads in such a way that it is moved to that thread in such a way that it preserves interior mutability so I can poll it?

Can't you just move the mutex itself, then?

maybe? But I'd have to lock it at every call, wouldn't I? Not that this is a problem since there's only one owner, but isn't there something better than a Mutex? Something with Interior mutability + Send, without lock

Well, RefCell<T> is Send iff T: Send and it has interior mutability. If your inner type is not Send, then I don't see how you could make it so without locking.

If there's only one owner, why are you using an Arc? You should only use Arc if it is shared.

I'm trying with Mutex just to see what happens but I get

error[E0599]: no method named `poll_read` found for struct `std::sync::MutexGuard<'_, (dyn AsyncRW + Sync + std::marker::Send + 'static)>` in the current scope
  --> src/async_transporter.rs:80:37
   |
80 |         self.stream.lock().unwrap().poll_read(cx, buf)
   |                                     ^^^^^^^^^ method not found in `std::sync::MutexGuard<'_, (dyn AsyncRW + Sync + std::marker::Send + 'static)>`

which made me wonder. How do I know if I can call poll_read through MutexGuard? I looked at MutexGuard in std::sync - Rust but couldn't find anything.

Also, is the error above possibly happening because I should pin things? How can I pin the result of the mutex lock?

in my mind Rc wouldn't work because this type is polled and polling = transfering between threads. I don't know, not sure

It dereferences to the value that the mutex is protecting. You can call any method on it if it is present on the inner type.

The error regarding calling poll_read is because you need a pinned reference to call it.

2 Likes
    fn poll_shutdown(
        self: Pin<&mut Self>,
        cx: &mut Context<'_>
    ) -> Poll<Result<(), std::io::Error>>

        Pin::new(*self.stream.lock().unwrap()).poll_shutdown(cx)
    }

something like this? I think I need to deference, otherwise I'm simply doing Pin<MutexGuard>, not Pin<&mut Self>


impl<T: ?Sized> DerefMut for MutexGuard<'_, T> {
    fn deref_mut(&mut self) -> &mut T {
        unsafe { &mut *self.lock.data.get() }
    }
}

I think self.lock.data.get() gets a pointer to the data. * before it acesses the value, and &mut returns a reference. So dereferencing returns a reference to the inner type T?

Also, doesn't *self.lock.data.get() create a copy? Or &mut before it means "address of object at *self.lock().data.get()"?

forgot the error:


error[E0277]: the size for values of type `dyn AsyncRW + Sync + std::marker::Send` cannot be known at compilation time
   --> src/async_transporter.rs:105:9
    |
105 |         Pin::new(*self.stream.lock().unwrap()).poll_shutdown(cx)
    |         ^^^^^^^^ doesn't have a size known at compile-time
    |
    = help: the trait `Sized` is not implemented for `dyn AsyncRW + Sync + std::marker::Send`
    = note: all function arguments must have a statically known size

Given your situation it looks like you should be using Box instead of Arc:

pub struct C{
-   c: Arc<dyn A + Send + Sync >
+   c: Box<dyn A + Send + Sync >
}

impl C {
    //Here we actually transfer/move our Arc<dyn A + Send + Sync >, 
    //we don't use it anymore on the other thread
-   pub fn new(c: Arc<dyn A + Send + Sync >) -> C {
+   pub fn new(c: Box<dyn A + Send + Sync >) -> C {
1 Like

Yes.

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.