Pull to refresh

Comments 109

В своё время плотно занимался внедрением асинхронного программирования на С++. В те времена Asio ещё только зарождался и до Boost'а ещё требовалось пройти значительный путь. В принципе, уже тогда для себя выработал принципы, которые через время начал видеть в рекомендациях к различным реализациям Promise на JS.

Первое о чём не стоит забывать во всей этой истории, что coroutine в оригинале — это фича виртуальных машин, которые могут в одном потоке операционной системы делить время на несколько потоков абстрактного машинного кода или скрипта.

Всё остальное — это искажения термина и лишь ещё один интерфейс для Thread Pool. Быстрый поиск выдаёт, что в MSVС реализовали таки через треды, что неудивительно.

Пробежал глазами упомянутое предложение, не увидел упоминания о потоках, как контролировать всю подноготную runtime в этом контексте. Непонятно как всё это будет дружить с Thread Local Storage и т.п. Без тонкого контроля за всем этим делом на уровне интерфейсов стандартной библиотеки, в крупных проектах это станет просто запрещённой фичей, а-ля goto, все и дальше будут использовать Reactor/Proactor/ThreadPool и прочие паттерны. В документе нет и упоминания слова "thread" (по крайней мере так говорит поиск в Okular).

Предложение пока сырое и неполное. Поэтому согласен с эмоциями экспертов, когда такое пытаются пропихнуть как можно быстрее.
Всё остальное — это искажения термина и лишь ещё один интерфейс для Thread Pool. Быстрый поиск выдаёт, что в MSVС реализовали таки через треды, что неудивительно.

Тогда мало смысла в этой реализации.

Пробежал глазами упомянутое предложение, не увидел упоминания о потоках, как контролировать всю подноготную runtime в этом контексте.

Кхм… неужели в этом предложении нету описания реализации?
Моё мнение: в корутинах потоков быть не должно. Должен быть диспетчер выполнения кода.
Я не увидел в этой статье что реализация использует Thread Pool. Да и по смыслу невидно откуда там могло бы они появиться.
Функция с co_await возвращает просто future, а кто ставит в нее результат уже не важно будь то какое-нибудь асинхронное API ОС например.
По этому поводу уже высказался ниже, действительно в статье прямо этого не было сказано, но возвращаясь к вопросу о классических coroutine — они предполагают поведение потоков — т.е. асинхронное выполнение, а не относительно детерминированное выполнение по вызову генератора.

Логика простая:
а) если асинхронно выполняется нативный код, то это по нормальному должен быть ещё один поток (при условии, что не используются паттерны вроде реактора)

б) если переключение детерминировано в точке синхронизации, то эту уже неполноценная корутина, а какой-то дополнительный геморрой в реализации языка. Обычные генераторы или асинхронные запросы и так уже легко реализуются без специальной поддержки языка и сомнительных манипуляций со стеком.

в) если подразумевается полноценное деление времени внутри одного потока ОС — это означает необходимость планировщика и механизма переключения, не говоря уже о всех второстепенных вопросах. Зачем делать ОС в ОС непонятно. Намного безопаснее улучшать существующие механизмы потоков добавлением "сверхлёгких зелёных" через стандартные интерфейсы. От этого смогут выиграть значительно больше технологий, в т.ч. и простой C.

С такими изменениями я сразу думаю о порождённой волне изменений в дебагерах, механизмах динамической защиты, разнообразного рода инструментов обработки и анализа бинарников.
Асинхронность — это не многопоточность. Асинхронность подразумевает, что поток для выполнения предоставляется тот библиотекой, которая предоставляет базовые асинхронные вызовы.

Да, в Windows многое в подобных библиотеках завязывается на пул потоков. Но это делается просто из-за удобства, такая реализация не является обязательной.

По поводу вашей логики.

а) Именно "паттерн вроде реактора" внутрях и используется. async/await — это присыпанный сахарком паттерн Proactor.

б) Оператор await можно реализовать поверх генераторов, только синтаксис будет корявый. Именно так, кстати, асинхронность и реализована на Питоне. Но на Питоне есть декораторы, которые этот самый синтаксис упрощают, в С++ реализация асинхронности на генераторах потребует применения макромагии, что тоже нехорошо.

в) Нет, никакого деления времени внутри потока ОС не подразумевается.

PS Забавно, но все ваши аргументы одинаково применимы сразу к двум реализациям короутин — и к async/await от Майкрософта, и к Boost.Coroutines.
Асинхронный вызов с точки зрения приложения действительно не многопоточен и даже на уровне операционной системы может быть реализован без отдельного потока обработки, а вот непосредственно сама корутина в идеале представляет из себя собственный поток исполнения, который синхронизируется с основным только в определённых точках.

Например, моя изначальная ссылка на MSDN показывает как простое сделать сложным, а именно вызвать sleep через 20+ дополнительных строк кода. Так демонстрируют достоинство async/await, куда уж дальше? Ах да, "Hello World" посимвольно через генератор.

Если использовать Reactor или Proactor, то нигде не должно происходить блокировки. Делать async/await на таком принципе на всю программу — это дать обезьяне гранату (привет UI треды).
По любому должен быть отдельный поток, т.к. обычная программа, которая сделала async call не может быть потоком обработки и имеет полное право вызвать какой-нибудь блокирующий вызов ещё до того, как дойдёт до await, а все вложенные async и await кто-то должен тем временем обрабатывать.

Меня сначала сбили с толку немного, но всё же нашёл, с чего изначально взял, что MS реализовали всё именно на Thread Pool'ах — это ссылка внутри приведённой мной ранее ссылки. Там есть такая фраза "Imagine a case where an invocation of a coroutine immediately schedules it to execute on a thread pool and yields control back to the caller. ". (Не исключаю, что фраза может быть вырвана из контекста, не углублялся).

В некоторых другим местах тоже видел отсылки на Thread Pool. Как дело обстоит на самом деле — не знаю, но ввиду опасности обезьяны с гранатой (ни в коем случае не хочу обидеть разработчиком под MSVC), наверняка всё же Thread Pool с возможной оптимизацией в том же потоке для генераторов.

Даже если бы там сделали грамотный Reactor, может даже с дополнительными рабочими процессами, в документации нет ни слова о контроле над ним.

P.S. Вот представьте, я скажу: "выкидывайте все ваши Promise и включайте FutoIn AsyncStep в стандарт ECMAScript". Вот примерно так сейчас выглядит продавливание async/await.
А ведь концепция AsyncSteps разработана с прицелом на практически любой язык с анонимными функциями, в т.ч. и C++.
Напротив, у вас есть полный контроль над потоками. Кто пишет проактор — тот и создает поток.
Об этом и речь, раз Proactor реализован внутри компилятора и его runtime библиотек, то без интерфейсов управления получается неуправляемый чёрный ящик.
Да с чего вы взяли, что Proactor реализован внутри компилятора и его рантайма? Proactor делается в прикладной библиотеке, компилятор реализует лишь сахар для "присыпки". В рантайме же (я посмотрел) — вообще никакого кода, весь заголовочный файл — просто шаблонная магия.
Вы сами себе противоречите. Теперь говорите обратное.

а) Именно «паттерн вроде реактора» внутрях и используется. async/await — это присыпанный сахарком паттерн Proactor.
Я писал с точки зрения прикладного программиста, который пользуется уже готовыми прикладными библиотеками.
В июле 2015 на C++ User Group Meeting был доклад Александра Фокина из Яндекс на тему текущего состояния "Resumable функции в C++".
Там он сообщает, что "честной" реализации coroutines в Visual Studio 2015 RC нет, а сделано на fibers.

