现在我有一个C的SDK库,作为习作,我希望在这个C库之上包装一个Rust库给外部,在C库中,有一个typedef void* device_t类型,有对应的device_open,device_close,device_read,device_write之类的接口,我在Rust中包装了新的结构,
struct Device {
dev: device_t,
rw_lock: Arc::Mutex<u32>,
rd_th:Arc<Mutex<Option<std::thread::JoinHandle<i32>>>>
},
struct SafePointer(*mut c_void);
unsafe impl Send for SafePointer {}
我在其实现中写了个receive函数如下:
impl Device {
pub fn start_receive(&mut self, can_cbk: NewDataCbk) -> i32 {
if !self.dev.is_null() { //监控,不互斥
if let Ok(_) = self.rw_lock.try_lock() {
match self.rd_th.lock() {
Ok(mut th) => {
let th1 = &mut *th;
match th1 {
Some(th) => {
if !th.is_finished() {
return -16;
}
//线程已经结束
},
_ => {/*线程尚未创建*/ }
}
/* 无脑地转换一下类型也绕不过去
let v : *mut u8 = unsafe { std::mem::transmute(self.dev) };
let p : device_t = unsafe { std::mem::transmute(v) };
*/
// 使用示例
let wrapped_ptr = SafePointer(std::ptr::null_mut());
//创建线程
*th1 = Some(thread::spawn(move||{
let mut buf = [0u8;1024];
//self.read(buf.as_mut_ptr(), buf.len(), 10);
let mut inner = wrapped_ptr.0;
let _ret = unsafe {
device_read(std::ptr::null_mut(), buf.as_mut_ptr(), buf.len(), 100);
};
return 0i32;
}));
},
Err(_) => { return -16;}
}
}
}
return -22;
}
其中,那个SafePointer是因为我开始直接使用的时候没能找到解决办法所以问DeepSeek他告诉我的这么包装方法,但是,用了这个方法也一样不好用,始终在线程处报错^ *mut c_void
cannot be shared between threads safely,照我现在的代码,我是要实现只有一个工作线程,问题肯定是出现在dev上,这个线程创建之后这个dev还仍然有效,但是我可以保证不操作了,大概编译器不知道这个事实,请问,我该如何绕过编译器这个死结?
1
共 7 条评论, 1 页
评论区
写评论谢谢,这个解决方案真的很神奇。我开始学习Rust一个月了,我对所有权转移和生命周期掌握得还是有很多欠缺
刚好自己又写了个闭包测试,形如
wrapped_ptr.0
这种的解构目前编译器称为partially move
部分移动。假如SafePointer是其他更复杂的类型,部分移动可以让闭包和外部分别使用不重合的两部分字段,比如:--
👇
xiaoyaou:
重新研究了一下,问题发生在这一行代码:
实际上你应该这么使用:
因为:
wrapped_ptr.0
这地方,会发生模式匹配解构,你原来的写法相当于只在闭包内使用SafePointer
的*mut c_void
类型的字段,线程闭包只会捕获使用的字段,但是字段类型*mut c_void
是!Send
的,所以会有编译器的提示。关于显式
move
:这个在你的代码示例中其实可有可无了,因为编译器会根据
let wrapped_ptr = wrapped_ptr;
自动推导出所有权的转移,不过显式加上我个人觉得可读性更好(当然是在没有影响其他变量的情况下)抱歉,昨天一早发完贴就出门了,没来得及搞格式,我刚才调整了一下,现在清晰了。 这里我新创建一个变量wrapped_ptr,用空指针,即便为这个类型实现了Send和Sync也无济于事
--
👇
xiaoyaou:
按你的需求,只在单线程工作,那你要的应该是使用
move
关键字把你说的dev
的所有权转移到线程闭包里,才是最根本的操作。 但是你贴出来的代码乱糟糟的,我尝试格式复原也看不懂你具体需求和问题在哪,按照你给定编译器提示信息,我猜你需要的是,把*mut c_coid
类型(或其包装类型)的所有权移动到线程内,而不是在线程里共享其引用我试了,无济于事
--
👇
Bai-Jinlin: 虽然你这问题大概加上一句unsafe impl Sync for SafePointer {}就可以解决,但是看了你的代码我觉得你可能控制不好这种东西。
按你的需求,只在单线程工作,那你要的应该是使用
move
关键字把你说的dev
的所有权转移到线程闭包里,才是最根本的操作。 但是你贴出来的代码乱糟糟的,我尝试格式复原也看不懂你具体需求和问题在哪,按照你给定编译器提示信息,我猜你需要的是,把*mut c_coid
类型(或其包装类型)的所有权移动到线程内,而不是在线程里共享其引用虽然你这问题大概加上一句unsafe impl Sync for SafePointer {}就可以解决,但是看了你的代码我觉得你可能控制不好这种东西。