Rust
2.24K subscribers
173 photos
109 videos
2 files
259 links
Полезный контент по программированию на Rust
Download Telegram
🪄 Разрушаем магию: Твоя Future - это просто enum

Многие приходят в Rust из Go или Erlang и думают, что async запускает легковесный поток (горутину). Забудьте. Асинхронный Rust работает совершенно иначе - он не аллоцирует стек под каждую задачу.

Когда вы пишете async fn, компилятор превращает вашу функцию в конечный автомат (State Machine).

Смотрим код:


async fn fetch_data() {
let req = build_request();
let resp = send(req).await; // <-- Точка остановки 1
process(resp).await; // <-- Точка остановки 2
}



Под капотом LLVM генерирует невидимый enum, который хранит локальные переменные между вызовами .await:


// Примерно так это видит компилятор:
enum FetchDataStateMachine {
Start,
WaitSend { req: Request }, // Храним локальные переменные
WaitProcess { resp: Response },
Done,
}



Когда рантайм вызывает метод poll() у этой Future:

1. Выполняется кусок кода до первого .await.
2. Состояние переключается (в WaitSend).
3. Управление возвращается обратно в рантайм! (Никакого блокирования потока).

Почему это круто? Такой enum весит считанные байты. Вы можете создать миллион таких Future на обычном ноутбуке, и они займут всего пару десятков мегабайт оперативки. Никакая ОС не позволит вам создать миллион настоящих потоков.

#rust #async #under_the_hood #future

👉 @rust_lib
👍20✍7❤3🥰1🤗1
⏰ Хватит дергать: Waker и паттерн пейджера

Окей, Future вернула управление (сказала Poll::Pending). Но как рантайм узнает, что данные по сети пришли и пора снова вызвать poll()? Если он будет постоянно опрашивать все Future в цикле (while loop), ваш CPU улетит в космос на 100% загрузке.

Здесь на сцену выходят Executor, Reactor и Waker.

• Executor - это менеджер задач. Он берет Future и вызывает у нее poll().
• Reactor - это слушатель ОС (обертка над epoll в Linux, kqueue в macOS или IOCP в Windows). Он ждет событий от сетевой карты или диска.
• Waker - это тот самый пейджер, который выдают в бургерной.

Как это работает:

1. Future делает сетевой запрос и понимает, что данных еще нет.

2. Она регистрирует этот запрос в Reactor и отдает ему свой Waker (колбэк, в котором зашит ID этой Future).

3. Future возвращает Pending. Executor забывает про неё и идет выполнять другие задачи.

4. ...проходит время... Сетевая карта получает пакеты. ОС дергает epoll.

5. Reactor видит: "Ага, пришли данные для сокета X". Он берет Waker, привязанный к этому сокету, и вызывает waker.wake().

6. Waker сигнализирует Executor'у: "Задача #42 готова, помести её обратно в очередь на выполнение".

7. Executor снова вызывает poll(), и стейт-машина делает следующий шаг.

Никакого холостого хода CPU. Только чистая событийная архитектура.

#rust #async #waker #tokio #epoll

👉 @rust_lib
👍12🔥4❤2🥰2👨‍💻1🤗1
🪄 Разрушаем магию: Твоя Future - это просто enum

Многие приходят в Rust из Go или Erlang и думают, что async запускает легковесный поток (горутину). Забудьте. Асинхронный Rust работает совершенно иначе - он не аллоцирует стек под каждую задачу.

Когда вы пишете async fn, компилятор превращает вашу функцию в конечный автомат (State Machine).

Смотрим код:


async fn fetch_data() {
let req = build_request();
let resp = send(req).await; // <-- Точка остановки 1
process(resp).await; // <-- Точка остановки 2
}



Под капотом LLVM генерирует невидимый enum, который хранит локальные переменные между вызовами .await:


// Примерно так это видит компилятор:
enum FetchDataStateMachine {
Start,
WaitSend { req: Request }, // Храним локальные переменные
WaitProcess { resp: Response },
Done,
}



Когда рантайм вызывает метод poll() у этой Future:

1. Выполняется кусок кода до первого .await.
2. Состояние переключается (в WaitSend).
3. Управление возвращается обратно в рантайм! (Никакого блокирования потока).

Почему это круто? Такой enum весит считанные байты. Вы можете создать миллион таких Future на обычном ноутбуке, и они займут всего пару десятков мегабайт оперативки. Никакая ОС не позволит вам создать миллион настоящих потоков.

#rust #async #under_the_hood #future

📲 Мы в MAX

👉 @rust_lib
🔥5👍4
⏰ Хватит дергать: Waker и паттерн пейджера

Окей, Future вернула управление (сказала Poll::Pending). Но как рантайм узнает, что данные по сети пришли и пора снова вызвать poll()? Если он будет постоянно опрашивать все Future в цикле (while loop), ваш CPU улетит в космос на 100% загрузке.

Здесь на сцену выходят Executor, Reactor и Waker.

• Executor - это менеджер задач. Он берет Future и вызывает у нее poll().
• Reactor - это слушатель ОС (обертка над epoll в Linux, kqueue в macOS или IOCP в Windows). Он ждет событий от сетевой карты или диска.
• Waker - это тот самый пейджер, который выдают в бургерной.

Как это работает:

1. Future делает сетевой запрос и понимает, что данных еще нет.

2. Она регистрирует этот запрос в Reactor и отдает ему свой Waker (колбэк, в котором зашит ID этой Future).

3. Future возвращает Pending. Executor забывает про неё и идет выполнять другие задачи.

4. ...проходит время... Сетевая карта получает пакеты. ОС дергает epoll.

5. Reactor видит: "Ага, пришли данные для сокета X". Он берет Waker, привязанный к этому сокету, и вызывает waker.wake().

6. Waker сигнализирует Executor'у: "Задача #42 готова, помести её обратно в очередь на выполнение".

7. Executor снова вызывает poll(), и стейт-машина делает следующий шаг.

Никакого холостого хода CPU. Только чистая событийная архитектура.

#rust #async #waker #tokio #epoll

📲 Мы в MAX

👉 @rust_lib
👍4🔥2