С 14-й минуты видео со слов "Теперь плохие новости...".

К 2017 году MS может и запилят "честную" реализацию без Thread Pool и fibers. Хотя для реализации в GCC и Clang потребуется еще время и испытания могут вылететь из графика C++17.
Всё остальное — это искажения термина и лишь ещё один интерфейс для Thread Pool.

Наверху же была ссылка на Youtube с CppCon 2015. Гор Нишанов рассказывает об истории корутин. Похоже это то, что он читал на С++ Russia 2016.
Вкратце:
есть реализация за счет подмена стека( Boost.Coroutines/Fibers/goroutine);
есть реализация за счет встраивания state machine в функцию. Для этой реализации предлагается поддержка её компилятором.
В приведенном вами примере используется таймер, а не поток.
Не захотелось тратить час время на видео, начинающегося с опасного стиля кода и предложения использовать генератор там, где это не требуется (получить изначальную строку извращенским способом и вывести с большими накладными расходами).

По поводу Thread Pool в реализации MSVC возможно погорячился, но интерфейс в Boost для схожей задачи откровенно более продуманным выглядит.

Спасибо за слайды, стало понятней.
Да он на рассказывал на C++ Russia 2016, один из лучших докладов конференции если не самый лучший.

Кстати он рассказывал про минусы предлагаемого подхода в Resumable Expression, от части ответ насколько я понимаю описан здесь http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0171r0.html
Не согласен с искажением термина coroutine. На компилируемом языке вполне возможны корутины. Тот же boost:coroutine или даже go. Абстракция та же самая: системных потоков мало, реальных потоков исполнения с независимыми стеками много. Это же не просто таски, обрабатываемые пулом потоков, это полноценные корутины, которые можно приостановить на середине, а потом продолжить с того же места. А поток может быть и один.
Однако предложение действительно сырое, не надо тащить чужеродные непродуманные концепции в язык. Похоже MS форсит свою реализацию, даже в свой компилятор поддержку добавили.
Похоже MS форсит свою реализацию, даже в свой компилятор поддержку добавили.
Это у них в ДНК.

Разработка в Microsoft обычно выглядит так:
1. Придумали X.
2. Реализовали X.
3. Задокументировали X.
4 (опционально). Стандартизовали X.

То есть стандартизация — это такой более жёсткий вариант документации: документацию Microsoft может время от времени менять, а стандарт — он визируется раз и навсегда. Когда релизация и стандарт расходятся — правится всё равно стандарт.

Это вовсе не значит, что нужно этому подчиняться и включать это предложение в C++, это я просто к тому, что никто особенно жёстко эту фичу не насаждает, это просто нормальное поведение для Microsoft'а.

Когда-то ему что-то подобное сделать не разрешили он «великий раскол» в IT-мире устроил и C# вместо J++ создал.

А вот в C++ мире — не удалось: Managed C++/C++/CLI/C++/CX имеют (слава богу) весьма ограниченное распространение.

Будем надеяться на благоразумие комитета по стандартизации…
Managed C++/C++/CLI/C++/CX имеют распространение там где они нужны, они создавались для определенных целей и для них и используются.
Совершенно верно. Вот только частью ни C++11, ни C++14 они не являются. А обсуждаемая здесь фича, в общем-то, нужна для тех же целей — но почему-то должна стать частью C++17. Где логика?
Для каких целей? Managed С++ никогда не предлагался на рассмотрение, это extension который работает под свою платформу. У GCC и Clang такие тоже есть. И?

await это стандарт работы с асинхронным кодом. А теперь объясните где вы видите связь между стандартом и расширением? Или это ненависть к MS?
await — это расширения удобной для работы с WinRT. И если бы они позиционировались в таком статусе — то проблем бы не было.

Но они позиционируются как стандарт работы с асинхронным кодом. Причём стандарт настолько важный и базовый, что его должны реализовывать все. На всех платформах.

И вот против этого — как раз все возражения.
Этот оператор как бы появился задолго до WinRT!
Это в каком-таком мире Visual Studio 2015 появился до WinRT?

Да, эксперименты и разные всякие игры с ним в разных языках (не в C++!) производились и раньше, но WinRT сделала интерфейсы, которыми страшно неудобно пользоваться не имея поддержки со стороны языка, частью системы.

Обнаружив, что они в этом увязли они решили, что самое лучшее — заставить с эти возиться всех и везде.

Что, может быть, даже и правда — но подобного рода изменения заслуживают больше обсуждения и тестирования перед тем, как все-все-все во всех-всех-всех библиотеках начнут всё-всё-всё переделывать.

Хватит с нас Python3, не стоит устраивать подобное же в C++. Тем более когда мотивация — поддержка API одной проприетарной системы.
А этот оператор впервые и не в плюсах появился. Об этом даже в обсуждаемой статье написано.
Согласен — плохо выразился. Думал, что будет понятно из контекста (Managed C++ ⇒C++/CLI ⇒ C++/CX ⇒await in C++17).

Я, конечно, имел в виду "await в C++", а не какой-то абстрактный "await где-то там фиг-знает где" (который, почти наверняка, у кого-нибудь на транспьютерах в 80е появился на каких-нибудь) — то, что, собственно, обсуждается.
Я правильно понял, что если бы это же решение пришло со стороны не МС никаких вопросов бы не возникло? МС очень не плохо чувствует себя выпуская расширения для языков, проблем с тем, что это расширения у разработчиков под МС нет.
Ну насчёт "никаких вопросов бы не возникло" — это, конечно, преувеличение, но да, разумеется. Подход был бы другой.

Есть такое понятие, как репутация. И у Microsoft'а она вполне определённая: для него "стандартизация" — это никак не попытка совместно что-то сделать и потом следовать разработанному (что, собственно, обычно предполагается от компаний, участвующих в стандартизации).

Это может быть попытка получить шилдик (когда что-то "за уши" дотягивают до уровня стандарта, а как только "шилдик" получен про всё это забывают — см. OOXML), либо отвлекающий манёвр (когда всем на уши вешают лапшу про "смовместные стнадарты", которые будут описаывать то, как будет "устроен мир", а тем временем сами разработывают нечто совсем иное — см Fahrenheit), либо ещё какой-нибудь способ использовать наивность конкурентов. Может быть всё, что угодно, кроме одного: желания совместно разработать стандарт и в дальшейшем ему следовать (хотя в последнее время начались подвижки в эту сторону среди разработчиков браузера).

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

И вот только если после применения этих двух предположений предложение кажется разумным и полезным — его стоит рассматривать.

P.S. При этом сам лично Гор Нишанов может хотеть как угодно искренне помочь C++, но ясно, что это ничего не меняет: все права на разработки в компиляторе MSVC принадлежат не ему, никто, кроме Microsoft'а их использовать тоже не может, а когда и что с ними решит делать Microsoft — никому не известно.
Вы, простите, пейперы наискосок читаете? Или комитет, за которым последнее слово, у вас просто так, для красоты?

Я вам пример приведу из пейпера

This work is the result of collaboration of researchers in industry and academia, including CppDes Microsoft
group and the WG21 study group SG1. We wish to thank people who made valuable contributions within
and outside these groups, including Jens Maurer, Artur Laksberg, Chandler Carruth, Gabriel Dos Reis, Deon
Brewis, Jonathan Caves, James McNellis, Stephan T. Lavavej, Herb Sutter, Pablo Halpern, Robert Schumacher,
Michael Wong, Niklas Gustafsson, Nick Maliwacki, Vladimir Petter, Shahms King, Slava Kuznetsov,
Tongari J, Lawrence Crowl, and many others not named here who contributed to the discussion

