Привет, коллеги!
Я использую вот
эту эмуляцию await/async by AlexPublic (правда, вперемешку с
замечательными идеями нашего же Евгения. Кастую обоих в эту тему.)
Boost.Coroutine
неюзабельна под Windows (в моём случае крэшится GetModuleFileNameW по той же причине).
К счастью, есть библиотека Boost.Coroutine2, которая под Windows может работать через fibers (включается макросом BOOST_USE_WINFIBERS, после чего GetModuleFileNameW перестаёт падать).
Казалось бы, живи, да радуйся, но поведение boost::coroutines::coroutine<void> отличается от boost::coroutines2::coroutine<void> тем, что первая yield-ит в вызвавшую корутину, а вторая — в создавшую!
Т.е. вот у нас две корутины. Первая корутина создаёт вторую. Вторая доходит до своего await-а и идёт отдыхать в GUI очередь. Первая корутина тоже выполняется до await и тоже попадает в очередь. Все спят, кроме главного потока (он к тому времени, кстати, уже сам корутина, потому, что при BOOST_USE_WINFIBERS делается ::ConvertThreadToFiber) — он достаёт первую корутину, которая снова работает до await-а, после чего засыпает! Куда после этого вернётся управление? Варианты:
1. Управление вернётся обратно в message loop потому, что он и вызывал первую корутину или
2. Управление вернётся во вторую корутину потому, что она создавала первую.
Как правильно?
Для Boost.Coroutine правильный ответ — первый, для Boost.Coroutine2 — второй.
Вот код — в первой строчке выбирается версия библиотеки. Этот же код
в более простом виде (wandbox его почему-то не может слинковать, под Windows — работает).
g1g1g1g1g1g2g1g2g1g2g1g2g1g2g1leaving coroutine 1
g2g2g2g2g2leaving coroutine 2
Здесь вызовы корутин (1 и 2) строго чередуются с вызовами g(ui), а после завершения первой — вторая нормально дорабатывает.
g1g1g1g1g1g21g1g21g1g1leaving coroutine 1
Assertion `(false)&&("pull_coroutine is complete")' failed.
Здесь видно, что из 2 мы попадали в 1, а после полного завершения первой — срабатывает assert.
Даже если бы не assert — вторая корутина вызывается до того, как поток GUI достанет её из очереди и она себя в очередь кладёт повторно (пока память в очереди не кончится).
Собственно, вопрос, это баг Coroutine2? Если нет, то как с этим поведением бороться?