У меня есть сценарий, в котором Rust вызовет C для malloc
буфера и сохранит полученный указатель в структуру. Позднее структура будет перемещена в поток и передана функции C, которая ее мутирует.
Наивный подход к моей проблеме выглядит следующим образом ( детская площадка ):
extern crate libc;
use libc::{c_void, malloc, size_t};
use std::thread;
const INITIAL_CAPACITY: size_t = 8;
extern "C" {
fn mutate(s: *mut Storage);
}
#[repr(C)]
struct Storage {
#[allow(dead_code)]
buf: *mut c_void,
capacity: usize,
}
fn main() {
let buf = unsafe { malloc(INITIAL_CAPACITY) };
let mut s = Storage {
buf: buf,
capacity: INITIAL_CAPACITY,
};
thread::spawn(move || {
unsafe {
mutate(&mut s); // mutates s.val, maybe reallocates it, updating s.capacity if so.
}
}).join()
.unwrap();
}
Дает:
error[E0277]: the trait bound `*mut libc::c_void: std::marker::Send` is not satisfied in `[closure@src/main.rs:26:19: 30:6 s:Storage]`
--> src/main.rs:26:5
|
26 | thread::spawn(move || {
| ^^^^^^^^^^^^^ `*mut libc::c_void` cannot be sent between threads safely
|
= help: within `[closure@src/main.rs:26:19: 30:6 s:Storage]`, the trait `std::marker::Send` is not implemented for `*mut libc::c_void`
= note: required because it appears within the type `Storage`
= note: required because it appears within the type `[closure@src/main.rs:26:19: 30:6 s:Storage]`
= note: required by `std::thread::spawn`
Это способ, которым компилятор может сказать, что, поскольку *mut c_void
не реализует Send
, также не Storage
, поэтому вы не можете переместить его в замыкание потока.
Я думал, что использование указателя Unique
может решить эту проблему. Давайте попробуем ( детская площадка ):
#![feature(ptr_internals)]
extern crate libc;
use libc::{c_void, malloc, size_t};
use std::ptr::Unique;
use std::thread;
const INITIAL_CAPACITY: size_t = 8;
extern "C" {
fn mutate(s: *mut Storage);
}
#[repr(C)]
struct Storage {
#[allow(dead_code)]
buf: Unique<c_void>,
capacity: usize,
}
fn main() {
let buf = Unique::new(unsafe { malloc(INITIAL_CAPACITY) }).unwrap();
let mut s = Storage {
buf: buf,
capacity: INITIAL_CAPACITY,
};
thread::spawn(move || {
unsafe {
mutate(&mut s); // mutates s.val, maybe reallocates it, updating s.capacity if so.
}
}).join()
.unwrap();
}
Но это дает:
warning: `extern` block uses type `std::ptr::Unique<libc::c_void>` which is not FFI-safe: this struct has unspecified layout
--> src/main.rs:11:18
|
11 | fn mutate(s: *mut Storage);
| ^^^^^^^^^^^^
|
= note: #[warn(improper_ctypes)] on by default
= help: consider adding a #[repr(C)] or #[repr(transparent)] attribute to this struct
Есть ли способ, чтобы структура Storage
реализовала Send
и имела изменяемые указатели на свои экземпляры, чтобы быть FFI-безопасной?