Все эти люди безусловно работают в Microsoft и\или получают оттуда деньги. А как иначе?

Структуру комитета и глав его подразделений вы можете посмотреть вот здесь. Обратите внимание на главу SG1.

Есть понятие formal proposal — без полного обоснования нового функционала и его реализации и концептов, с вами общаться никто не будет. Сами же эти бумаги после внедрения в стандарт становятся собственностью ISO C++. О каких разработках вы говорите, я не понимаю.

Поведение плюсов стандартизировано. Вне зависимости от компилятора. Все что не входит в стандарт и не является частью TS не поощряется в использовании, в любом виде.
Мне совершенно пофигу — как именно Microsoft создаёт шумиху вокруг своих предложений. Заносит деньги в конверте или, может, у него просто карма такая.

Мне это по-фи-гу. Все ваши доводы уже один раз сыграли: были и поддержка независимых разработчиков и куча компаний вдруг к форматам хранения документов воспылала любовью.

А потом OOXML стал стандартом — и всё это растворилось в тумане. С этого момента все предложения Microsoft рассматриваются в ключе этого прецедента.

Справедливо это или нет — я не знаю. Но Microsoft знал, что так будет и, тем не менее, на это пошел в 2008м году. Ну что же — время пожинать плоды.
await в том виде, в котором он предлагается появился в бумаге предложенной на рассматривание комитета

А обсуждение подобного функционала шло еще в 2013 когда был предложен концепт.

И ресерч датируемый 2011 годом.

Вы пока не привели ни одного аргумента кроме
await был сделан для WinRT. WinRT плохой, следовательно и await плохой.

У вас есть опыт работы с Boost.Coroutine? Ограничения которые он накладывает? Спецификации того как он работает? И тот факт, что вариант Кристофера не решает проблему с блокировкой не подготовленных методов?

vector тоже плохой — это не повод убивать шаблонный класс vector как сущность.
Сопрограммы в стиле C# await — во многом результат простого копирования синтаксиса из других языков. Скопировали и запустили в работу. При этом были скопированы и ошибки и недостатки сопрограмм из этих языков (C#, Python).

А есть гденить конкретика? т.е. какие конкретно ошибки из этих языков были унесены?

В этой критике много воды. Даже не знаю… самое существенное это то, что это инвазивный вариант и лучше было бы назвать служебное слово какнить типо std_await. Мне как пользователю Python который часто мучает Tornado и Asyncio всё кажется логичным.
типо std_await

На C++ Russia Гор Нишанов говорил, что будут добавлены ключевые слова co_await, co_return и co_yield, и рассказывал, почему именно так.
Тогда особых проблем не вижу.
Мне как пользователю Python который часто мучает Tornado и Asyncio всё кажется логичным.
А мне, как пользователю C++ «на железе» — ни разу.

Как это взаимодествует с `__thread`? А с `tgkill`/`sigaction`? Что мне потребуется сделать в RTEMS чтобы это использовать? Ах, вы не знаете и вас это не волнует — ну это ваш выбор. Только не суйте это сырое и недодуманное поделие в стандарт.

Одно дело — сделать фичу, которая реализуется в виртуальной машине поверх ограниченного рантайма, другое — придумать нечто, что должно работать на всём, начиная со стиральных машин и марсоходов, и кончая суперкомпьютерами.
А мне, как пользователю C++ «на железе» — ни разу.

Ну и используйте C++14 дальше, С++17 никто не заставляет использовать.

Как это взаимодествует с `__thread`? А с `tgkill`/`sigaction`? Что мне потребуется сделать в RTEMS чтобы это использовать? Ах, вы не знаете и вас это не волнует — ну это ваш выбор. Только не суйте это сырое и недодуманное поделие в стандарт.

Думаю особых проблем с этим не будет.

Одно дело — сделать фичу, которая реализуется в виртуальной машине поверх ограниченного рантайма, другое — придумать нечто, что должно работать на всём, начиная со стиральных машин и марсоходов, и кончая суперкомпьютерами.

Именно там асинхроность очень часто встречается. Тут же просто синтаксический сахар, что бы избежать callback-hell.
А мне, как пользователю C++ «на железе» — ни разу.
Ну и используйте C++14 дальше, С++17 никто не заставляет использовать.

Нифига себе заявочки. Хорошо что не вы эту фичу пытаетесь в комитет по стандартизации продвигать. Потому что одно такое заявление само по себе было бы достаточно чтобы фичу "зарубить" раз и навсегда. Независимо от каких-либо других аргументов.

Как это взаимодествует с `__thread`? А с `tgkill`/`sigaction`? Что мне потребуется сделать в RTEMS чтобы это использовать? Ах, вы не знаете и вас это не волнует — ну это ваш выбор. Только не суйте это сырое и недодуманное поделие в стандарт.
Думаю особых проблем с этим не будет.
«Думаете» или знаете? Или опять: «я хочу эту фичу, что будете делать вы — меня не волнует?»

Именно там асинхроность очень часто встречается. Тут же просто синтаксический сахар, что бы избежать callback-hell.
Если это «просто синтаксический сахар», то почему это вдруг требует изменений в компилятор вообще? А если это не «синтаксический сахар», то почему вы так уверены, что с другими вещами это предложение не будет конфликтовать?
Если это «просто синтаксический сахар», то почему это вдруг требует изменений в компилятор вообще? А если это не «синтаксический сахар», то почему вы так уверены, что с другими вещами это предложение не будет конфликтовать?
Вот потому оно и требует изменений в компиляторе, что является синтаксическим сахаром.

«Думаете» или знаете? Или опять: «я хочу эту фичу, что будете делать вы — меня не волнует?»
Я — знаю. Я занимался обратным портированием этой фичи в C# на фреймворк 2.0 когда только-только вышел Async CTP. Фича реализуется пользостью переносимым кодом.
Данная фича, насколько я понимаю, довольно тесно увязана с тем, как "устроен мир" в Windows. И то, что фича "реализуется полностью переносимым кодом" не делает её автоматически на MT-Safe, на, тем более, AS-Safe.

В то же время основные потребители — живут в совсем другом мире. Это всё равно как если бы гениальный авиаконструктор создал бы документ, который бы описывал — как будет устроено новое поколение кораблей, а на вопросы отвечал бы: "да не волнуйтесь вы — я с горки пару раз всё запустил… летает".

P.S. На всякий случай: вы вообще понимаете о чём я?
Не вижу связи между этой фичей и мироустройством Windows.
PS если рантайм-часть этой фичи будет реализована поверх существующих MT-safe примитивов — она автоматически станет MT-safe. Точно не знаю, но предполагаю что этим свойством просто обязаны обладать std::atomic-переменные. Так что сделать переносимую MT-safe реализацию несложно.

С AS-safe сложнее, но тоже возможно. Я не уверен, что этим свойством обладают thread_local-переменные. Что произойдет, если сигнал придет посреди записи в такую переменную? Если обладают — то все в порядке. Если нет — то для получения AS-safe реализации придется городить много хаков.
Что произойдет, если сигнал придет посреди записи в такую переменную?
Это-то как раз не страшно. С точки зрения записи-чтения — это обычная переменная.

