Tenho trabalhado em uma implementação Future em Rust, e encontrei um comportamento que não entendo completamente. Especificamente, estou usando std::sync::mpsc::Receiver
dentro do poll
método, e estou tentando decidir entre while let
e if let
para receber mensagens.
Aqui está a versão simplificada do meu método de enquete:
fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.get_mut();
// Option 1: Using `while let`
while let Ok(new_attributes) = this.receiver.try_recv() {
// Process the message
}
// Option 2: Using `if let`
if let Ok(new_attributes) = this.receiver.try_recv() {
// Process the message
}
Poll::Pending
}
O que eu observei
Quando uso while let, tudo parece funcionar como esperado: o Future é re-polled quando novas mensagens chegam. No entanto, quando uso if let, o Future parece travar e nunca mais acorda, mesmo quando novas mensagens estão disponíveis.
Meu Entendimento
Eu sei que o Waker deve ser usado para notificar o executor de que o Future deve ser re-pesquisado. Dado que minha função de pesquisa sempre retorna Poll::Pending, eu esperaria que o Future continuasse sendo pesquisado repetidamente, a menos que o próprio processo pare. No entanto, não entendo por que while let garante que o Waker seja acionado corretamente, enquanto if let não parece fazer o mesmo.
Contexto adicional
O Futuro é gerado assim:
ctx.task_executor()
.spawn_critical_blocking("transaction execution service", Box::pin(fut_struct));
A função spawn_critical_blocking funciona da seguinte maneira:
pub fn spawn_critical_blocking<F>(&self, name: &'static str, fut: F) -> JoinHandle<()>
where
F: Future<Output = ()> + Send + 'static,
{
self.spawn_critical_as(name, fut, TaskKind::Blocking)
}
E internamente:
fn spawn_critical_as<F>(
&self,
name: &'static str,
fut: F,
task_kind: TaskKind,
) -> JoinHandle<()>
where
F: Future<Output = ()> + Send + 'static,
{
// Wrapping the future and handling task errors
let task = std::panic::AssertUnwindSafe(fut)
.catch_unwind()
.map_err(...);
let task = async move {
// Handling task shutdown and execution
let task = pin!(task);
let _ = select(on_shutdown, task).await;
};
self.spawn_on_rt(task, task_kind)
}
O spawn_on_rt
método então usa spawn_blocking
.
Minha pergunta
- Por que usar while let garante que o Waker seja acionado, mas usar if let
- faz
não?
- Qual é o mecanismo subjacente que causa essa diferença de comportamento?
Quaisquer informações ou explicações serão muito apreciadas!