Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>А зачем копировать внешний мир?
Ну потому что корутина нужна не для галочки, а для обслуживания сигналов из внешнего мира.
V>>Т.е. какова семантика происходящего при этом
EP>Копируются/перемещаются все созданные поля плюс индекс текущего состояния этого конечного автомата.
Т.е. хендл сокета тоже копируется? А дальше как?
Вот мы начали асинхронную операцию, после этого вошли в "ожидание", то бишь, сделали этой корутине yield, потом сделали копию корутины. Теперь обе корутины "ждут" один и тот же сокет, но на самом деле, ждет только одна — экземпляр которой был зарегистрирован как обработчик события. Вторая корутина представляет из себя бессмысленный набор байт.
V>>и для чего это всё?
EP>Например для того чтобы использовать обычные правила автоматических/stack объектов C++, а не аллоцировать кадр под корутину.
О каком "стеке" идет речь? После создания корутины надо выйти из текущего уровня стека, т.е. вернуть управление вызвавшему коду через future. А корутина путь живет себе и реагирует на события, продвигая как автомат своё тело. Это про stackless. Фишка в том, что stackless корутина шагает по своим состояниям в общем случае в РАЗНЫХ потоках, из которых события приходят по push. Т.е., вот есть две асинхронные операции внутри такой корутины, оповещение по каждой из них продвигает корутину-автомат, но это оповещение приходит для каждой операции из другого потока. Поэтому, я не очень понимаю, что значит "правила автоматических объектов" для этого сценария.
EP>Для того чтобы можно было легко передавать её из одного места в другое.
Как раз по указателю она передаётся легко.
То, что ты говоришь — оно интересно только для генераторов/трансформаторов и только для модели poll, типа как IEnumerable<>/yield в дотнете.
Поверь, даже в дотнете это наглухо совсем другое, чем async/await, хотя происходящее при этом как бы близкое. ))
EP>Для того чтобы генераторы (т.е. yield value) — были ForwardIterator а не single pass InputIterator.
Это решается оберткой-итератором один раз и для всех таких корутин. Спасибо за уточнение, я хоть понял, куда ты клонил.
Я думаю, что тут лучше не рассуждать о корутинах вообще, а применить этот подход прямо по назначению — развить концепцию итератора.
Потому что никакой универсальности, а отличие от resumable-ф-ий. Потому что только poll, и потому что только обязательный внешний шедуллинг, управляемый дынными, который уместен как раз в концепции итератора (оно же генератора/трансформатора) и нигде более.
EP>Для того чтобы можно было такую корутину сериализовать по сети.
Ну это сфероконские мечты. Как ты будешь сериализовать корутину, которая обрабатывает сокет?
Я же говорю, упомянутое тобой — совсем отдельный класс задач.
EP>>>future тут вообще не причём, stackless coroutine как фича языка должна брать код/функцию обычного вида и трансформировать её в класс-автомат, где поля это переменные из тела функции, а состояния — точки останова yield. В C# yield именно таким образом и реализован.
V>>Если брать C#, то там для аналогичного служит async/await, где async является аналогом resumable из proposal для C++17. В обоих случаях сигнатуры async/resumable возвращают идентичный по назначению объект — Task/future.
EP>Сигнатура тут тоже не причём.
Увы, это принципиально. Без future нет await, без await нет возврата управления вызывающему коду (в дотнетном Task и плюсовых TBB/PPL — это потоковая процедура рабочего потока из пула потоков, где тело этой потоковой процедуры выполняет work stealing алгоритм, т.е. по await происходит возврат управления шедуллеру).
Понимаешь, в resumable-методах не мы делаем этому методу resume (как в случае с итератором/генератором/трансформатором), а именно что future в момент изменения своего состояния.
Я же говорю, это модель push vs poll.
EP>>>future и подобное появляются только в частных случаях корутин применения типа await.
V>>Дык, именно для await это всё, для этой самой точки автоматической передачи управления следующей задаче из очереди задач текущего потока.
EP>Для этого в том числе. НО, это также достигается другими, лучшими методами.
Пока никто не показал.
Не приводи как аргумент stackless корутины из буста, плиз )
Потому что это фигня в сравнении с await, если честно.
Потому что никакого контроля со стороны компилятора. Потому что одним неверным чихом можно всё поломать.
Я реально игрался с корутинами на дотнете еще до async/await, делал их на IEnumebrable/yield. Очень быстро уперся в то, что адекватно эта корутина работает только на одном уровне. Но я ж, типа, не сдался (10 лет назад было), начал городить кучу хелперов для автоматического "ныряния" итератора внутрь за логикой yield во вложенные ф-ии (тоже итераторы, ес-но) и быстро обнаружил, что ВСЕ вызовы, ВСЕ анонимные методы (лямбды) надо покрывать подобными хелперами, изобретая в итоге новый язык программирования с конским синтаксисом. ))
И да. Одним неверным чихом можно всё поломать и доооолго будешь искать — где.
На future/await поломать что-либо намного сложнее... И все-равно, по опыту дотнетного Task/await — даже в таких "тепличных" условиях народ с БОЛЬШИМ трудом рожает асинхронные алгоритмы. И практически никогда и ни у кого это с первого раза не работает. Се ля ви. ))
EP>Вот как раз в stackless корутинах из Boost.Asio и показано как это может быть реализовано по другому — там не нужно вызывать аллокатор для корутины, это просто класс, объект которого можно создать любым из возможных способов, его можно скопировать, перемещать, сериализовать и т.п.
Одна тонкость — это всё не нужно, в тех сценариях, для которых, собсно, корутины разрабатываются.
EP>Мне тут скинули ссылки, оказывается автор Boost.Asio сделал другой proposal N4244 — он как раз основан на дизайне stackless coroutine из Boost.Asio.
Это стоит назвать чем-то вроде итератора, дабы не вводить людей в заблуждение.
EP>Я надеюсь что proposal от Microsoft не пройдёт, а пройдёт от автора Boost.Asio.
Чур тебя))
Это ортогональные техники для непересекающихся сценариев.