А вот что будет, если переменной ещё нет — тут, зачастую, обращения к `__thread` как раз и становятся AS-Unsafe.
Нифига себе заявочки. Хорошо что не вы эту фичу пытаетесь в комитет по стандартизации продвигать. Потому что одно такое заявление само по себе было бы достаточно чтобы фичу «зарубить» раз и навсегда. Независимо от каких-либо других аргументов.

Почему?

Ах, вы не знаете и вас это не волнует — ну это ваш выбор.

Я вот даже скорее вот на это ответил. С чего вы решили, что меня не волнует? После такого отношения, мягко говоря не конструктивного так и хочется, что бы эта фича прошла. >_<

«Думаете» или знаете? Или опять: «я хочу эту фичу, что будете делать вы — меня не волнует?»

Oh my god! Я не автор этого пропозла и не автор реализации, я потребитель. О том, что бы эта фича не поломала все остальные компоненты языка должны думать разработчики. Если вдруг технически нельзя безопасно (ничего не поломав) внедрить то я конечно расстроюсь но переживу. С чего вы решили, что я стою на позиции: запихнём эту фичу, а остальное пусть горит синем пламенем?
Ну и используйте C++14 дальше, С++17 никто не заставляет использовать.
Нифига себе заявочки. Хорошо что не вы эту фичу пытаетесь в комитет по стандартизации продвигать. Потому что одно такое заявление само по себе было бы достаточно чтобы фичу «зарубить» раз и навсегда. Независимо от каких-либо других аргументов.
Почему?
По‐моему, это очевидно: предложение «не использовать C++17 на …» при предложении нового функционала для включения в стандарт означает предложение остановить разработку языка на …, потому что следующий стандарт языка основывается на предыдущем. На такую заморозку никто не пойдёт. Предложение сделать функционал необязательным для компиляторов было бы более адекватным.
Я про, то что ещё во многих отраслях используют ANSI C++ и не спешат к новомодным штучкам (иногда для упрощения сопряжения с Си). Т.е. для многих это вполне приемлемый вариант не использовать новые фичи/стандарты. Собственно и khim мог бы просто не использовать async, что бы не боятся на счёт совместимости своих подходов к разработке и технологиями.
Собственно и khim мог бы просто не использовать async, что бы не боятся на счёт совместимости своих подходов к разработке и технологиями.
Если я разрабатываю платформу? Как вы это себе представляете?

Предложение сделать функционал «необязательным для разработчиков компиляторов» — уже лучше, но, собственно, это авторы обсуждаемой статьи и прделагают: сделать это не частью стандарта C++17, а отдельным TS. Типа того же ISO/IEC DTR 18037.

Тогда можно говорить разговоры на тему: «хотите поддерживать и использовать — поддерживайте и используйте, не хотите — проходите мимо».

Но тут же сырую, в общем-то, фичу, пихают «по-быстрому» в общий стандарт!
Если я разрабатываю платформу? Как вы это себе представляете?

Скажите что ваша платформа не поддерживает эту фичу. Я не понимаю вас, поясните.
Поясняю. Ситуация немного схожая с await сложилась в процессе обсуждения стандарта C++11 (тогда известного под именем C++0x).

Там тоже было много конфликтующих предложений на тему концептов и тоже все долго перетягивали одеяло в разные стороны, но в конце-концов потом было принято решение: язык C++ — един. Для всяких экспериментальных есть такая вещь, как technical specification. Там эти идеи могут обкатываться годами — и любой разработчик компилятора вправе решать поддерживать фичу или нет.

А основной стандарт — един. Опциональные фишки в нём — это признания того, что что-то было в него включено по ошибке (VLA-массивы в C11, export шаблонов в C++11).

Потому заявления
Скажите что ваша платформа не поддерживает эту фичу.
автоматически трактуются как
Предлагаемая мною классная фича для включения в стандарт — не готова.
О чём, собственно, авторы обсуждаемой статьи и говорят: если ваша фича кому-то может помочь, а кому-то — не нужна совершенно значит ей в стандарте C++17 делать нечего.
А основной стандарт — един. Опциональные фишки в нём — это признания того, что что-то было в него включено по ошибке (VLA-массивы в C11, export шаблонов в C++11).

С этим я соглашусь, хотя VLA после C99 мне не хватает в C++.

О чём, собственно, авторы обсуждаемой статьи и говорят: если ваша фича кому-то может помочь, а кому-то — не нужна совершенно значит ей в стандарте C++17 делать нечего.

Вот с этим я категорически не согласен.
Правда мне кажется мы просто на это совершенно с разных точек зрения смотрим. Я смотрю как пользователь языка и мне кажется совершенно нормально когда ты в проекте используешь 20-30% возможностей (правило Парето).
т.е. для меня это естественно, что вот в этом проекте я использую эту фичу, а вон в том мне она нафиг не нужна. К примеру есть много кода где даже не пахнет unique_ptr и аналогами.

Правильно я понимаю, что вы разработчик компилятора или stdc++ и у вас есть серьёзные опасения, что без костылей и проблем эту фичу не реализовать? Если так, то это совсем иная история, и тут спорить не хочу.
Однако замечу, что скорее всего есть проблема сопряжения подходов, но тогда всегда можно написать (даже в стандарте) к примеру: внутри await функции не может быть порождение thread и т.д. Хотя может быть это и костыляво.

И всё же, вы часто работали с асинхронынми кодом? У вас был тот самый callback-hell? Просто те кто через это прошли и потом освоили технологии аналогичные asyncio понимают их необходимость и то, что это просто must have.
Синтаксически и логически мне текущее предложение нравится НО не нравится то, что не описана реализация, а то что предложил MS за гранью добра.
Когда слушал доклад, сразу показалось, что добавление новых ключевых слов это не есть хорошо. Это в любом случае порождает проблемы, сложности, грабли. Гораздо лучше представить какие-то инструменты, чтобы можно было подобные штуки делать на уровне библиотек.
Думаю тут могли бы помочь нормальные гигиенические макросы и возможность оптимизатора самому перемещать объект, созданный через new на стек, если он точно может проследить его время жизни.
Как фанат асинхронного программирования в стиле C#, могу в качестве ответа привести возражения против бустовой реализации.

Boost.Coroutines использует модель "suspend-down", которая является тайной. Смотря на прототип функции, невозможно сказать, не начнет ли она переключать контексты. Пропущенное переключение контекста может плохо кончиться — ведь не все функции являются реентерабельными, и другая сопрограмма может вызвать такую функцию пока она выполняется в соседней сопрограмме.

Особенно остро эта проблема встанет в случае взаимодействия библиотек на разных языках — так, среда .NET CLR не является реентерабельной. Если вызвать через P/Invoke нативную библиотеку, которая переключит контекст и сделает обратный вызов — возникнет неустранимая ошибка.

Асинхронные обещания модели "suspend-up" куда легче пробрасываются сквозь код на другой платформе.

Кроме того, реализация Boost.Context платформозависима, ее надо переделывать под каждую новую платформу. Причем платформой тут является комбинация ОС и архитектуры процессора. Модель же async/await реализуется на высоком уровне одинаково для всех платформ.

