Во время ожидания сетевого события future возвращает Pending: каким образом Waker обеспечивает её повторную проверку после готовности события?
Waker связывает ожидающий future с механизмом планирования async runtime. Когда future возвращает Poll::Pending, она или используемая ею библиотека регистрирует переданный runtime waker во внешнем источнике события. После готовности события этот waker вызывается, runtime ставит задачу в очередь на повторный poll, а сама future проверяет состояние заново.
Вызов wake не продолжает future немедленно и не означает, что операция уже завершена. Он лишь сообщает исполнителю: задачу нужно снова опросить.
Асинхронный код должен ожидать медленные операции, не удерживая поток в блокирующем вызове. Для этого Rust разделяет описание операции, представленное Future, и механизм её исполнения, предоставляемый runtime.
Одного возвращения Pending недостаточно: исполнителю нужно знать, когда имеет смысл повторить проверку. Waker решает эту задачу без постоянного опроса всех незавершённых futures и без выделенного потока на каждую операцию.
Пусть future ожидает сетевой пакет и при первом poll ещё не может завершиться. Если она просто вернёт Pending, но не зарегистрирует waker, runtime не получит надёжного сигнала о моменте готовности данных.
Постоянный активный опрос создаёт лишнюю нагрузку на процессор, а блокировка потока снижает масштабируемость. Ошибка в регистрации или вызове waker может привести к зависшей задаче, которая никогда не будет опрошена повторно.
Во время poll runtime передаёт future контекст, содержащий текущий waker. Future передаёт его дальше драйверу ввода-вывода или сохраняет в состоянии операции, если именно она отвечает за ожидание события.
Если событие пока не готово, future возвращает Poll::Pending. Когда драйвер обнаруживает готовность сокета, таймера или другого ресурса, он вызывает wake у зарегистрированного waker. Обычно это приводит к постановке связанной async-задачи в очередь исполнителя.
При следующем poll future должна снова проверить источник события. Если данные уже доступны, она возвращает Poll::Ready; если нет, снова регистрирует актуальный waker и возвращает Pending.
Waker является сигналом, а не результатом операции: вызов wake может произойти раньше фактической готовности, повториться несколько раз или быть объединён runtime. Поэтому future обязана корректно обрабатывать лишние пробуждения и не полагаться на точное число вызовов wake.
Нельзя бездумно хранить старый waker: задача может быть перепланирована с другим waker. Реализация ожидания должна обновлять сохранённый waker, если он относится к другому экземпляру планировщика. Кроме того, после завершения или отмены операции зарегистрированный источник событий не должен продолжать использовать недействительное состояние future.
Сервис обрабатывает тысячи TCP-соединений. Для каждого соединения future при отсутствии данных возвращает Pending, а сетевой драйвер будит только те задачи, чьи сокеты стали готовы.
Вариант с блокирующим чтением потребовал бы отдельный поток или блокирующий пул на множество соединений. Это проще для локальной логики, но увеличивает потребление потоков и ухудшает масштабирование.
Вариант с циклом постоянного опроса всех сокетов не блокирует потоки, но тратит процессор на проверки, когда событий нет. Выбранная схема с регистрацией waker и уведомлением драйверами событий позволяет ожидать готовность эффективно: runtime повторно опрашивает только потенциально активные задачи.
1. Дополнительный вопрос: обязан ли вызов wake немедленно выполнить future?
Нет. wake только делает задачу доступной для последующего планирования. Runtime может поставить её в очередь, объединить несколько пробуждений или выполнить позднее на другом потоке. Само выполнение происходит при следующем вызове poll.
2. Дополнительный вопрос: почему после wake future всё равно должна заново проверить источник события?
Пробуждение не гарантирует, что ожидаемое условие всё ещё истинно к моменту poll. Событие могло быть потреблено другой задачей, готовность могла быть временной, а пробуждение — лишним. Поэтому корректный контракт future требует проверять фактическое состояние и возвращать Ready только при реальной готовности результата.
3. Дополнительный вопрос: что произойдёт, если future вернёт Pending, но не сохранит переданный waker?
Runtime не получит необходимого уведомления и может больше не вызвать poll для этой задачи. В результате future зависнет, хотя внешний ресурс впоследствии станет готов. Реализация должна зарегистрировать waker до возврата Pending, а при последующих опросах заменить устаревший waker актуальным.