为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

HUX布斯 2019-08-03 11:03:46
你怎么能在铁锈里做一个安全的静电单例?这是一个有争议的话题,所以让我先解释一下我的用例,然后谈谈实际的问题。我发现对于一堆不安全的事情,确保不泄漏内存是很重要的;如果您开始使用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中的测试运行程序不提供任何这样的单一入口点。显然,除了指定任务的最大数量之外,如果有几十个测试,那么只需要做几个这样的事情,并且只在这一种情况下将测试任务池限制为一个是缓慢和毫无意义的。
查看完整描述

2 回答

?
紫衣仙女

TA贡献1839条经验 获得超15个赞

它看起来像用例std::sync::Once:

use std::sync::{Once, ONCE_INIT};static INIT: Once = ONCE_INIT;

然后在你的测试中调用

INIT.doit(|| unsafe { init(); });

Once保证你init将只执行一次,不管您调用了多少次。INIT.doit().


查看完整回答
反对 回复 2019-08-05
?
尚方宝剑之说

TA贡献1788条经验 获得超4个赞


另见懒静这使得事情更符合人体工程学。它所做的与静态的事情本质上是一样的。Once对于每个变量,但将其包装为实现Deref这样您就可以像普通引用一样访问它。

用法如下(从文件中):

#[macro_use]extern crate lazy_static;use std::collections::HashMap;lazy_static! {
    static ref HASHMAP: HashMap<u32, &'static str> = {
        let mut m = HashMap::new();
        m.insert(0, "foo");
        m.insert(1, "bar");
        m.insert(2, "baz");
        m    };
    static ref COUNT: usize = HASHMAP.len();
    static ref NUMBER: u32 = times_two(21);}fn times_two(n: u32) -> u32 { n * 2 }fn main() {
    println!("The map has {} entries.", *COUNT);
    println!("The entry for `0` is \"{}\".", HASHMAP.get(&0).unwrap());
    println!("A expensive calculation on a static results in: {}.", *NUMBER);}

请注意,autoderef意味着您甚至不必使用*每当您对静态变量调用方法时。变量将在第一次初始化它的Deref德。

但是,惰性_静态变量是不可变的(因为它们在引用后面)。如果您想要一个可变的静态,则需要使用Mutex:

lazy_static! {
    static ref VALUE: Mutex<u64>;}impl Drop for IsFoo {
    fn drop(&mut self) {
        let mut value = VALUE.lock().unwrap();
        *value += 1;
    }}#[test]fn test_drops_actually_work() {
    // Have to drop the mutex guard to unlock, so we put it in its own scope
    {
        *VALUE.lock().unwrap() = 0;
    }
    {
        let c = CBox;
        c.set(IsFoo);
        c.set(IsFoo);
        c.poll(/*...*/);
    }
    assert!(*VALUE.lock().unwrap() == 2); // Assert that all expected drops were invoked}




查看完整回答
反对 回复 2019-08-05
  • 2 回答
  • 0 关注
  • 415 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信