这是一个有争议的话题的东西,所以让我解释我的使用情况下启动,然后谈谈实际问题。
我发现了一堆不安全的事情,一定要确保你没有内存泄漏是非常重要的; 其实这是很容易做到,如果你开始使用transmute()
和forget()
例如,传递一个装箱实例为C代码的时间的任意的量,然后取出它背出,并通过使用“复活它transmute
。
想象一下,我对这种API的安全包装:
trait Foo {}
struct CBox;
impl CBox {
/// Stores value in a bound C api, forget(value)
fn set<T: Foo>(value: T) {
// ...
}
/// Periodically call this and maybe get a callback invoked
fn poll(_: Box<Fn<(EventType, Foo), ()> + Send>) {
// ...
}
}
impl Drop for CBox {
fn drop(&mut self) {
// Safely load all saved Foo's here and discard them, preventing memory leaks
}
}
为了测试这个实际上没有任何泄漏的内存,我想这样的一些测试:
#[cfg(test)]
mod test {
struct IsFoo;
impl Foo for IsFoo {}
impl Drop for IsFoo {
fn drop(&mut self) {
Static::touch();
}
}
#[test]
fn test_drops_actually_work() {
guard = Static::lock(); // Prevent any other use of Static concurrently
Static::reset(); // Set to zero
{
let c = CBox;
c.set(IsFoo);
c.set(IsFoo);
c.poll(/*...*/);
}
assert!(Static::get() == 2); // Assert that all expected drops were invoked
guard.release();
}
}
你怎么能创建这种类型的静态单一对象的?
它必须使用Semaphore
风格防护锁,以确保多个测试不同时运行,然后不安全访问某种静态的可变值。
我想也许这个实现会工作 ,但实际而言,因为偶尔的竞争条件导致的重复执行失败init
:
/// Global instance
static mut INSTANCE_LOCK: bool = false;
static mut INSTANCE: *mut StaticUtils = 0 as *mut StaticUtils;
static mut WRITE_LOCK: *mut Semaphore = 0 as *mut Semaphore;
static mut LOCK: *mut Semaphore = 0 as *mut Semaphore;
/// Generate instances if they don't exist
unsafe fn init() {
if !INSTANCE_LOCK {
INSTANCE_LOCK = true;
INSTANCE = transmute(box StaticUtils::new());
WRITE_LOCK = transmute(box Semaphore::new(1));
LOCK = transmute(box Semaphore::new(1));
}
}
特别要注意不像一个正常的程序,你可以肯定,你的入口点(主)在一个任务中总是处于运行状态,在拉斯特测试运行不提供任何类型的单一入口点是这样的。
其他,显然,不是指定的最大任务数; 给几十个测试,只有极少数需要做这样的事情,这是缓慢的,毫无意义的测试任务池限制在一个只为这一个情况。