包含原始指针的结构可以实现发送并且FFI安全吗? [英] Can a struct containing a raw pointer implement Send and be FFI safe?
问题描述
我有一个场景,Rust将调用C到malloc
缓冲区并将结果指针存储到结构中.稍后,该结构将被移至线程并传递给C函数以对其进行变异.
I have a scenario where Rust will call C to malloc
a buffer and stash the resulting pointer into a struct. Later on, the struct will be moved to a thread and passed to a C function which mutates it.
天真的解决方法看起来像这样(游乐场):
The naive approach to my problem looks like this (playground):
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
也没有实现,所以您不能将其移入线程闭包.
Which is the compiler's way of saying that because a *mut c_void
doesn't implement Send
, neither does Storage
so you can't move it into the thread closure.
我认为使用Unique
指针可能会解决此问题.让我们尝试一下(游乐场):
I thought that using a Unique
pointer might solve this. Let's try it (playground):
#![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安全的?
Is there a way to have the Storage
struct both implement Send
and have mutable pointers to its instances be FFI safe?
推荐答案
默认情况下,Rust假定*mut T
在线程之间发送不安全,这意味着包含它的结构也不安全.
By default Rust assumes *mut T
is not safe to send between threads, and this means structs containing it are not safe either.
您可以告诉Rust确实是安全的:
You can tell Rust that it is safe indeed:
unsafe impl Send for Storage {}
它完全取决于您对C如何使用此指针后面的数据的了解.实现Send
意味着,在使用此指针后面的对象时,C将不依赖于线程本地存储或特定于线程的锁(反常地,对于大多数线程不安全"的C代码而言,这都是正确的).
It relies entirely on your knowledge of how C uses data behind this pointer. Implementing Send
means C won't rely on thread-local storage or thread-specific locks when using the object behind this pointer (paradoxically, that's true for most "thread-unsafe" C code).
不需要c一次处理来自多个线程的访问-这就是Sync
的目的.
It doesn't require C to handle access from multiple threads at once — that's what Sync
is for.
这篇关于包含原始指针的结构可以实现发送并且FFI安全吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!