Wasm出现恐慌后,如何触发Rust Mutex的释放,以便以后的通话可以进行?

问题描述 投票:1回答:1

我在使用Rust和WebAssembly进行开发时遇到了死锁。

由于使用了一些全局访问的变量,我选择了lazy_static和一个Mutex(使用thread_local回调会导致嵌套问题)。我已经通过#[wasm_bindgen]声明了很多Rust函数被JavaScript使用。他们读取和写入lazy_static变量。

其中一种功能出现紧急情况后,互斥锁无法释放,如果其他功能需要使用相同的互斥锁,则会导致紧急情况。

我知道恐慌问题是出乎意料的,需要解决,但是这些功能彼此相对独立。尽管lazy_static变量的读取和写入相交,但某些错误可能不一定会影响其他部分。

在Wasm出现紧急情况之后,如何触发Mutex释放以允许其他呼叫正常?对于这种问题,是否有更好的做法?

铁锈:

use std::sync::Mutex;
use std::sync::PoisonError;
use wasm_bindgen::prelude::*;

pub struct CurrentStatus {
    pub index: i32,
}

impl CurrentStatus {
    fn new() -> Self {
        CurrentStatus { index: 1 }
    }
    fn get_index(&mut self) -> i32 {
        self.index += 1;
        self.index.clone()
    }

    fn add_index(&mut self) {
        self.index += 2;
    }
}

lazy_static! {
    pub static ref FOO: Mutex<CurrentStatus> = Mutex::new(CurrentStatus::new());
}

unsafe impl Send for CurrentStatus {}

#[wasm_bindgen]
pub fn add_index() {
    FOO.lock()
        .unwrap_or_else(PoisonError::into_inner)
        .add_index();
}

#[wasm_bindgen]
pub fn get_index() -> i32 {
    let mut foo = FOO.lock().unwrap_or_else(PoisonError::into_inner);
    if foo.get_index() == 6 {
        panic!();
    }
    return foo.get_index();
}

JavaScript:

const js = import("../pkg/hello_wasm.js");
js.then(js => {
  window.js = js;
  console.log(js.get_index());
  js.add_index();
  console.log(js.get_index());
  js.add_index();
  console.log(js.get_index());
  js.add_index();
  console.log(js.get_index());
  js.add_index();
  console.log(js.get_index());
  js.add_index();
});

出现紧急情况后,我根本无法调用该函数,好像Wasm已死。

rust webassembly
1个回答
2
投票

在回答这个问题之前,我可能应该提到,不应将恐慌处理用作一般错误机制。它们应用于不可恢复的错误。

引用documentation.

这允许程序立即终止并向程序的调用者提供反馈。恐慌!当程序达到不可恢复的状态时应使用。

Rust中的Panic实际上比来自C ++背景的人看起来要柔和得多(我认为有些人在评论中写这种情况)。默认情况下,未捕获的Rust恐慌会终止线程,而C ++异常会终止整个过程。

引用documentation

Rust中的致命逻辑错误会导致线程崩溃,在此期间,线程将展开堆栈,运行析构函数并释放拥有的资源。尽管这不是一种“尝试/捕获”机制,但是Rust的紧急情况仍然可以通过catch_unwind捕获(除非使用panic = abort进行编译)并从catch_unwind中恢复,或者通过resume_unwind恢复。如果未捕获到紧急情况,则线程将退出,但可以选择通过具有join的其他线程来检测紧急情况。如果主线程出现紧急情况而没有捕获到紧急情况,则应用程序将以非零的退出代码退出。

catch_unwind可以从紧急情况中恢复线程,但是您应该知道,不能保证catch_unwind能够捕获所有紧急情况。

请注意,此功能可能无法捕获Rust中的所有紧急情况。 Rust中的恐慌并不总是通过平仓来实现,但是也可以通过中止该过程来实现。此功能仅捕获正在缓解的紧急情况,而不会中止该过程的紧急情况。

因此,我们了解从恐慌中恢复是可以的。问题是锁中毒时该怎么办。

引用documentation

此模块中的互斥锁实施一种称为“中毒”的策略,只要线程在按住该互斥锁时发生紧急情况,该互斥锁即被视为有毒。一旦互斥体中毒,默认情况下,所有其他线程都无法访问数据,因为它很可能已被污染(某些不变性未被维护)。

存在中毒的正当理由,因为可能不会保留您数据的不变性。考虑某些功能中间的panic!。这只是您可以绕过的附加安全级别。

但是,中毒的互斥锁不会阻止对底层数据的所有访问。 PoisonError类型具有int_inner方法,该方法将返回保护,否则将在成功锁定后返回该保护。尽管锁被中毒,这仍允许访问数据。

use std::sync::{Mutex, PoisonError};
fn main() {
    let mutex = Mutex::new(1);

    // We are prepared to face bugs if invariants are wrong
    println!("{}", mutex.lock().unwrap_or_else(PoisonError::into_inner));
}

Playground link

当然,解决恐慌总比这样做好。

© www.soinside.com 2019 - 2024. All rights reserved.