Кроме того, модель "suspend-down" довольно активно пожирает пространство оперативной памяти под стек. Если для модели "suspend-up" требуется не такое большое число потоков, и для каждого из них можно выделить "обычный" стек, а основная требуемая задачам память будет выделяться из кучи кусками — то в модели "suspend-down" свой стек нужен под каждую задачу, и их придется делать маленькими. А отсюда — высокая вероятность переполнения стека при ошибке в угадывании требуемого размера.
а как же тогда решить проблему с невозможностью применения STL? Придётся просто вторую версию написать специально для корутин?
Вторую версию писать придется в любом случае, и даже не одну. Потому что с переходом на асинхронность возникает возможность параллельного выполнения, что позволяет эти самые алгоритмы улучшить.
STL 2 скорее всего, все равно будет — range v3 Эрика Найбера и концепты. Однако я не понимаю связи между алгоритмами и сопрограммами. Возможность параллелизации алгоритмов скорее Parallelism TS и Concurrency TS. Которые планируются пока скорее к C++20.
Можете вкратце пояснить, в чем разница suspend-down и suspend-up моделей? Загуглить сходу не удалось, а из комментариев не вполне понятно.
Это терминология авторов обсуждаемого документа. Неудивительно, что загуглить не удалось.

suspend-down — это модель, которая предполагает, что вызываемая функция может остановить работу всего стека вызовов. В такой модели с точки зрения языка и компилятора нет никакой разницы между синхронным и асинхронным ожиданием. Реализуется такая приостановка с переключением на другую задачу через подмену указателя стека.

Гуглить по словам "реализация потоков в режиме пользователя" ("userspace threads") или "волокна" ("fibers"), а также "кооперативная многозадачность".

Модель suspend-up предполагает, что асинхронные функции отличаются от обычных тем, что возвращают не значение, а специальную обертку вокруг него, называемую обещанием ("promise") или задачей ("task"). В С++, вероятно, назовут как-то по-другому. Асинхронное ожидание, выполняемое в асинхронной функции, выливается в немедленный возврат из нее (если этого не было сделано раньше) и продолжение работы вызывающей функции, т.е. в данной модели функция может приостановить только саму себя. Вызывающая функция получит эту самую задачу в качестве возвращаемого значения — и сможет использовать ее чтобы подписаться на событие завершения выполнения.

Для того, чтобы программисту не пришлось писать многоуровневые колбеки — придуман синтаксический сахар async/await. Функция, отмеченная как async, преобразуется компилятором в конечный автомат, который выполняется от одного оператора await до другого. Оператор await получает задачу, прерывает выполнение функции и подписывает на событие завершения задачи продолжение текущей функции.
Ох уж эти программисты с их выдуманными терминами. Спасибо.
Если я правильно понимаю, выходит что в suspend-down метод просто отдает контроль пока не задача не будет выполнена и в это время этот же поток не может продолжать работу? То есть если мне нужно загрузить н ресурсов из сети, мне придется это делать поочереди?
То есть если мне нужно загрузить н ресурсов из сети, мне придется это делать поочереди?

Да, но так как большая часть времени уйдёт на транспорт данных по TCP, ваша программа будет заниматься чем то ещё.
По факту выполнение будет только для пред обработки зарпоса и обработки ответа. При этом не будет происходить вредных контекст свичей ядра ОС.
Передачу управления нужно делать явно — собственно этим сейчас и занимается Boost ASIO внутри с помощью асинхронных примитивов ОС. Вроде есть ASIO на корутинах, но я не смотрел.
Ну я скорее про это и говорил.
Я почти каждый день работаю с Tornado и хорошо понимаю именно их подход, а он именно за ручную асинхронность.
> возвращают не значение, а специальную обертку вокруг него, называемую обещанием («promise») или задачей («task»).

Обычно немного не так. Promise (≈ TaskCompletionSource) асинхронная операция оставляет себе. А отдаёт соответствующий Future (≈ Task), который является, грубо говоря, read-only view для обещания.
Как его назвать в C++ — еще не определились. Видел варианты и future, и task, и awaitable. А в последней версии так вообще эту задачу "сбросили" на прикладную библиотеку: как хочешь, так и называй. Т.е. "окончательное" название мы увидим только когда поддержка await появится в boost :)

Я приводил примеры из других языков.

В Javascript такая пара объектов для передачи сигнала называется deferred / promise (в последнем стандарте от deferred отказались вовсе).

В C# — это TaskCompletionSource / Task

В стандарте C++ сейчас действительно присутствует пара promise / future — но ее в текущем виде для асинхронного программирования использовать невозможно (это была одна из претензий к реализации await в студии — там вместо исправления future решили тихонько создавать отдельный поток для ожидания).
UPD: еще один аргумент.

В модели async/await оператор await, отработавший в потоке UI, освободит поток, возобновив цикл обработки оконных сообщений.

При использовании сопрограмм буста любое ожидание с переключением контекста, выполненное в потоке UI, остановит цикл обработки сообщений столь же хорошо, как это мог бы сделать блокирующий вызов...
Для этого оператору await нужно знать слишком много — о том, что бывает UI и не-UI поток, о том, что он выполняется в UI потоке и о том, что нужно выполнить специальный код, который «прокрутит» очередь сообщений (для каждой ОС разный).
Это решается одним вызовом библиотечной функции, которая запишет переданный ей обработчик обратных вызовов в thread_local-переменную.

Оператору await ничего не нужно знать про UI-потоки. Ему нужно знать лишь где лежит обработчик, который позволит асинхронному потоку выполнения вернуться в исходный контекст.
Синхронное ожидание (suspend-down) с переключением контекста — оно где угодно может возникнуть.
Глядя на прототип функции, нельзя сказать, что внутри она не делает select(), sleep() или хотя бы printf().
Даже виндовский SendMessage может устроить цирк с переключениями контекстов.

Мы же не в хаскелле, чтобы на уровне типов отличать IO-функции от всех остальных.

По большому счёту, как только мы открываемся для многозадачности, — сразу же приходится мыть руки с мылом на предмет реентерабельности. Неважно, на процессах, потоках, волокнах или даже на продолжениях (suspend-up). Просто разный уровень изоляции, разные сценарии погромов.

Так что — это не показатель.

А вот возможность сделать машину состояний не своими руками, а силами компилятора — это круто.
Не могу не согласиться, что реализации на основе Boost.Context мягко говоря имеют не меньшее количество проблем.
1) Про платформенно-зависимость было упомянуто. Список поддерживаемых архитектур довольно скуден. Сохранение стэка с регистрами никогда не заработает для экзотических платформ, типа Emscripten (компиляция C++ в asm.js).
2) Boost.Context не решает проблему "параллельности" стандартной библиотеки.
Список поддерживаемых архитектур довольно скуден.
А чего вам не хватает? Sparc'а? Itanic'а? Для них кто-то что ещё разрабатывает?

В любом случае поддержать их можно — было бы желание. В любом случае это лучше, чем один компилятор для одной архитектуры и одной OS.

Сохранение стэка с регистрами никогда не заработает для экзотических платформ, типа Emscripten (компиляция C++ в asm.js).
А на этих «экзотических платформах» реально кто-то что-то пишет? И кто, собственно, помешает вам реализовать поддержку соответствующий абстракций для них? Тот же Emscripten эмулирует всё на свете поверх одного массива — совершенно непонятно что может помешать реализовать Boost.Context для него. Ну кроме отсутствия необходимости, тут всё понятно.

2) Boost.Context не решает проблему «параллельности» стандартной библиотеки.
Что вы имеете в виду? Вы вполне можете использовать стандартную библиотеку с Boost.Context — кто вам помешает.

P.S. Всё вышесказанное не означает, что Boost.Context нужно включать в стандартную библиотеку. Всё-таки сырое оно. Не настолько сырое, как этот самый `await` — но сырое.
В любом случае поддержать их можно — было бы желание.

Поддержать на уровне буста? То есть вместо того, чтобы поддерживать на уровне рантайма и компилятора будем для каждой пары компилятор-платформа добавлять что-то в буст? Как по мне это путь в никуда.

