为结构创建* mut * mut

时间:2017-02-14 20:52:34

标签: rust ffi

我试图用指向我结构的指针调用pthread_join,以便C线程可以将结构填充到我指向它的内存中。 (是的,我知道这是非常不安全的。)

pthread_join的功能签名:

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

我这样做是为了将一本书中的C代码移植到Rust。 C代码:

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

我想出了这段代码:

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);
}

但我看到的错误是:

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>`

甚至可以使用演员表来实现这一点,还是我需要转换?

2 个答案:

答案 0 :(得分:4)

代码不能像写的那样工作;那是因为C线程并没有真正“填充你指向的内存中的结构”。它负责分配自己的内存(或事先从另一个线程接收它)并填写它。 C线程“返回”的唯一内容是单个地址,此地址由pthread_join选取。

这就是pthread_join收到void **的原因,即指向void *的指针。这种输出参数使pthread_join能够存储(返回)由新完成的线程提供的void *指针。线程可以通过将指针传递给pthread_exit或从传递给start_routine的{​​{1}}返回指针来提供指针。在Rust中,可以使用以下代码接收原始指针:

pthread_create

返回指针指向的内存的内容和大小是要连接的线程和连接它的线程之间的契约问题。如果工作线程在C中实现并且被设计为由其他C代码调用,那么显而易见的选择是为结果结构分配内存,填充它并提供指向已分配内存的指针。例如:

let mut c_result: *mut libc::c_void = ptr::null_mut();
libc::pthread_join(tid1, &mut c_result as *mut _);
// C_RESULT now contains the raw pointer returned by the worker's
// start routine, or passed to pthread_exit()

在这种情况下,加入线程的Rust代码可以通过复制C结构并获取其所有权来解释结果:

struct ThreadResult { ... };

...
ThreadResult *result = malloc(sizeof(struct ThreadResult));
result->field1 = value1;
...
pthread_exit(result);

答案 1 :(得分:3)

这里有几个问题:

  • Box是指向堆分配资源的指针,您可以使用Box::into_raw(some_box)
  • 提取指针本身
  • 引用不会被默默地强制转换为指针(即使它们具有相同的表示),您需要一个显式的强制转换,
  • 您需要从具体类型转换为c_void,类型推断可能会这样做
  • 你有一个对指针的引用的引用,你需要一个指向指针的指针;你有一个太多的间接层。

让它成功:

// 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 _);
}

请注意,这是内存泄漏(into_raw的结果),通常应将内存推回Boxfrom_rawFoo的析构函数被召唤和被释放的记忆。