Структура Waker
Описание
Waker - это дескриптор для пробуждения задачи, уведомляющий её исполнитель о готовности к выполнению.
Этот дескриптор инкапсулирует экземпляр RawWaker, который определяет специфичное для исполнителя поведение пробуждения.
Типичный жизненный цикл Waker:
- Создается исполнителем
- Оборачивается в
Context - Передается в
Future::poll() - Если future возвращает
Poll::Pending, он должен сохранить waker и вызватьWaker::wake(), когда future нужно опросить снова
Реализует Clone, Send и Sync; следовательно, waker может быть вызван из любого потока, включая те, которые никак не управляются исполнителем. Например, это может быть сделано для пробуждения future, когда блокирующий вызов функции завершается в другом потоке.
Примечание: предпочтительнее использовать waker.clone_from(&new_waker) вместо *waker = new_waker.clone(), так как первый вариант избежит ненужного клонирования waker, если оба waker пробуждают одну и ту же задачу.
Создание Waker из RawWaker небезопасно. Реализация трейта Wake является безопасной альтернативой, но требует выделения памяти.
Синтаксис
#![allow(unused)] fn main() { pub struct Waker { /* приватные поля */ } }
Методы
wake
#![allow(unused)] fn main() { pub fn wake(self) }
Пробуждает задачу, связанную с этим Waker.
Пока исполнитель продолжает работать и задача не завершена, гарантируется, что каждый вызов wake() (или wake_by_ref()) будет сопровождаться как минимум одним вызовом poll() задачи, к которой принадлежит этот Waker. Это позволяет временно уступать другим задачам во время выполнения потенциально неограниченных циклов обработки.
Примечание: вышесказанное подразумевает, что множественные пробуждения могут быть объединены в один вызов poll() средой выполнения.
Также учтите, что уступка конкурирующим задачам не гарантируется: исполнитель сам выбирает, какую задачу запускать, и может выбрать повторный запуск текущей задачи.
wake_by_ref
#![allow(unused)] fn main() { pub fn wake_by_ref(&self) }
Пробуждает задачу, связанную с этим Waker, не потребляя Waker.
Это похоже на wake(), но может быть немного менее эффективно в случае, когда доступен owned Waker. Этот метод следует предпочитать вызову waker.clone().wake().
will_wake
#![allow(unused)] fn main() { pub fn will_wake(&self, other: &Waker) -> bool }
Возвращает true, если этот Waker и другой Waker пробуждают одну и ту же задачу.
Эта функция работает по принципу "best-effort" и может возвращать false, даже если Waker пробуждают одну и ту же задачу. Однако, если функция возвращает true, гарантируется, что Waker пробуждают одну и ту же задачу.
Эта функция в основном используется для оптимизации — например, реализация clone_from этого типа использует её, чтобы избежать клонирования waker, когда они и так пробуждают одну и ту же задачу.
new ⚠️
#![allow(unused)] fn main() { pub const unsafe fn new( data: *const (), vtable: &'static RawWakerVTable, ) -> Waker }
Создает новый Waker из предоставленного указателя данных и vtable.
Доступность: 1.83.0 (const: 1.83.0)
Безопасность
Поведение возвращаемого Waker не определено, если контракт, определенный в документации RawWakerVTable, не соблюдается.
from_raw ⚠️
#![allow(unused)] fn main() { pub const unsafe fn from_raw(waker: RawWaker) -> Waker }
Создает новый Waker из RawWaker.
Доступность: 1.36.0 (const: 1.82.0)
Безопасность
Поведение возвращаемого Waker не определено, если контракт, определенный в документации RawWaker и RawWakerVTable, не соблюдается.
noop
#![allow(unused)] fn main() { pub const fn noop() -> &'static Waker }
Возвращает ссылку на Waker, который ничего не делает при использовании.
Доступность: 1.85.0 (const: 1.85.0)
В основном полезно для написания тестов, которым нужен Context для опроса некоторых future, но которые не ожидают, что эти future пробудят waker, или не нужно делать ничего специфического, если это произойдет.
В более общем смысле, использование Waker::noop() для опроса future означает отбрасывание уведомления о том, когда future следует опросить снова. Поэтому это следует использовать только тогда, когда такое уведомление не потребуется для прогресса.
Если нужен owned Waker, можно клонировать этот.
Примеры
#![allow(unused)] fn main() { use std::future::Future; use std::task; let mut cx = task::Context::from_waker(task::Waker::noop()); let mut future = Box::pin(async { 10 }); assert_eq!(future.as_mut().poll(&mut cx), task::Poll::Ready(10)); }
data
#![allow(unused)] fn main() { pub fn data(&self) -> *const () }
Получает указатель данных, использованный для создания этого Waker.
Доступность: 1.83.0
vtable
#![allow(unused)] fn main() { pub fn vtable(&self) -> &'static RawWakerVTable }
Получает указатель vtable, использованный для создания этого Waker.
Доступность: 1.83.0
from_fn_ptr ⚡️
#![allow(unused)] fn main() { pub const fn from_fn_ptr(f: fn()) -> Waker }
Создает Waker из указателя на функцию.
Примечание: Это экспериментальное API, доступно только в ночных сборках Rust.
Реализации трейтов
From<Arc<W>> для Waker
#![allow(unused)] fn main() { impl<W> From<Arc<W>> for Waker where W: Wake + Send + Sync + 'static, }
Использует пробуждаемый тип как Waker.
Особенности:
- Не требует выделения памяти или атомарных операций
- Доступно только на платформах с
target_has_atomic=ptr
Clone
#![allow(unused)] fn main() { impl Clone for Waker }
clone_from
#![allow(unused)] fn main() { fn clone_from(&mut self, source: &Waker) }
Присваивает клон source для self, если только self.will_wake(source) и так не возвращает true.
Этот метод предпочтительнее простого присваивания source.clone() для self, так как он избегает клонирования waker, если self уже является тем же waker.
Примеры
#![allow(unused)] fn main() { use std::future::Future; use std::pin::Pin; use std::sync::{Arc, Mutex}; use std::task::{Context, Poll, Waker}; struct Waiter { shared: Arc<Mutex<Shared>>, } struct Shared { waker: Waker, // ... } impl Future for Waiter { type Output = (); fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> { let mut shared = self.shared.lock().unwrap(); // обновляем waker shared.waker.clone_from(cx.waker()); // логика готовности ... } } }
Debug
#![allow(unused)] fn main() { impl Debug for Waker }
Позволяет форматирование Waker для отладки.
Drop
#![allow(unused)] fn main() { impl Drop for Waker }
Реализует деструктор для Waker.
Автоматические реализации трейтов
Freeze for Waker- может быть замороженRefUnwindSafe for Waker- безопасен для паникиUnwindSafe for Waker- безопасен для паникиSend for Waker- может быть передан между потокамиSync for Waker- может быть разделен между потокамиUnpin for Waker- может быть безопасно перемещено
Назначение
Waker используется для:
- Пробуждения асинхронных задач
- Интеграции с системными событиями
- Создания эффективных исполнителей
- Реализации сложных асинхронных паттернов