请问怎么能在铁锈里做一个安全的静电单例?

你怎么能在铁锈里做一个安全的静电单例?

这是一个有争议的话题,所以让我先解释一下我的用例,然后谈谈实际的问题。

我发现对于一堆不安全的事情,确保不泄漏内存是很重要的;如果您开始使用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 instancestatic 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 existunsafe 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));
    }}

特别要注意的是,不同于普通程序,您可以确定入口点(Main)总是在单个任务中运行,RUST中的测试运行程序不提供任何这样的单一入口点。

显然,除了指定任务的最大数量之外,如果有几十个测试,那么只需要做几个这样的事情,并且只在这一种情况下将测试任务池限制为一个是缓慢和毫无意义的。



HUX布斯
浏览 416回答 2
2回答

紫衣仙女

它看起来像用例std::sync::Once:use&nbsp;std::sync::{Once,&nbsp;ONCE_INIT};static&nbsp;INIT:&nbsp;Once&nbsp;=&nbsp;ONCE_INIT;然后在你的测试中调用INIT.doit(||&nbsp;unsafe&nbsp;{&nbsp;init();&nbsp;});Once保证你init将只执行一次,不管您调用了多少次。INIT.doit().

尚方宝剑之说

另见懒静这使得事情更符合人体工程学。它所做的与静态的事情本质上是一样的。Once对于每个变量,但将其包装为实现Deref这样您就可以像普通引用一样访问它。用法如下(从文件中):#[macro_use]extern&nbsp;crate&nbsp;lazy_static;use&nbsp;std::collections::HashMap;lazy_static!&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;static&nbsp;ref&nbsp;HASHMAP:&nbsp;HashMap<u32,&nbsp;&'static&nbsp;str>&nbsp;=&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;let&nbsp;mut&nbsp;m&nbsp;=&nbsp;HashMap::new(); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m.insert(0,&nbsp;"foo"); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m.insert(1,&nbsp;"bar"); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m.insert(2,&nbsp;"baz"); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m&nbsp;&nbsp;&nbsp;&nbsp;}; &nbsp;&nbsp;&nbsp;&nbsp;static&nbsp;ref&nbsp;COUNT:&nbsp;usize&nbsp;=&nbsp;HASHMAP.len(); &nbsp;&nbsp;&nbsp;&nbsp;static&nbsp;ref&nbsp;NUMBER:&nbsp;u32&nbsp;=&nbsp;times_two(21);}fn&nbsp;times_two(n:&nbsp;u32)&nbsp;->&nbsp;u32&nbsp;{&nbsp;n&nbsp;*&nbsp;2&nbsp;}fn&nbsp;main()&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;println!("The&nbsp;map&nbsp;has&nbsp;{}&nbsp;entries.",&nbsp;*COUNT); &nbsp;&nbsp;&nbsp;&nbsp;println!("The&nbsp;entry&nbsp;for&nbsp;`0`&nbsp;is&nbsp;\"{}\".",&nbsp;HASHMAP.get(&0).unwrap()); &nbsp;&nbsp;&nbsp;&nbsp;println!("A&nbsp;expensive&nbsp;calculation&nbsp;on&nbsp;a&nbsp;static&nbsp;results&nbsp;in:&nbsp;{}.",&nbsp;*NUMBER);}请注意,autoderef意味着您甚至不必使用*每当您对静态变量调用方法时。变量将在第一次初始化它的Deref德。但是,惰性_静态变量是不可变的(因为它们在引用后面)。如果您想要一个可变的静态,则需要使用Mutex:lazy_static!&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;static&nbsp;ref&nbsp;VALUE:&nbsp;Mutex<u64>;}impl&nbsp;Drop&nbsp;for&nbsp;IsFoo&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;fn&nbsp;drop(&mut&nbsp;self)&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;let&nbsp;mut&nbsp;value&nbsp;=&nbsp;VALUE.lock().unwrap(); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*value&nbsp;+=&nbsp;1; &nbsp;&nbsp;&nbsp;&nbsp;}}#[test]fn&nbsp;test_drops_actually_work()&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Have&nbsp;to&nbsp;drop&nbsp;the&nbsp;mutex&nbsp;guard&nbsp;to&nbsp;unlock,&nbsp;so&nbsp;we&nbsp;put&nbsp;it&nbsp;in&nbsp;its&nbsp;own&nbsp;scope &nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*VALUE.lock().unwrap()&nbsp;=&nbsp;0; &nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;let&nbsp;c&nbsp;=&nbsp;CBox; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;c.set(IsFoo); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;c.set(IsFoo); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;c.poll(/*...*/); &nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;assert!(*VALUE.lock().unwrap()&nbsp;==&nbsp;2);&nbsp;//&nbsp;Assert&nbsp;that&nbsp;all&nbsp;expected&nbsp;drops&nbsp;were&nbsp;invoked}
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Python