SeniorКодИногдаЕщё не отвечали
Как написать кастомный Awaitable, возобновляющий корутину при завершении I/O?
Реализуйте кастомный Awaitable ReadAwaitable, который приостанавливает корутину до завершения асинхронного чтения I/O, затем возобновляет её и отдаёт число байт.
Ограничения:
- Корутина обязана всегда приостанавливаться для ожидания I/O (никогда не завершаться синхронно).
- Колбэк завершения должен захватывать handle корутины по значению, а не по ссылке.
- Результат должен сохраняться до возобновления корутины.
- Поток должен быть свободен для другой работы, пока I/O выполняется.
#include <coroutine>
struct ReadAwaitable {
IoService& io;
int fd;
ssize_t result = 0;
bool await_ready() const;
void await_suspend(std::coroutine_handle<> h);
ssize_t await_resume() const;
};
Допишите реализацию.
Сделайте await_ready() возвращающим false, пусть await_suspend(handle) регистрирует I/O и сохраняет handle в колбэке завершения, а await_resume() возвращает результат. Колбэк вызывает handle.resume().
- ✗Вернуть
trueизawait_ready(), из-за чего корутина не приостанавливается для ожидания I/O - ✗Захватить
handleпо ссылке в колбэке — он повиснет после возвратаawait_suspend - ✗Возобновить handle до сохранения результата, из-за чего
await_resume()читает мусор
- →Почему безопасно возобновлять handle из потока I/O, а не из исходного?
- →Как пробросить ошибку I/O наружу через
await_resume()?
Кастомный Awaitable для I/O
#include <coroutine>
struct ReadAwaitable {
IoService& io;
int fd;
ssize_t result = 0; // живёт во фрейме корутины
bool await_ready() const { return false; } // всегда ждём I/O
void await_suspend(std::coroutine_handle<> h) {
// Регистрируем операцию. Колбэк захватывает handle ПО ЗНАЧЕНИЮ.
io.async_read(fd, [this, h]() mutable {
result = io.last_bytes(); // сначала сохраняем результат...
h.resume(); // ...потом возобновляем корутину
});
// await_suspend возвращает void → управление уходит вызывающему,
// поток свободен, пока I/O не завершится
}
ssize_t await_resume() const { return result; } // значение всего co_await
};
Task<void> handle_client(IoService& io, int fd) {
ssize_t n = co_await ReadAwaitable{io, fd}; // приостановка до завершения чтения
process(n);
}
Ключевые моменты: result — член Awaitable, который сам лежит во фрейме корутины, поэтому он переживает приостановку. Колбэк захватывает h по значению — coroutine_handle это лёгкий указатель, копировать дёшево и безопасно. Результат записывается до resume(), иначе await_resume() прочитает мусор.