0

I am building safe bindings for a C library in Rust and I started facing a weird issue.

I created a struct to own the unsafe pointer to the objects returned by the library and free them safely.

This is what I have:

pub struct VipsImage {
    pub(crate) ctx: *mut bindings::VipsImage,
}

impl Drop for VipsImage {
    fn drop(&mut self) {
        unsafe {
            if !self.ctx.is_null() {
                bindings::g_object_unref(self.ctx as *mut c_void);
            }
        }
    }
}

This works fine as long as I don't share this between async calls. If I return one of this objects in an async function and use it afterwards it will be corrupted. If I used and free them in a single operation, they work as expected. How would I implement Send and Sync for a struct like that so I can safely share it between threads?

If someone wants to check the full library code, here's the link

Augusto
  • 1,234
  • 1
  • 16
  • 35
  • 4
    Your real problem is that you derive `Clone` for this type. The pointer is trivially copied, but then you have two instances of the same image that will each be dropped. Either don't make that type `Clone`, or implement `Clone` yourself properly, ie. not by making a trivial copy of the pointer. – mcarton Feb 03 '20 at 15:58
  • @mcarton I agree... But when I was doing this, I'm sure I wasn't cloning the object. I understand this is a failure and I have to remove it, but I believe it was not the cause of my issue. – Augusto Feb 03 '20 at 16:00
  • The easiest way is to wrap the pointer inside an `Arc>` – Boiethios Feb 03 '20 at 16:01
  • I might make more sense to wrap the `VipsImage` in an `Arc>`. – mcarton Feb 03 '20 at 16:05
  • @mcarton Yeah. That would solve the problem for thread safety. But as far as I understood correctly, when it comes to async it doesn't need to be thread safe because it is all in a single thread. – Augusto Feb 03 '20 at 16:24
  • 2
    It most definitely is not "all in a single thread". It *might* be in a single thread. You do need thread safety for async programming. – mcarton Feb 03 '20 at 16:31
  • 1
    FYI, I've written some async code inside GTK framework and implying raw pointers: https://gitlab.gnome.org/World/Rust/libgoa-rs/blob/c9b2d2b2b36ee4ebaede540019e4014a4e0811a6/src/simple/account.rs#L37 – Boiethios Feb 03 '20 at 16:32
  • 1
    @mcarton you can actually enforce a single threaded runtime and then rely on that to not require data to be `Send` for a particular set of tasks: https://docs.rs/tokio/0.2.11/tokio/task/struct.LocalSet.html. Here is an example using Hyper: https://github.com/hyperium/hyper/blob/master/examples/single_threaded.rs – Peter Hall Feb 03 '20 at 18:30
  • @PeterHall Sure, but that's a dynamic thing, while most of Rust's safety is statically enforced. The compiler would still have no idea that only one thread will actually be used and that most things are safe. – mcarton Feb 04 '20 at 10:15

0 Answers0