Search code examples
rustffi

Create *mut *mut to a struct


I'm trying to call pthread_join with a pointer to my struct in order that the C thread can fill in the struct to the memory I point it to. (Yes, I'm aware that this is highly unsafe..)

The function signature of pthread_join:

pub unsafe extern fn pthread_join(native: pthread_t,
                                  value: *mut *mut c_void)
                                  -> c_int

I'm doing this as an exercise of porting C code from a book to Rust. The C code:

pthread_t   tid1;
struct foo  *fp;
err = pthread_create(&tid1, NULL, thr_fn1, NULL);
err = pthread_join(tid1, (void *)&fp);

I came up with this code:

extern crate libc;
use libc::{pthread_t, pthread_join};

struct Foo {}

fn main() {
    let tid1:pthread_t = std::mem::uninitialized();
    let mut fp:Box<Foo> = std::mem::uninitialized();
    let value = &mut fp;
    pthread_join(tid1, &mut value);
}

But the error I see is:

error[E0308]: mismatched types
  --> src/bin/11-threads/f04-bogus-pthread-exit.rs:51:24
   |
51 |     pthread_join(tid1, &mut value);
   |                        ^^^^^^^^^^ expected *-ptr, found mutable reference
   |
   = note: expected type `*mut *mut libc::c_void`
              found type `&mut &mut std::boxed::Box<Foo>`

Is it even possible to achieve this just using casts, or do I need to transmute?


Solution

  • There are several issues here:

    • Box is a pointer to a heap-allocated resource, you can extract the pointer itself using Box::into_raw(some_box),
    • References are not silently coerced into pointers (even though they have the same representation), you need an explicit cast,
    • You need to cast from your concrete type to c_void, type inference may be able to do that
    • You have a reference to a reference to a pointer, you need a pointer to a pointer; you have one too many levels of indirection.

    Let's make it work:

    // pthread interface, reduced
    struct Void;
    
    fn sample(_: *mut *mut Void) {}
    
    // actual code
    struct Foo {}
    
    fn main() {
        let mut p = Box::into_raw(Box::new(Foo{})) as *mut Void;
        sample(&mut p as *mut _);
    }
    

    Note that this is leaking memory (as a result of into_raw), normally the memory should be shoved back into a Box with from_raw for the destructor of Foo to be called and the memory to be freed.