Futurelock: A subtle risk in async Rust 🔥 Горячее 💬 Длинная дискуссия
В статье описывается концепция "futurelock" — особый тип дедлока, возникающий в асинхронном Rust, когда ресурс, принадлежащий Future A, требуется для Future B, а задача, ответственная за оба Future, перестала опрашивать A. Oxide обнаружила эту проблему в проекте omicron.
Пример демонстрирует программу, которая надежно замирает: фоновая задача захватывает блокировку на 5 секунд, основная задача использует tokio::select! для ожидания двух конкурентных Future. Один Future ждет блокировку, другой — таймаут. Когда таймаут срабатывает, создается новый Future, который также ждет блокировку, но задача больше не опрашивает исходный Future, что приводит к дедлоку.
Комментарии (217)
- Проблема в том, что
tokio::select!отменяет все futures, кроме одного, и это может привести к deadlock, если оставленные в стороне future владеет ресурсом (например, MutexGuard). - Возможность отмены future в Rust в целом не определена, и это приводит к тому, что
tokio::select!ведёт себя непредсказуемо для разработчика, что может привести к утечке ресурсов или deadlock. - В отличие от других языков, где отмена future ведёт к её уничтожению, в Rust модель владения и заимствования не позволяет гарантировать, что ресурсы будут очищены.
- Это приводит к тому, что в Rust нет способа защититься от таких ситуаций на уровне языка, и единственное, что может сделать разработчик - это не использовать
select!и вместо этого вручную управлять состоянием. - В конце концов, это делает async Rust более сложным в использовании, чем он должен бы быть, и создаёт уязвимости, которые не существовали бы в других моделях параллельности.
Corrosion
Fly.io столкнулся с крупнейшим сбоей в истории 1 сентября 2024 года из-за ошибки в Rust-коде их системы распределенного состояния Corrosion. Ошибка в if let выражении над RWLock привела к мгновенному и "заразному" deadlock, который парализовал все прокси-серверы платформы. Авторы подчеркивают, что распределенные системы — это "усилители взрывов": они распространяют данные по сети, а вместе с ними — и ошибки в системах, зависящих от этих данных.
Эта катастрофа стала следствием архитектурных решений Fly.io. В отличие от Kubernetes с его централизованной базой данных, Fly.io использует децентрализованную модель, где серверы являются источником правды о своих рабочих нагрузках. Чтобы масштабироваться по всему миру, они перешли от HashiCorp Consul и SQLite-кэшей к собственной системе Corrosion. Авторы предупреждают: если распределенная система еще не испортила вам выходные или не заставила не спать всю ночь, вы еще не до конца ее понимаете.
Комментарии (95)
- Баг с
if letиRWLockв Rust приводил к мгновенному глобальному дедлоку, что вынудило перейти от единого кластера к региональной двухуровневой схеме данных. - Использование
cr-sqlite(CRDT SQLite) для согласованности вызвало проблемы с nullable-колонками и масштабируемостью, что привело к критике выбора SQLite вместо Postgres. - Gossip-протокол (SWIM) показал ограниченную масштабируемость (~2K-3K узлов), что потребовало иерархической структуры и разделения на региональные кластеры.
- Дизайн блога и отсутствие дат в статьях вызвали нарекания, но технические решения (регионализация, отказ от мгновенного глобального состояния) признаны необходимыми.
Safe C++ proposal is not being continued
Safe C++ больше не развивается.
Год назад появился черновик, обещавший «ржавую» безопасность без ломки старого кода: память, типы, потоки — всё через явный safe-контекст. Комитету идея не зашла: модель Rust назвали чуждой, голоса отдали Profiles — набору ограничений на уже существующий C++, которые на этапе компиляции блокируют UAF, data-race, deadlock, утечки.
Profiles не добавляют синтаксиса, только запрещают опасные паттерны; включаются опцией, обратно совместимы. Safe C++ требовал новых квалификаторов и заимствовал проверки времени жизни — слишком радикально.
Автор Safe C++, Шон Бакстер, признал:
Дальнейшая работа бесполезна; все силы — в Profiles.
Вывод: Profiles слабее, но реалистичнее; ждём их в C++26.
Комментарии (140)
- Комитет C++ фактически отверг Safe C++ и ограничился «Profiles» — набором флагов, запрещающих часть небезопасных конструкций.
- Участники сходятся: главная проблема не техническая, а культурная — сообщество не хочет ограничений, считая «если код работает — значит, он безопасен».
- Profiles критикуют за примитивность: они не решают temporal safety, не выражают лифтаймы и не требуют изменения синтаксиса, поэтому дают лишь иллюзию защиты.
- Большинство считает, что без полного отказа от C-подобного подмножества и без внедрения lifetimes C++ никогда не станет «безопасным»; проще перейти на Rust.
- Практический вывод: крупные проекты продолжат игнорировать Profiles, а новые кодовые базы будут выбирать Rust либо двухъязычный подход «C++26 + внешние анализаторы».