А на этих «экзотических платформах» реально кто-то что-то пишет?

Представьте себе да. Точнее это одна из платформ, под которую собирается код.

И кто, собственно, помешает вам реализовать поддержку соответствующий абстракций для них? Тот же Emscripten эмулирует всё на свете поверх одного массива — совершенно непонятно что может помешать реализовать Boost.Context для него.

… Используя сакральное знание о деталях реализации Emscripten. И если вдруг подход поменяется (например, в сторону JS objects как у альтернатив), то внезапно все перестанет работать. Собственно в этом и есть главный изъян подхода, когда библиотека берет на себя platform-specific вещи.
Поддержать на уровне буста? То есть вместо того, чтобы поддерживать на уровне рантайма и компилятора будем для каждой пары компилятор-платформа добавлять что-то в буст? Как по мне это путь в никуда.
Ну если миллиарды устройств и пользователей это для вас «никуда» — тогда да, наверное.

Представьте себе да. Точнее это одна из платформ, под которую собирается код.
И? Код можно собрать и под PDP-10 и под C64. Вот недавно какую вещь написали для CGA написали. Причём тут Boost и C++11/14/17? Будем теперь ради двух с половиной разработчиков всех остальных заставлять делать невесть что?

И если вдруг подход поменяется (например, в сторону JS objects как у альтернатив), то внезапно все перестанет работать.
Ну значит у пользователей возникнет вопрос: то ли отказаться от «супер-пупер» версии Emscripten'а, либо от Boost.Context.

Собственно в этом и есть главный изъян подхода, когда библиотека берет на себя platform-specific вещи.
Что совершенно не значит, что этот подход не имеет права к существованию. Вот тут советуют обратить внимание на председателя SG1. Рекомендую вам это сделать, а потом подумать где вы слышали это имя.


Да, для пользователя, конечно, удобнее когда подобные вещи являются частью языка. Однако если уж вы так наставиваете на то, чтобы поддерживать всякую экзотику, то вспомните про всякие DSP и прочие всякие Arduino, которые практически на порядок (а то и на два) более релевантны при обсуждении стандартов C++, чем какой-то никому непонятное мертворождённое творение типа Emscripten'а. Честное слово: возможность запустить DOS для того, чтобы поиграть в игрушку, выпущенную лет 30 назад не стоит того, чтобы ради неё корёжить стандарты.
Ну если миллиарды устройств и пользователей это для вас «никуда» — тогда да, наверное.

Если верить статистике доля Windows на рынке декстопов порядка 90%, давайте поддерживать только ее, ведь это "миллиарды устройств и пользователей".

И? Код можно собрать и под PDP-10 и под C64.

Собирается и успешно работает.

двух с половиной разработчиков

мертворождённое творение типа Emscripten'а

Приятно говорить с объективным человеком, не склонным к холивару.

чтобы ради неё корёжить стандарты

Пользователи других языков с async/await не считают, что их языки покорежили. Поддержка таких вещей как асинхронность, рефлексия и прочее без помощи рантайма никогда не будет эффективной, и рано или поздно появится.
И? Код можно собрать и под PDP-10 и под C64.

Собирается и успешно работает.

О как.

чтобы ради неё корёжить стандарты

Пользователи других языков с async/await не считают, что их языки покорежили. Поддержка таких вещей как асинхронность, рефлексия и прочее без помощи рантайма никогда не будет эффективной, и рано или поздно появится.

Программу с async/await для C64 — в студию. Я ведь такой, да — у меня C64 есть, я проверю.
Я совсем не понимаю, в чем проблема с реализацией async/await для всякой экзотики. Будет точно так же, как уже есть для std::async — если платформа не позволяет, то код будет просто синхронным.
Вы вообще обсуждаемые возражения читали? В том-то и дело, что код не будет "просто синхронным". Код будет "просто неработающим".

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

Но стоит мне присобачить к моей железяке аппаратный генератор случайных чисел, который "всегда готов" — и всё, процедура пересылки случайных данных в последовательный порт займёт процесср — и уже никому его не отдаст.

Как это всё рализовать для подобных систем — точно неясно, но однозначно не так, как написано в пропозале: пока реализация всего этого безобразия сделана поверх потоков, волокон и т.п. — всё работает, как только у нас однопоточная система — всё встаёт колом.

Собственно реализации пропозала в природе нет: он обещает сопрограммы, которые, понятно, полезнее всего на платформах без "настоящих" потоков, но как это всё будет работать на подобных платформах — неясно. Если прямо по пропозалу — никак не будет работать.
Вот как раз на платформах без потоков оно и будет работать лучше всего — там просто негде допустить ошибку. Посмотрите как на Javascript работают промизы — там как раз ровно один поток.

