首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在闭包之间与wasm-bindgen具体地共享数据?

如何在闭包之间与wasm-bindgen具体地共享数据?
EN

Stack Overflow用户
提问于 2021-04-03 20:20:55
回答 1查看 639关注 0票数 0

在我的浏览器应用程序中,两个闭包访问存储在Rc<RefCell<T>>中的数据。一个闭包不断地借用数据,而另一个闭包则不断地借用数据。这两个闭包是相互独立地调用的,这偶尔会导致BorrowErrorBorrowMutError

下面是我在MWE上的尝试,尽管它利用未来人为地夸大了错误发生的可能性:

代码语言:javascript
复制
use std::cell::RefCell;
use std::future::Future;
use std::pin::Pin;
use std::rc::Rc;
use std::task::{Context, Poll, Waker};
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsValue;

#[wasm_bindgen]
extern "C" {
    #[wasm_bindgen(js_namespace = console)]
    pub fn log(s: &str);
    #[wasm_bindgen(js_name = setTimeout)]
    fn set_timeout(closure: &Closure<dyn FnMut()>, millis: u32) -> i32;
    #[wasm_bindgen(js_name = setInterval)]
    fn set_interval(closure: &Closure<dyn FnMut()>, millis: u32) -> i32;
}

pub struct Counter(u32);

#[wasm_bindgen(start)]
pub async fn main() -> Result<(), JsValue> {
    console_error_panic_hook::set_once();

    let counter = Rc::new(RefCell::new(Counter(0)));

    let counter_clone = counter.clone();
    let log_closure = Closure::wrap(Box::new(move || {
        let c = counter_clone.borrow();
        log(&c.0.to_string());
    }) as Box<dyn FnMut()>);
    set_interval(&log_closure, 1000);
    log_closure.forget();

    let counter_clone = counter.clone();
    let increment_closure = Closure::wrap(Box::new(move || {
        let counter_clone = counter_clone.clone();
        wasm_bindgen_futures::spawn_local(async move {
            let mut c = counter_clone.borrow_mut();
            // In reality this future would be replaced by some other
            // time-consuming operation manipulating the borrowed data
            SleepFuture::new(5000).await;
            c.0 += 1;
        });
    }) as Box<dyn FnMut()>);
    set_timeout(&increment_closure, 3000);
    increment_closure.forget();

    Ok(())
}

struct SleepSharedState {
    waker: Option<Waker>,
    completed: bool,
    closure: Option<Closure<dyn FnMut()>>,
}

struct SleepFuture {
    shared_state: Rc<RefCell<SleepSharedState>>,
}

impl Future for SleepFuture {
    type Output = ();
    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        let mut shared_state = self.shared_state.borrow_mut();
        if shared_state.completed {
            Poll::Ready(())
        } else {
            shared_state.waker = Some(cx.waker().clone());
            Poll::Pending
        }
    }
}

impl SleepFuture {
    fn new(duration: u32) -> Self {
        let shared_state = Rc::new(RefCell::new(SleepSharedState {
            waker: None,
            completed: false,
            closure: None,
        }));

        let state_clone = shared_state.clone();
        let closure = Closure::wrap(Box::new(move || {
            let mut state = state_clone.borrow_mut();
            state.completed = true;
            if let Some(waker) = state.waker.take() {
                waker.wake();
            }
        }) as Box<dyn FnMut()>);

        set_timeout(&closure, duration);

        shared_state.borrow_mut().closure = Some(closure);

        SleepFuture { shared_state }
    }
}

惊慌失措地说“已经借来了:BorrowError”

这个错误是有意义的,但我应该如何解决呢?

我目前的解决方案是让闭包使用try_borrowtry_borrow_mut,如果不成功,则在尝试再次借款之前使用setTimeout任意时间。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-04-03 23:50:57

独立于Rust的借用语义来思考这个问题。您有一个长期运行的操作,正在更新某些共享状态。

  • ,如果您使用线程,您将如何做?你会把共享状态放在锁后面。RefCell就像一个锁,只是您不能在解锁时阻止它,但是您可以通过某种消息传递来模拟阻塞,从而唤醒读者。

  • ,如果你使用的是纯JavaScript,你会怎么做?您不会自动拥有类似于RefCell的任何东西,所以也不会有:

代码语言:javascript
复制
- The state can be safely read while the operation is still ongoing (in a concurrency-not-parallelism sense): in this case, emulate that by not holding a single `RefMut` (result of `borrow_mut()`) alive across an `await` boundary.
- The state is _not_ safe to be read: you'd either write something lock-like as described above, or perhaps arrange so that it's only written once when the operation is done, and until then, the long-running operation has its own private state _not_ shared with the rest of the application (so there can be no `BorrowError` conflicts).

考虑您的应用程序实际需要什么,并选择一个合适的解决方案。实现这些解决方案中的任何一个都很可能涉及到用于通信的额外的内部可变对象。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/66935541

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档