Pull to refresh

Comments 11

Как говорится, стесняюсь спросить:
Для демонстрации корутин на плюсах действительно нужен такой монструозный код?
Как говорил Янычар из фильма "72 метра":
Тут же… можно сломать, пока до конца доберёшься!

Конкретно данные примеры рассматривают различные кейсы работы корутин. К более простым примерам в boost asio я уже отсылал в комментах к предыдущей статье, вот и сюда положу. Я потому и начал перевод главы из книги, что немного закипел после разбора этих примеров. Однако последние 4 абзаца, которые поясняют различия, расставили всё на места. По крайней мере для меня.
Более детально, event1 посылает уведомление до того как receiverThread1 был запущен.

Я не понимаю чем это гарантируется. Также я не понимаю почему нет рейса между if (event.notified), suspendedWaiter.load() и event.suspendedWaiter.store(this)
Я не понимаю чем это гарантируется.

Правильно не понимаете, ничем это не гарантируется. К счастью, код на это не полагается.


Также я не понимаю почему нет рейса между if (event.notified), suspendedWaiter.load() и event.suspendedWaiter.store(this)

Между этими строками нет гонки потому что событие ожидается строго одним потоком, и в этом потоке методы await_ready и await_suspend выполняются последовательно.

Простите, я имел ввиду тот suspendedWaiter.load() что в notify(). notify() и await_suspend() вызываются же в разных потоках?

Да, вы правы. Теперь вижу: там гонка. Только проблема не в suspendedWaiter.load/store, а в notified.


Во-первых, поле notified не атомарно (точнее, оно объявлено как атомарное, но используется почему-то по-другому). Во-вторых, работа с notified и с suspendedWaiter неатомарна в целом (атомарность не компонуется).


Правильный вариант должен использовать всего одно поле, CAS-операции и синтетическое значение для состояния notified. Должно получиться как-то так (на современных плюсах не писал, прошу простить ошибки):


// class Event
constexpr Awaiter * NOTIFIED = (Awaiter*)(void*)(1);
std::atomic<Awaiter *> state;

void Event::notify() {
    auto *waiter = state.exchange(NOTIFIED);
    if (waiter != nullptr && waiter != NOTIFIED) {
        waiter->coroutineHandle.resume();
    }
}

bool Event::Awaiter::await_ready() const {
    return false;
}

bool Event::Awaiter::await_suspend(std::coroutine_handle<> ch) {
    Awaiter *expected = nullptr;
    return event.state.compare_exchange_strong(&expected, this);
}
тоже вчера задумался почему handle_type не является владеющим и не уважает мув. Потом дошло — вы можете захотеть сделать shared future, хитрые sheduler'ы и пр. По сути, std::coroutine_handle — обычный указатель на фрейм, но с парой методов, которые реализовывать самому было бы больно. По сути стандарт предоставил необходимо-достаточный кирпичик, уже который можете оборачивать как заблагорассудится. Что в целом неудивительно, учитывая что самих классов корутин стандарт тоже не предоставил
А в std не планируют добавить дефолтную реализацию вот этого Generator?
Sign up to leave a comment.

Articles