Но стоит мне присобачить к моей железяке аппаратный генератор случайных чисел, который «всегда готов» — и всё, процедура пересылки случайных данных в последовательный порт займёт процесср — и уже никому его не отдаст.
Значит, надо всего лишь добавить задержку. Или вставить в цикл инструкцию принудительного переключения контекста (в js такое делается согласно стандарту Promises/A+ для любого продолжения, в C# это делается через await Task.Yield() — значит и для C++ решение найдется).
Зачит, надо всего лишь добавить задержку. Или вставить в цикл инструкцию принудительного переключения контекста (в js такое делается согласно стандарту Promises/A+ для любого продолжения, в C# это делается через await Task.Yield()).
Но в предложении от Microsoft ничего такого нету! И в реализации нету! Ни короутин (то, что там называется короутиными реализовано через волокна — механизм, существующий только в Windows), ни поддержки потоков, ни многого другого!

значит и для C++ решение найдется
Вот когда оно найдётся — тогда и будет пора включить предложение в стандарт.

Хватит с нас экспорта шаблонов, зачем в стандарте вещи, которые нигде толком не обкатаны и существуют на уровне «мы точно не знаем как эту проблему решать, но верим, что она разрешима»? Тем более когда реализация предложения в том виде, в каком оно выкачено гарантированно приводит к проблемам.

Куда такая спешка? На ум приходит только очевидный возможный довод: «в бюджет на 2016й-2017й годы у нас заложены расходы на вылизывание этой фичи, а если она не будет включена в стандарт, то финансирование прекратится».

Ну так если так — то тем более стоит подождать пока разработкой займётся кто-то для кого галочка «включено в стандарт» не является самоцелью…
Ну нахера там поддержка потоков-то? Откуда такое желание сначала "прибить гвоздями" эту поддержку, а потом задавать вопросы вроде "как оно будет работать на платформах без настоящих потоков"?

Вот именно потому что там нет ни слова про потоки — оно и будет без потоков превосходно работать!

Что же до волокон — тут я даже предположить не могу зачем такое сделали.
Ну нахера там поддержка потоков-то?
Потому что начиная с C++11 в C++ есть поддержка потоков. И, стало быть, любое предложение расширения языка обязано это учитывать. В том числе объяснять — как это всё будет работать на платформах, где «волокон» нету.

а потом задавать вопросы вроде «как оно будет работать на платформах без настоящих потоков»?
Потому что на таких платформах C++ тоже активно используется.

Вот именно потому что там нет ни слова про потоки — оно и будет без потоков превосходно работать!
Снова «я Пастернака не читал, но осуждаю»? Или, как в данном случае, «поддерживаю»? Не будет оно работать. В обсуждаемом документе подробно написано — почему то, что предложено, работать не будет и почему то, что реализовано таки работает (за счёт вполне определённой реализации на вполне определённой платформе).

Я понимаю, что вам очень хочется иметь await — но одного вашего желания мало.

А предложение в существующем виде — откровенно сырое. Когда в ответ на критику звучат объяснения что «это разработчики библиотки должны всё правильно сделать — а это, что тривиальная релизации оной библиотеки, являющаюся частью предложенного расширения стандарта не работает — это мелочи» у меня возникает желание вообще послать всех нафиг с такими предложениями.

Стандарт с примерами того, как нельзя делать в качестве стандартной реализации — это новое слово в науке и технике.

Но я, слава богу, не член комитета по стандартизации и моё мнение мало чего решает. Члены предлагают вместо того, чтобы предложение просто «зарубить» оформить его в качестве TS, посмотреть как оно развиваться будет, а потом, может быть, и включить в следующую версию C++, если оно будет достаточно популярно.
Я читал вот этот документ — http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0057r2.pdf

Что же до приведенного вами — епрст, я думал, тут его перевод-пересказ опубликован. Но, видимо, что-то важное пересказать автор забыл.

Отвечу по исходному документу позже и уровнем по-выше.
Но, видимо, что-то важное пересказать автор забыл.
Автор, собственно, техническую часть опустил с замечанием «Честно говоря, мне этот пункт показался несколько притянутым. Во всяком случае отдельные детали реализации могут быть исправлены и принципиальных недостатков, присущих модели, не видно.»

Но обсуждается-то не модель с её «принципиальными недостатками», а конкретное предложение! Как могут быть «детали реализации» исправлены, если они уже будут намертво «прошиты в стандарте»?
Но обсуждается-то не модель с её «принципиальными недостатками», а конкретное предложение!

Согласен, возможно слишком увлекся сравнением абстрактных моделей Suspend-up vs Suspend-down, поэтому в тот момент мне показалось, что раз проблемы со scheduling'ом и starvation устранимы (используя yield, spawn, изменением реализации await, как угодно), то это и несущественно.

Но, действительно, рассматривается конкретное предложение о реализации, и это его недостаток.
я думал, тут его перевод-пересказ опубликован

Как я отмечал во вступлении, эта статья скорее дайджест из исходного документа. Цель была привлечь внимание к альтернативной точке зрения на сопрограммы с await (особенно suspend-up vs suspend-down).

До этого я смотрел доклад "C++ Coroutines — a negative overhead abstraction" и ответы Гора на Q&A в Яндексе, там минусы модели suspend-up особо не упоминались. Поэтому для меня точка зрения, представленная в "Coroutines belong in a TS", выглядела как новый и неожиданный взгляд на await. Поэтому решил поделиться и услышать мнение знающих людей.

Сожалею, если сбило с толку, добавил UPD2 с пояснением в текст.
UPD: нет, не буду развернуто отвечать. Обсуждаемая тут критика — ноября 2015 года. Черновик стандарта — февраль 2016 года. Все приведенные замечания были учтены, и для новой версии уже неактуальны.
Неизвестно. Старая версия претендовала на описание некоторой референсной реализации, новая — стала уже практически "сферическим предложением в вакууме". С одной стороны есть "хороший" прецедент: STL. Который был стандартизован примерно на таком же "уровне готовности" (в момент выхода стандарта ни одной реализации мало-мальски ему соответствующей не было и близко), с другой — есть "плохой": export для шаблонов.

И не очень понятно — куда торопиться. Что, после C++17 уже ничего не будет? Будем опять больше дюжины лет ждать следующего стандарта? Не хотелось бы...
Все приведенные замечания были учтены, и для новой версии уже неактуальны.

Тут не уверен.
Если мы говорим про критику из "Coroutines belong in a TS" в целом, то модель suspend-up c await как была инвазивная так и осталась. А это было основным замечанием.

Если говорить про пункт "2.8.2 Security and Performance Risks", который в статье упомянут как

5) Риск для безопасности и производительности: текущий дизайн сопрограмм увеличивает риск jitter'a, недостатка ресурсов отдельным задачам (starvation) и DOS атаки.

то причиной этого авторы "Coroutines belong in a TS" называют реализацию await:

 ( await-ready-expr
     ? await-resume-expr
     : (await-suspend-expr, suspend-resume-point, await-resume-expr))

В том случае если выражение await-ready-expr все время готово (как генератор случайных чисел выше), то сопрограмма будет все время продолжаться и продолжаться, захватывая процессорное время. Цитата:

this essentially means that if the Awaitable is already ready (await_ready returns true) the coroutine continues without suspending. And so on and so on for an indefinite number of iterations. This can at best result in high jitter; and at worst starvation.

В новой редакции P0057R2 от 12 февраля 2016 я вижу, что выполнение также будет продолжено, если awaited-выражение готово:

The await-expression evaluates the await-ready expression, then:
— If the result is true, or when the coroutine is resumed, the await-resume expression is evaluated, and its result is the result of the await-expression.

Не увидел разницы.

Если исправление в том, что добавлено слово co_yield и его можно вставить в цикл:

Значит, надо всего лишь добавить задержку. Или вставить в цикл инструкцию принудительного переключения контекста (в js такое делается согласно стандарту Promises/A+ для любого продолжения, в C# это делается через await Task.Yield() — значит и для C++ решение найдется)

то это не совсем то, чего требуют авторы "Coroutines belong in a TS". Они настаивают, что сопрограммы должны быть спроектированы таким образом, чтобы не перекладывать эту заботу на пользователя сопрограмм и starvation был исключен by-design. Предлагается запуск с указанием характеристик выполнения:

Scheduling ought to be a cross cutting concern for the coroutine, so we'd much rather see it as a library interface, something like:

    spawn(always_suspend, some_async_function);

or:

    spawn(always_continue, some_async_function);

spawn, насколько я понимаю, что-то похожее на spawn из Boost

В еще более позднем proposal "A networking library extension to support co_await-based coroutines" от 14 февраля 2016 Christopher Kohlhoff более подробно описывает работу spawn с co_await:

New coroutine-based threads of execution are explicitly launched using a spawn function. This function also allows the user to specify the execution properties of the new thread of execution.

5.2. Introducing new threads of execution should be explicit

As mentioned above, coordinating multiple threads of execution is a requirement of all but the most trivial applications. Even if a networking application is single-threaded, there still exists concurrency in the scheduling and execution of these threads of execution. Therefore, to reduce the risk of programmer error, the introduction of new threads of execution should be explicit.

In this proposal, new coroutine-based threads of execution are initiated using the spawn function. In addition to launching a new thread of execution, this function requires the programmer to specify the executor that will be used for it.

Ничего такого в последней ревизии P0057R2 не видно. Поэтому это замечание, в той форме в какой было сформулировано, также не исправлено.
Хотя средства, чтобы обходить проблему, в виде co_yield есть, это да.
Нет, средства для обхода проблемы — это не co_yield — а механизм await_transform.

Делается это так:

  1. Делается новая реализация future (можно в виде обертки-наследника существующей).
  2. У нее переопределяется promise_type на новую реализацию promise.
  3. У этой реализации promise заводится метод await_-
    transform, который оборачивает "чужие" awaitable-значения в "свои".

Все! Теперь у нас есть контроль над всеми операторами await в теле функции. Можно, к примеру, сделать так, чтобы await-ready-expr всегда возвращало false.

Таким образом, ответственность переложена на ту реализацию future/promise, которую использует пользователь. Нужна быстрая реализация (а с зависаниями программист будет бороться "внимательным взглядом") — берем одну реализацию. Нужна надежная реализация — берем другую. Нужно чтобы код выполнялся строго в потоке UI — берем третью.
Мне кажется достаточно просто первого пункта что бы эта идея не прошла.
Даже в реализации от MS, в своем собственном Фреймворке с этим проблемы возникают. Понапихав везде async'и достаточно забыть сделать это в одном месте что бы сломать целую подсистему. Как это случилось например в WinRT Paginate Event.
Я так понимаю если вы внутри await функции, что то дёргаете стороннее оно уже не обязано быть await.
Да.
Проблемы возникают когда вы внутри синхронной функции, например в обработчике события и вам нужно дергать async'и фреймворка (синхронных методов нет, от них МС избавилась). И вам нужно ждать окончания их выполнения (иначе по выходу из обработчика фреймворк начнет обрабатывать неподготовленные данные). И тут оказывается что подождать окончания их выполнения нельзя, потому что и обработчик вызван из UI потока и методы которые нужно дернуть могут выполнятся только в UI потоке.
Да распространение async кода вверх по стеку по началу донимало, но в итоге после не долгой адаптации все нормализовалось. Обычно точки сопряжения с не асинхронным кодом минимальны и достаточно хорошо контролируются.
Да еще добавлю в данной концепции, асинхронный вызов не обязательно становится фоновым(не удачный термин), в общем асинхронный вызов вполне может быть выполнен в том же потоке :) Или может быть возвращен промайс который, будет установлен результатом IO, опять же без ухода в фон.
Хорошее обсуждение вот тут. Если вкратце это предположительный вариант (по мнению одного из членов комитета) о том, что войдет в C++17. Интересно, что про корутины там не слова.

Сейчас вообще интересный момент — часть людей считает, что многие из бумаг не готовы. Особенно неприятная ситуация выходит с Concepts TS — но я надеюсь, что комитету удастся наконец принять решение в пользу принятия этого предложения. Больше 10 лет обсуждения — надоело уже.

Честно говоря читая пэйпер, у меня возник вопрос — а ваша реализация то где? Во вторых, я соглашусь с тем, что текущий вариант обладает недостатками — но вариант с переключением контекста вручную (yield\longjmp) мало того, что создаст дорогу для кода который будет провоцировать баги, так еще и усложнит работу создателей компилятора, который будут вынуждены писать код под каждую платформу. Лично я против включения корутин в виде явного переключения контекста, в стандарт. Он не решает проблем, которые высказаны выше, а еще и своих добавляет.
Также небезинтересное обсуждение документа "Coroutines belong in a TS" на reddit и здесь. Мнения разделились. Некоторые считают await неприемлемым, потому что корутины "non-composable", плохо сочетаются с синхронным кодом и STL.

Другой пользователь возражает, что критика await имеет несколько FUD стиль (Fear, uncertainty and doubt), мало конкретики и неопределенные страхи. А явное указание await он считает хорошим подходом.
Мне кажется сочетание с STL будет проблемой тех кто решит использовать await. Сейчас ведь по факту так вообще нельзя делать.
Про корутины вспомнили когда стали больше писать асинхронные сервера. И Гор Нишанов приводит пример из сетевого программирован. Лично я пока не понимаю как STL использовать с корутинами. Быстрая сортировка на корутинах?
Да, конечно, проблемы возникнут только при вызове асинхронного кода из алгоритмов STL. В своей работе «Resumable Expression» Christopher Kohlhoff более подробно описывает проблему с STL. Он называет это "Острова абстракций".

Такой код работать не будет:

std::future<void>   tcp_sender(std::vector<std:string>  msgs)
{
        auto    conn    = await Tcp::Connect("127.0.0.1”,   1337);
        std::for_each(msgs.begin(), msgs.end(),
                [&conn](const   std::string&    msg)
                {
                        await   conn.write(msg.data(),  msg.size());
                });
}

Поэтому придется писать новую вариацию алгоритма, который знает про await:

template    <class  I,  class   F>
std::future<void>   resumable_for_each(I    begin,  I   end,    F   f)
{
        for (I  iter    =   begin;  iter    !=  end;    ++iter)
                await f(*iter);
}

std::future<void>   tcp_sender(std::vector<std:string>  msgs)
{
        auto    conn    = await Tcp::Connect("127.0.0.1”,   1337);
        resumable_for_each(msgs.begin(),    msgs.end(),
                [&conn](const   std::string&    msg)
                {
                        await conn.write(msg.data(),    msg.size());
                });
}

Со временем это может привести к появлению набора алгоритмов, которые являются "отражением STL". Это будут "острова" по-мнению Кристофера.
Фокус в том, что в асинхронном программировании циклы бывают не только последовательными, но и параллельными.

И если для подхода async/await понадобится писать новую реализацию последовательного цикла for_each — то для подхода boost понадобится писать новую параллельную реализацию.
std::for_each сейчас лучше заменить Range-based for loop — компилятор его порой намного лучше понимает. Со вторым примером соглашусь, но я думаю целесообразнее было бы на вход уже генератор подавать — если мы говорим про асинхронность.

Вариант с переключением контекста как предлагает Кристофер потребует O(n) доп памяти. Причем неявно. Плохо согласуется с zero cost abstractions.
А можно пример алгоритмов (кроме for_each) которые вам нужны для работы с корутинами?
Лично для меня проблема с STL алгоритмами не стоит) Я бы и "рукопашные" циклы писал, чтобы пользоваться await вместо callback'ов. Поскольку сallback'и в наших асинхронных операциях значительно запутывают код, c await было бы проще.
Я скорее рассматриваю аргументы Кристофера.

Могу предположить, что это может быть полезно, когда для каждого элемента последовательности нужно вычислять предикат настолько дорогой/долгий, что его выполнение выносится в фон. std::any_of, std::find_if, std::count_if, remove_if. То есть запустили вычисление предиката для первого элемента в фоне, вернулись в EventLoop, когда досчитали, "проснулись" с await, перешли к следующему элементу последовательности и снова запустили предикат в фоне.
Там битва настоящая идет, между конкурирующими пейперами. Комитет чуть не "подрался" после очередного заседания по концептам. Очень накаленная беседа была.

Одни сторонники "лучшее враг хорошего" включая Бьерна. Другие постоянно ищут способ, как сделать плюсы еще более мощными (читай сложными) для реализации. Больше функционала! И ведь хорошие идеи все, да только сложность языка растет все выше и выше(куда уже еще то?!).
А где вы смотрите? Видео с заседания где-то выкладывают?
На реддите и почтовых рассылках члены комитета делятся впечатлениями =)
Yield позволит реализовать генераторы. ;)
Использую ноду с "файберами" и недоумеваю зачем в неё рсиленно пихают генераторы, промисы и асинкавайты. Разработчики языков сейчас совсем разучились думать. Только и могут что популярные костыли стандартизировать. Забавно наблюдать как форсится очередная глупая идея, чтобы через 5 от неё методично отказываться. У руля не хватает талантливых дизайнеров, а не вечно заседающих балаболов, которые придумывают проблемы, а потом героически их решают. С++ идёт в сторону сишарпа, вместо того, чтобы впитать в себя проверенные и продуманные решения из языка д.
Хм, а какие модули в ноде эти "файберы" поддерживают? Что-то первый раз о таком слышу.
Любые синхронные модули. node-fibers действует совершенно прозрачно для них. Плюс, я написал обёртку, превращающую асинхроные функции всинхронные. Из известных проектов использующих их — метеор.инхронные.
Sign up to leave a comment.

Articles