我正在用Rust编写一个PKCS#11库,我遇到了一些问题,比如如何使调用者提供的锁定机制(如CreateMutex、DestroyMutex、LockMutex、UnlockMutex )与Rust的互斥锁实现相协调。
当然,C中的Mutexes并不依赖于数据。它们只是在一个段上设置一个锁,程序员负责创建/销毁和锁定/解锁互斥锁。然而,Rust确实将数据绑定到互斥锁本身。
我如何协调这两种情况:当调用程序提供锁定机制(不绑定到数据和手动创建/锁定/解锁/销毁)时,没有提供锁定机制并使用Rust锁定(绑定到数据和MutexGuard以及互斥锁的作用域删除/解锁)?
我是否应该使用parking_lot::Mutex和raw_lock/raw_unlock来使模式相似,例如不绑定到数据?
发布于 2021-09-28 01:11:23
这是我的解决方案;它并不美观,但它很有效,并且通过了miri测试。一般的想法是我们为该互斥锁捆绑一个Mutex<()>和一个可选的MutexGuard。我没有添加正确的错误处理或命名来匹配标准。
结构:
struct MutexContainer {
mutex: Mutex<()>,
guard: Cell<Option<MutexGuard<'static, ()>>>,
}
pub struct OpaqueMutex {
_data: [usize;0],
}请注意,OpaqueMutex只是一个不透明的句柄,以避免泄漏MutexContainer的内部,这是ffi期间的最佳实践。这种设计的关键之处在于,如果结构有一个守卫,它就不能移动。
创建互斥锁:
pub unsafe extern "C" fn create_mutex(mutex: *mut *mut OpaqueMutex) -> libc::c_ulong {
*mutex = Box::into_raw(Box::new(
MutexContainer {
mutex: Mutex::new(()),
guard: Cell::new(None),
},
))
.cast();
0
}这里没有什么特别的事情,只是泄漏了堆上的互斥锁。
互斥锁:
pub unsafe extern "C" fn lock_mutex(mutex: *mut OpaqueMutex) -> libc::c_ulong {
let container:&MutexContainer = &*mutex.cast();
let lock:MutexGuard<'static, _> = mem::transmute(container.mutex.lock().unwrap());
container.guard.set(Some(lock));
0
}在这里,我使用mem::transmute来延长锁的生命周期并存储结果。transmute是安全的,因为我们确保不移动它的父互斥锁,也不让它在这里或其他地方超过互斥锁。互斥锁将保持锁定状态,直到解除守卫。
互斥解锁:
pub unsafe extern "C" fn unlock_mutex(mutex: *mut OpaqueMutex) -> c_ulong{
let container:&MutexContainer = &*mutex.cast();
if container.mutex.try_lock().is_ok(){
return !0; //can't unlock an unlocked mutex
} else {
container.guard.set(None);
}
0
}在这里,我们使用非阻塞try_lock来确定互斥锁是否被锁定。如果try_lock成功,这意味着互斥锁被解锁,我们返回一个错误。否则,我们通过用None覆盖它来放弃保护。
互斥锁丢弃:
pub unsafe extern "C" fn drop_mutex(mutex: *mut OpaqueMutex) -> c_ulong{
unlock_mutex(mutex);
drop(Box::from_raw(mutex.cast::<MutexContainer>()));
0
}在这里,我们确保互斥锁被解锁,以防止守卫的悬空引用,然后通过重新装箱指针来回收内存。
Full playground link plus test harness.
免责声明:虽然这段代码通过了miri,但它还远远没有准备好投入生产:基本上没有错误处理。您应该确保健壮地处理错误,并进行健全性检查(如null和对齐检查),尤其是在实现加密库的情况下。
https://stackoverflow.com/questions/69350600
复制相似问题