Skip to content

Problems with asynchronous interfaces #151

@Dr-TSNG

Description

@Dr-TSNG

考虑我们有这样的结构,它主要实现了这样的功能:

  1. 发送读请求,将 waker 保存,让出 CPU。
  2. 中断到来,根据 token 取出 waker,唤醒 Future。
  3. 完成读取。

这套方案存在两个问题:

  1. 在非理想情况下,Future 可能被其他事件取消(信号/进程终止等),此时后续的 complete_read_blocks 不会被执行,因此需要驱动支持直接取消一个 token,而不需要完成数据复制工作。(不能创建一个新的 buffer 放到全局区,把 complete 操作移到 handle_irq 处,因为这会导致额外的复制开销。)
  2. peek_used 方法只会取出一个待处理的 token(而不会消费),而在这个 token 被处理前无法得知其他 token 是否完成,从而将对应任务唤醒。这会导致其他的 Future 因为后续无法收到中断信号而永远被挂起(假设中断发生时所有任务都已经就绪)。

这样的问题在目前的异步接口下很难解决。

struct ReqGuard<'a>(&'a VirtIOBlkDevice, u16);

impl Drop for ReqGuard<'_> {
    fn drop(&mut self) {
        let mut inner = self.0.inner.lock();
        inner.tokens.remove(&self.1);
        inner.block.// How: cancel_request(self.1);
    }
}

async fn read_block(&self, block_id: usize, buf: &mut [u8]) -> SyscallResult {
    let mut req = BlkReq::default();
    let mut resp = BlkResp::default();
    let token = loop {
        let token = unsafe {
            self.inner.lock().block.read_blocks_nb(block_id, &mut req, buf, &mut resp)
        };
        // Error handling...
    };
    let guard = ReqGuard(self, token);
    poll_fn(|cx| {
        self.inner.lock().tokens.insert(token, cx.waker().clone());
        Poll::<()>::Pending
    }).await;
    core::mem::forget(guard);
    let res = unsafe {
        self.inner.lock().block.complete_read_blocks(token, &mut req, buf, &mut resp)
    };
    // Error handling...
}

fn handle_irq(&self) {
    let mut inner = self.inner.lock();
    if let Some(token) = inner.block.peek_used() {
        if let Some(waker) = inner.tokens.remove(&token) {
            waker.wake();
        }
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions