Rust 在异步之外的futures::lock::Mutex上尝试锁定?

Rust 在异步之外的futures::lock::Mutex上尝试锁定?,rust,Rust,我正在尝试为具有futures::lock::Mutex的结构实现Async读取: pub struct SmolSocket<'a> { stack: Arc<futures::lock::Mutex<SmolStackWithDevice<'a>>>, } impl<'a> AsyncRead for SmolSocket<'a> { fn poll_read( self: Pin&l

我正在尝试为具有
futures::lock::Mutex
的结构实现
Async
读取:

pub struct SmolSocket<'a> {
    stack: Arc<futures::lock::Mutex<SmolStackWithDevice<'a>>>,
}

impl<'a> AsyncRead for SmolSocket<'a>  {
    fn poll_read(
        self: Pin<&mut Self>,
        cx: &mut Context<'_>,
        buf: &mut tokio::io::ReadBuf<'_>
    ) -> Poll<std::io::Result<()>> {
        block_on(self.stack).read(...)
    }
}
pub-struct-SmolSocket>>,
}
恳求{
fn poll_read(
self:Pin,
cx:&mut上下文
)->投票{
块_on(self.stack).read(…)
}
}
问题是,因为
poll\u read
不是异步的,所以我不能调用
wait
。但我也不想,因为它会阻塞。我可以调用
try\u lock
进行尝试,如果没有,我会注册一个
Waker
,将来由
SmolSocket
调用


既然我也不能这样做,因为它不是异步的,那么是否有一个版本的
block\u on
try\u lock
futures::lock::Mutex
async
之外?

你可能想轮询
MutexLockFuture
相反,这可以通过,以下哪种情况:

let num=match fut.poll(cx){
轮询::就绪(t)=>t,
Poll::Pending=>返回Poll::Pending,
};
要对未来进行投票,您还需要锁定它(确保它不会被移动)。如果类型已经是
Unpin
MutexLockFuture
is),则可以使用或
Pin::new
在堆栈上执行此操作,或者使用
Box::Pin
移动到堆上

下面是一个可运行的示例

⚠️ 继续阅读,看看你为什么不想这样做

#![功能(就绪\u宏)]
使用核心::{
未来,
pin::pin,
任务:{ready,Context,Poll},
};
使用std::sync::Arc;
使用tokio::io::{AsyncRead,AsyncReadExt};
发布结构SmolStackWithDevice{
fn poll_read(
mut self:Pin,
cx:&mut上下文,
)->投票{
如果self.counter%2==0{
自计数器+=1;
cx.waker().wake_by_ref();
println!(“不读”);
返回轮询::待定;
}
buf.put_切片(&[self.data[self.counter/2]]);
自计数器+=1;
println!(“读点什么”);
轮询::就绪(Ok(()))
}
}
发布结构SmolSocket>>,
}
恳求{
fn poll_read(
self:Pin,
cx:&mut上下文,
)->投票{
让mut lock_fut=self.stack.lock();
让pinted_lock_fut=Pin::new(&mut lock_fut);
让mut guard=ready!(锁定未来轮询(cx));
println!(“获得的锁”);
让pinted_inner=Pin::new(&mut*guard);
固定的内部轮询读取(cx,buf)
}
}
#[tokio::main(flavor=“current_thread”)]
异步fn main(){
让数据=b“马”;
设mut buf=[0;5];
设mut s=SmolSocket{
堆栈:Arc::new(
SmolStackWithDevice{
柜台:0,,
数据:&数据[…],
}
.into(),
),
};
s、 读取准确的(&mut buf)。等待。展开();
println!(“{}”,字符串::from_utf8_lossy(&buf));
}

⚠️ 继续阅读,看看你为什么不想这样做

那么,问题是什么? 从输出中可以看出,每当我们成功获取锁,但底层源尚未准备好读取,或者只给我们一个小的读取,我们就会丢弃锁,在下一次轮询时,我们必须再次获取它

这是一个很好的要点,请记住,只有当成功锁定的
防护装置将在
等待
期间保持,或显式存储在
未来
数据结构中时,才建议在
std
停车场
上使用
异步
互斥模式

我们在这里并没有这样做,我们只是在练习与相同的快速路径,因为每当锁不立即可用时,我们就会丢弃
MutexLockFuture
,而不是等待被唤醒再次轮询它

但是,将锁存储在数据结构中很容易意外死锁。因此,一个好的设计可能是创建一个难以存储(借用)
AsyncRead
的适配器来包装锁:

pub-struct-SmolSocket>>,
}
恳求{
fn read(&'a self)->读卡器{
锁定(futures::lock::MutexLockFuture>),
已锁定(未来::锁定::MutexGuard>),
}
恳求{
fn poll_read(
self:Pin,
cx:&mut上下文,
)->投票{
让这个=self.get_mut();
匹配这个{
读卡器::锁定(f)=>{
*this=Reader::Locked(ready!(Pin::new(f).poll(cx));
println!(“获得的锁”);
Pin::新建(此).poll\u读取(cx,buf)
}
读卡器::锁定(l)=>Pin::新建(&mut**l)。轮询读取(cx,buf),
}
}
}
#[tokio::main(flavor=“current_thread”)]
异步fn main(){
让数据=b“马”;
设mut buf=[0;5];
设s=SmolSocket{
堆栈:Arc::new(
SmolStackWithDevice{
柜台:0,,
数据:&数据[…],
}
.into(),
),
};
s、 read().read_精确(&mut buf)。等待。展开();
println!(“{}”,字符串::from_utf8_lossy(&buf));
}

这是可行的,因为
LockFuture
和我们的
SmolStackWithDevice
都是
Unpin
(非自参考),因此我们不必保证不会移动它们

在一般情况下,例如,如果您的
SmolStackWithDevice
Unpin
,则必须像这样投影
Pin

不安全{
让this=self.get_unchecked_mut();
匹配这个{
读卡器::锁定(f)=>{
*this=Reader::Locked(ready!(Pin::new_unchecked(f).poll(cx));
println!(“获得的锁”);
Pin::未选中新建(此)。轮询读取(cx,buf)
}
读卡器::已锁定(l)=>Pin::新未选中(&mut**l)。轮询读取(cx,buf),
}
}
由于不确定如何封装不安全性,
pin_项目
在这里是不够的,因为我们