Недавно Россию посетила группа экспертов С++, участвующих в работе комитета по стандартизации. 25 февраля они участвовали в Q&A сессии, организованной Яндексом, а после выступали на конференции С++ Russia 2016. Одним из них был Гор Нишанов, автор предложения о включении C#-подобных сопрограмм (те которые async/await) в стандарт С++17. Ранее Гор выступал на CppCon 2015 с докладом "С++ Сопрограммы — Абстракция с отрицательным оверхедом". Такая возможность структурировать асинхронный код выглядит привлекательно, Гор в докладе убедительно показывает, что количество кода сокращается, а скорость работы возрастает по сравнению с «рукописными» State Machine. Кроме того, это одно из самых больших потенциальных изменений в следующем стандарте и привлекает к себе внимание. Судя по публикациям, proposal получил значительное одобрение и комитет склоняется к включению в стандарт.

После этого я был весьма удивлен, когда загуглив упомянутый proposal P0057, получил в выдаче встречный документ "Coroutines belong in a TS", который подвергает предлагаемую реализацию сопрограмм жесткой и весьма эмоциональной критике и требует не включать в С++17, а отложить для «обкатки» в Technical Specification. Отмечу, что не являюсь сторонником этих возражений, а приглашаю интересующихся к обсуждению, насколько обоснованны перечисленные претензии и все ли так плохо. Под катом «выжимка» из документа с небольшими комментариями.

Первое, что обращает внимание: одним из противников является Christopher Kohlhoff, автор и maintainer Boost.Asio, которого можно считать специалистом в асинхронном программировании. Затем бросается в глаза резкий и эмоциональный тон документа. Не просто мелкие замечания, а принципиальное несогласие. Приведу несколько цитат:

Предложение кажется копирует из других языков, но не учится на ошибках, которые они сделали. Мы не должны слепо копировать эти ошибки в С++.

… стремление включить в С++17 такое предложение и поспешное и глупое.

Учитывая, что мы имеем шанс стать одним из первых языков, в котором это реализовано правильно, я удивлен, что мы так спешим стандартизовать неоптимальный, я бы даже сказал, сломанный подход.

Есть два больших красных флага, которые люди игнорируют в стремлении добавить «большой билет» и «крутую» фичу в С++17.


Итак, какие же претензии предъявляются:

1) Самый главный пункт: необходимость использования ключевого слова await, чтобы помечать асинхронные вызовы, на которых происходит suspend. Эту модель авторы документа называют «suspend-up» и перечисляют такие недостатки:
  • Модель является инвазивной, «вирусно» распространяется по кодовой базе, по всему стеку асинхронных вызовов.
  • Требуется значительное изменение уже существующего кода, чтобы он использовал сопрограммы.
  • Если код из 3rdparty библиотеки вызывает наш асинхронный callback, то может потребоваться добавить await и в него.
  • Нельзя использовать стандартные алгоритмы STL с асинхронным кодом, нужно писать специальные «асинхронные версии». Это может привести к «копии STL» и дублированию кода.
  • Требует добавления нового ключевого слова и не может быть реализовано только на уровне библиотеки, как, например, Boost.Coroutines.


Этой модели авторы противопоставляют так называемую «suspend-down» модель, которая не является инвазивной и используется в Boost.Coroutines. Она также предлагалась в комитет, «Resumable Expression» от Christopher Kohlhoff, но предпочтение было отдано С#-подобному await.
Отличие этой модели в том, что не требуется помечать асинхронные вызовы словом await. Вызывается сущность, которая «снаружи выглядит как обычная функция», например, suspend(). Она внутри осуществляет переключение контекста (Boost.Coroutines использует Boost.Context) без выхода из вызывающего метода и «подвешивает» весь стек вызовов до завершения асинхронной операции. При этом первые функции в цепочке могут даже «не знать», что вызывают асинхронную операцию, и не требуется модификация существующего кода.

На эту тему Гор Нишанов отвечал следующее (видео доклада на CppCon, Q&A в Яндексе):
  • Да, модель с await инвазивная и требует модификации кода. Как и использование std::future и std::promise. Если вы готовы применять std::future, то сопрограммы с await предлагают вам более простой и понятный путь.
  • Да, модель Boost.Coroutines (он назвал ее «Fibers») имеет свои сильные стороны и свою область применения. Это не взаимоисключающие модели и обе будут включены в С++ рано или поздно. И могут даже применяться совместно для различных задач.


2) Следующее на что указывают авторы документа: при проектировании сопрограмм была проигнорирована значительная часть опыта и use-case'ов для платформ, которые требуют малого времени отклика. Для примера приводятся системы для финансовых рынков. Указывается, что работа таких систем регламентируется законами, а отказ может повлечь расследование и судебное преследование.
По этой причине мысленный эксперимент не подходит и сопрограммы должны быть предварительно испытаны в работе в таких областях.

3) Далее идет интересный аргумент в стиле «столкновение миров»: Linux — это наиболее распространенная платформа для высокопроизводительных систем, поэтому пробная реализация сопрограмм для MSVC/Windows мало что дает для оценки производительности.

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

Кроме того, модель сопрограмм во многом была позаимствована из динамических языков. И очень вероятно, что она является не оптимальной и для С++ можно сделать лучше.

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

Честно говоря, мне этот пункт показался несколько притянутым. Во всяком случае отдельные детали реализации могут быть исправлены и принципиальных недостатков, присущих модели, не видно.

6) Риск для корректности: после завершения асинхронной операции сопрограмма может быть продолжена на другом потоке. Авторы считают, что такое поведение допустимо, но оно не должно быть по умолчанию.

В то же время в C# await эта проблема прекрасно решается. Если операция запускается из UI потока, то продолжение (continuation) всегда вызывается тоже в UI потоке.

7) Риск, что если модель await (suspend-up) будет принята, то это осложнит включение в стандарт suspend-down модели (по образцу Boost.Corountines). Станет трудно убеждать тратить время комитета по стандартизации на функциональность, которая «уже есть».

Если бы предложенные await-сопрограммы реализовывались только на уровне библиотеки, то это оставляло бы пространство для маневра и исследования альтернативных вариантов. Но в текущей форме может не быть пути назад.

Предложения



1) Отложить включение сопрограмм в С++17 и включить пока в Technical Specification для «обкатки».

2) Продолжать работу над suspend-down моделью и возможно сформировать два предложения по сопрограммам в будущие стандарты «Suspend-Up Coroutines» и «Suspend-Down Coroutines».

3) Текущее предложение по сопрограммам в значительной степени получило преимущество потому, что Гор Нишанов имел возможность экспериментировать и дорабатывать компилятор Visual Studio в Microsoft. Поэтому авторы альтернативной модели сопрограмм ожидают сотрудничества с разработчиками GCC и Clang для доработки и экспериментов на высоконагруженных Linux системах.

Заключение



В заключение авторы упоминают, что есть два «больших красных флага»:

1) Те участники комитета, которые имеют большой опыт применения сопрограмм в высоконагруженных системах, по разным причинам имеют мало влияния.

2) В настоящее время есть несколько предложений в Technical Specification: Parallelism, Concurrency, Networking и потенциально «Suspend-Up Coroutines» и «Suspend-Down Coroutines», которые требуют единой и последовательной модели или по крайней мере продуманного взаимодействия.

Поэтому нужно время и торопиться с сопрограммами не стоит.

А каково ваше мнение? Насколько критичны перечисленные недостатки?
И стоит ли откладывать сопрограммы, чтобы позднее включить в стандарт сразу две разновидности?


UPD: в комментариях Mikanor дает ссылку на статью "resumable functions — async and await" в блоге Meeting C++ от 2013 года. В ней довольно подробно описаны изначальная идея и преимущества coroutines с await.
На Хабре был перевод этой статьи от tangro и обсуждение.

UPD2: Напомню, что эта статья не является полным переводом документа «Coroutines belong in a TS», а скорее дайджестом, призванным привлечь внимание к альтернативной точке зрения на сопрограммы с await (особенно suspend-up vs suspend-down). Детали и развернутое описание каждого пункта можно найти в исходном документе.

Комментарии (105)


  1. andvgal
    01.03.2016 15:16
    +10

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

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

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

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

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


    1. stalkerg
      01.03.2016 15:33

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

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

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

      Кхм… неужели в этом предложении нету описания реализации?
      Моё мнение: в корутинах потоков быть не должно. Должен быть диспетчер выполнения кода.


      1. KindDragon
        02.03.2016 19:22

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


        1. andvgal
          02.03.2016 19:57
          +1

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

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

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

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

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


          1. mayorovp
            02.03.2016 20:44

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

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

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

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

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

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

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


            1. andvgal
              03.03.2016 00:14

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

              Например, моя изначальная ссылка на 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++.


              1. mayorovp
                03.03.2016 07:12

                Напротив, у вас есть полный контроль над потоками. Кто пишет проактор — тот и создает поток.


                1. andvgal
                  03.03.2016 14:52

                  Об этом и речь, раз Proactor реализован внутри компилятора и его runtime библиотек, то без интерфейсов управления получается неуправляемый чёрный ящик.


                  1. mayorovp
                    03.03.2016 15:42

                    Да с чего вы взяли, что Proactor реализован внутри компилятора и его рантайма? Proactor делается в прикладной библиотеке, компилятор реализует лишь сахар для "присыпки". В рантайме же (я посмотрел) — вообще никакого кода, весь заголовочный файл — просто шаблонная магия.


                    1. andvgal
                      03.03.2016 15:48

                      Вы сами себе противоречите. Теперь говорите обратное.

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


                      1. mayorovp
                        03.03.2016 16:02

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


              1. axden
                03.03.2016 13:44

                В июле 2015 на C++ User Group Meeting был доклад Александра Фокина из Яндекс на тему текущего состояния "Resumable функции в C++".
                Там он сообщает, что "честной" реализации coroutines в Visual Studio 2015 RC нет, а сделано на fibers.

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

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


    1. RPG18
      01.03.2016 16:09
      +2

      Всё остальное — это искажения термина и лишь ещё один интерфейс для Thread Pool.

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


      1. andvgal
        01.03.2016 18:22

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

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

        Спасибо за слайды, стало понятней.


      1. KindDragon
        02.03.2016 18:38

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

        Кстати он рассказывал про минусы предлагаемого подхода в Resumable Expression, от части ответ насколько я понимаю описан здесь http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0171r0.html


    1. bfDeveloper
      01.03.2016 16:10
      +2

      Не согласен с искажением термина coroutine. На компилируемом языке вполне возможны корутины. Тот же boost:coroutine или даже go. Абстракция та же самая: системных потоков мало, реальных потоков исполнения с независимыми стеками много. Это же не просто таски, обрабатываемые пулом потоков, это полноценные корутины, которые можно приостановить на середине, а потом продолжить с того же места. А поток может быть и один.
      Однако предложение действительно сырое, не надо тащить чужеродные непродуманные концепции в язык. Похоже MS форсит свою реализацию, даже в свой компилятор поддержку добавили.


      1. khim
        01.03.2016 16:28

        Похоже MS форсит свою реализацию, даже в свой компилятор поддержку добавили.
        Это у них в ДНК.

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

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

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

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

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

        Будем надеяться на благоразумие комитета по стандартизации…


        1. Viacheslav01
          02.03.2016 00:09
          +2

          Managed C++/C++/CLI/C++/CX имеют распространение там где они нужны, они создавались для определенных целей и для них и используются.


          1. khim
            02.03.2016 16:43
            +2

            Совершенно верно. Вот только частью ни C++11, ни C++14 они не являются. А обсуждаемая здесь фича, в общем-то, нужна для тех же целей — но почему-то должна стать частью C++17. Где логика?


            1. Mikanor
              02.03.2016 16:56
              -1

              Для каких целей? Managed С++ никогда не предлагался на рассмотрение, это extension который работает под свою платформу. У GCC и Clang такие тоже есть. И?

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


              1. khim
                02.03.2016 18:48

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

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

                И вот против этого — как раз все возражения.


                1. mayorovp
                  02.03.2016 18:50

                  Этот оператор как бы появился задолго до WinRT!


                  1. khim
                    02.03.2016 19:12
                    +1

                    Это в каком-таком мире Visual Studio 2015 появился до WinRT?

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

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

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

                    Хватит с нас Python3, не стоит устраивать подобное же в C++. Тем более когда мотивация — поддержка API одной проприетарной системы.


                    1. mayorovp
                      02.03.2016 20:23

                      А этот оператор впервые и не в плюсах появился. Об этом даже в обсуждаемой статье написано.


                      1. khim
                        02.03.2016 21:15

                        Согласен — плохо выразился. Думал, что будет понятно из контекста (Managed C++ ?C++/CLI ? C++/CX ?await in C++17).

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


                        1. Viacheslav01
                          02.03.2016 23:17
                          -3

                          Я правильно понял, что если бы это же решение пришло со стороны не МС никаких вопросов бы не возникло? МС очень не плохо чувствует себя выпуская расширения для языков, проблем с тем, что это расширения у разработчиков под МС нет.


                          1. khim
                            03.03.2016 01:05

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

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

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

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

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

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


                            1. Mikanor
                              03.03.2016 01:39

                              Вы, простите, пейперы наискосок читаете? Или комитет, за которым последнее слово, у вас просто так, для красоты?

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

                              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 не поощряется в использовании, в любом виде.


                              1. khim
                                03.03.2016 09:56
                                +1

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

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

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

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


                1. Mikanor
                  03.03.2016 01:15

                  await в том виде, в котором он предлагается появился в бумаге предложенной на рассматривание комитета

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

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

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

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

                  vector тоже плохой — это не повод убивать шаблонный класс vector как сущность.


  1. stalkerg
    01.03.2016 15:30
    +2

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

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

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


    1. IvaYan
      01.03.2016 16:03
      +1

      типо std_await

      На C++ Russia Гор Нишанов говорил, что будут добавлены ключевые слова co_await, co_return и co_yield, и рассказывал, почему именно так.


      1. stalkerg
        01.03.2016 16:28

        Тогда особых проблем не вижу.


    1. khim
      01.03.2016 16:08
      +11

      Мне как пользователю Python который часто мучает Tornado и Asyncio всё кажется логичным.
      А мне, как пользователю C++ «на железе» — ни разу.

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

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


      1. stalkerg
        01.03.2016 16:24
        -9

        А мне, как пользователю C++ «на железе» — ни разу.

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

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

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

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

        Именно там асинхроность очень часто встречается. Тут же просто синтаксический сахар, что бы избежать callback-hell.


        1. khim
          01.03.2016 16:39
          +8

          А мне, как пользователю C++ «на железе» — ни разу.
          Ну и используйте C++14 дальше, С++17 никто не заставляет использовать.

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

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

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


          1. mayorovp
            01.03.2016 17:02

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

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


            1. khim
              01.03.2016 20:46
              +2

              Данная фича, насколько я понимаю, довольно тесно увязана с тем, как "устроен мир" в Windows. И то, что фича "реализуется полностью переносимым кодом" не делает её автоматически на MT-Safe, на, тем более, AS-Safe.

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

              P.S. На всякий случай: вы вообще понимаете о чём я?


              1. mayorovp
                01.03.2016 22:00

                Не вижу связи между этой фичей и мироустройством Windows.


              1. mayorovp
                01.03.2016 22:23

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

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


                1. khim
                  01.03.2016 23:09

                  Что произойдет, если сигнал придет посреди записи в такую переменную?
                  Это-то как раз не страшно. С точки зрения записи-чтения — это обычная переменная.

                  А вот что будет, если переменной ещё нет — тут, зачастую, обращения к `__thread` как раз и становятся AS-Unsafe.


          1. stalkerg
            02.03.2016 00:21
            -4

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

            Почему?

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

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

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

            Oh my god! Я не автор этого пропозла и не автор реализации, я потребитель. О том, что бы эта фича не поломала все остальные компоненты языка должны думать разработчики. Если вдруг технически нельзя безопасно (ничего не поломав) внедрить то я конечно расстроюсь но переживу. С чего вы решили, что я стою на позиции: запихнём эту фичу, а остальное пусть горит синем пламенем?


            1. ZyXI
              02.03.2016 01:25
              +5

              Ну и используйте C++14 дальше, С++17 никто не заставляет использовать.
              Нифига себе заявочки. Хорошо что не вы эту фичу пытаетесь в комитет по стандартизации продвигать. Потому что одно такое заявление само по себе было бы достаточно чтобы фичу «зарубить» раз и навсегда. Независимо от каких-либо других аргументов.
              Почему?
              По?моему, это очевидно: предложение «не использовать C++17 на …» при предложении нового функционала для включения в стандарт означает предложение остановить разработку языка на …, потому что следующий стандарт языка основывается на предыдущем. На такую заморозку никто не пойдёт. Предложение сделать функционал необязательным для компиляторов было бы более адекватным.


              1. stalkerg
                02.03.2016 10:39
                -3

                Я про, то что ещё во многих отраслях используют ANSI C++ и не спешат к новомодным штучкам (иногда для упрощения сопряжения с Си). Т.е. для многих это вполне приемлемый вариант не использовать новые фичи/стандарты. Собственно и khim мог бы просто не использовать async, что бы не боятся на счёт совместимости своих подходов к разработке и технологиями.


                1. khim
                  02.03.2016 16:42
                  +1

                  Собственно и khim мог бы просто не использовать async, что бы не боятся на счёт совместимости своих подходов к разработке и технологиями.
                  Если я разрабатываю платформу? Как вы это себе представляете?

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

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

                  Но тут же сырую, в общем-то, фичу, пихают «по-быстрому» в общий стандарт!


                  1. stalkerg
                    02.03.2016 21:12
                    -2

                    Если я разрабатываю платформу? Как вы это себе представляете?

                    Скажите что ваша платформа не поддерживает эту фичу. Я не понимаю вас, поясните.


                    1. khim
                      02.03.2016 22:25
                      +1

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

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

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

                      Потому заявления

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


                      1. stalkerg
                        03.03.2016 09:47

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

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

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

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

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

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


  1. Gorthauer87
    01.03.2016 16:06
    +1

    Когда слушал доклад, сразу показалось, что добавление новых ключевых слов это не есть хорошо. Это в любом случае порождает проблемы, сложности, грабли. Гораздо лучше представить какие-то инструменты, чтобы можно было подобные штуки делать на уровне библиотек.
    Думаю тут могли бы помочь нормальные гигиенические макросы и возможность оптимизатора самому перемещать объект, созданный через new на стек, если он точно может проследить его время жизни.


  1. mayorovp
    01.03.2016 16:16
    +13

    Как фанат асинхронного программирования в стиле C#, могу в качестве ответа привести возражения против бустовой реализации.

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

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

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

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

    Кроме того, модель "suspend-down" довольно активно пожирает пространство оперативной памяти под стек. Если для модели "suspend-up" требуется не такое большое число потоков, и для каждого из них можно выделить "обычный" стек, а основная требуемая задачам память будет выделяться из кучи кусками — то в модели "suspend-down" свой стек нужен под каждую задачу, и их придется делать маленькими. А отсюда — высокая вероятность переполнения стека при ошибке в угадывании требуемого размера.


    1. ZaMaZaN4iK
      01.03.2016 18:01
      +3

      а как же тогда решить проблему с невозможностью применения STL? Придётся просто вторую версию написать специально для корутин?


      1. mayorovp
        01.03.2016 19:09

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


      1. Mikanor
        02.03.2016 00:40

        STL 2 скорее всего, все равно будет — range v3 Эрика Найбера и концепты. Однако я не понимаю связи между алгоритмами и сопрограммами. Возможность параллелизации алгоритмов скорее Parallelism TS и Concurrency TS. Которые планируются пока скорее к C++20.


    1. bobermaniac
      01.03.2016 18:38
      +1

      Можете вкратце пояснить, в чем разница suspend-down и suspend-up моделей? Загуглить сходу не удалось, а из комментариев не вполне понятно.


      1. mayorovp
        01.03.2016 19:38
        +8

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

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

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

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

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


        1. bobermaniac
          01.03.2016 22:24

          Ох уж эти программисты с их выдуманными терминами. Спасибо.


        1. aikixd
          01.03.2016 23:51

          Если я правильно понимаю, выходит что в suspend-down метод просто отдает контроль пока не задача не будет выполнена и в это время этот же поток не может продолжать работу? То есть если мне нужно загрузить н ресурсов из сети, мне придется это делать поочереди?


          1. stalkerg
            02.03.2016 00:26

            То есть если мне нужно загрузить н ресурсов из сети, мне придется это делать поочереди?

            Да, но так как большая часть времени уйдёт на транспорт данных по TCP, ваша программа будет заниматься чем то ещё.
            По факту выполнение будет только для пред обработки зарпоса и обработки ответа. При этом не будет происходить вредных контекст свичей ядра ОС.


            1. Mikanor
              02.03.2016 00:42

              Передачу управления нужно делать явно — собственно этим сейчас и занимается Boost ASIO внутри с помощью асинхронных примитивов ОС. Вроде есть ASIO на корутинах, но я не смотрел.


              1. stalkerg
                02.03.2016 00:55

                Ну я скорее про это и говорил.
                Я почти каждый день работаю с Tornado и хорошо понимаю именно их подход, а он именно за ручную асинхронность.


        1. Qbit
          11.03.2016 18:51

          > возвращают не значение, а специальную обертку вокруг него, называемую обещанием («promise») или задачей («task»).

          Обычно немного не так. Promise (? TaskCompletionSource) асинхронная операция оставляет себе. А отдаёт соответствующий Future (? Task), который является, грубо говоря, read-only view для обещания.


    1. mayorovp
      01.03.2016 19:42
      +3

      UPD: еще один аргумент.

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

      При использовании сопрограмм буста любое ожидание с переключением контекста, выполненное в потоке UI, остановит цикл обработки сообщений столь же хорошо, как это мог бы сделать блокирующий вызов...


      1. bobermaniac
        01.03.2016 22:30
        +2

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


        1. mayorovp
          01.03.2016 22:41

          Это решается одним вызовом библиотечной функции, которая запишет переданный ей обработчик обратных вызовов в thread_local-переменную.

          Оператору await ничего не нужно знать про UI-потоки. Ему нужно знать лишь где лежит обработчик, который позволит асинхронному потоку выполнения вернуться в исходный контекст.


    1. nickolaym
      02.03.2016 15:29
      +2

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

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

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

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

      А вот возможность сделать машину состояний не своими руками, а силами компилятора — это круто.


    1. GraD_Kh
      08.03.2016 18:51

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


      1. khim
        08.03.2016 20:09

        Список поддерживаемых архитектур довольно скуден.
        А чего вам не хватает? Sparc'а? Itanic'а? Для них кто-то что ещё разрабатывает?

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

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

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

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


        1. GraD_Kh
          08.03.2016 21:01

          В любом случае поддержать их можно — было бы желание.

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

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

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

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

          … Используя сакральное знание о деталях реализации Emscripten. И если вдруг подход поменяется (например, в сторону JS objects как у альтернатив), то внезапно все перестанет работать. Собственно в этом и есть главный изъян подхода, когда библиотека берет на себя platform-specific вещи.


          1. khim
            08.03.2016 22:34

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

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

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

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

              Ну если миллиарды устройств и пользователей это для вас «никуда» — тогда да, наверное.

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

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

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

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

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

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

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

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


              1. khim
                09.03.2016 00:29

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

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

                О как.

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

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

                Программу с async/await для C64 — в студию. Я ведь такой, да — у меня C64 есть, я проверю.


                1. GraD_Kh
                  09.03.2016 00:56

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


                  1. khim
                    09.03.2016 02:42

                    Вы вообще обсуждаемые возражения читали? В том-то и дело, что код не будет "просто синхронным". Код будет "просто неработающим".

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

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

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

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


                    1. mayorovp
                      09.03.2016 06:59

                      Вот как раз на платформах без потоков оно и будет работать лучше всего — там просто негде допустить ошибку. Посмотрите как на Javascript работают промизы — там как раз ровно один поток.

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


                      1. khim
                        09.03.2016 14:02

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

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

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

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

                        Ну так если так — то тем более стоит подождать пока разработкой займётся кто-то для кого галочка «включено в стандарт» не является самоцелью…


                        1. mayorovp
                          09.03.2016 14:41

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

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

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


                          1. khim
                            09.03.2016 15:51

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

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

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

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

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

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

                            Но я, слава богу, не член комитета по стандартизации и моё мнение мало чего решает. Члены предлагают вместо того, чтобы предложение просто «зарубить» оформить его в качестве TS, посмотреть как оно развиваться будет, а потом, может быть, и включить в следующую версию C++, если оно будет достаточно популярно.


                            1. mayorovp
                              09.03.2016 16:50

                              Я читал вот этот документ — http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0057r2.pdf

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

                              Отвечу по исходному документу позже и уровнем по-выше.


                              1. khim
                                09.03.2016 17:08

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

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


                                1. axden
                                  09.03.2016 21:22

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

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

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


                              1. axden
                                09.03.2016 20:12

                                я думал, тут его перевод-пересказ опубликован

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

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

                                Сожалею, если сбило с толку, добавил UPD2 с пояснением в текст.


                            1. mayorovp
                              09.03.2016 17:04

                              UPD: нет, не буду развернуто отвечать. Обсуждаемая тут критика — ноября 2015 года. Черновик стандарта — февраль 2016 года. Все приведенные замечания были учтены, и для новой версии уже неактуальны.


                              1. khim
                                09.03.2016 17:14

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

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


                              1. axden
                                09.03.2016 20:56

                                Все приведенные замечания были учтены, и для новой версии уже неактуальны.

                                Тут не уверен.
                                Если мы говорим про критику из "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 есть, это да.


                                1. mayorovp
                                  09.03.2016 21:21

                                  Нет, средства для обхода проблемы — это не co_yield — а механизм await_transform.

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

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

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

                                  Таким образом, ответственность переложена на ту реализацию future/promise, которую использует пользователь. Нужна быстрая реализация (а с зависаниями программист будет бороться "внимательным взглядом") — берем одну реализацию. Нужна надежная реализация — берем другую. Нужно чтобы код выполнялся строго в потоке UI — берем третью.


  1. Krey
    01.03.2016 16:22
    +3

    Мне кажется достаточно просто первого пункта что бы эта идея не прошла.
    Даже в реализации от MS, в своем собственном Фреймворке с этим проблемы возникают. Понапихав везде async'и достаточно забыть сделать это в одном месте что бы сломать целую подсистему. Как это случилось например в WinRT Paginate Event.


    1. stalkerg
      01.03.2016 16:27

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


      1. Krey
        01.03.2016 17:58
        +1

        Да.
        Проблемы возникают когда вы внутри синхронной функции, например в обработчике события и вам нужно дергать async'и фреймворка (синхронных методов нет, от них МС избавилась). И вам нужно ждать окончания их выполнения (иначе по выходу из обработчика фреймворк начнет обрабатывать неподготовленные данные). И тут оказывается что подождать окончания их выполнения нельзя, потому что и обработчик вызван из UI потока и методы которые нужно дернуть могут выполнятся только в UI потоке.


        1. Viacheslav01
          02.03.2016 00:21

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


  1. Viacheslav01
    02.03.2016 00:36

    Да еще добавлю в данной концепции, асинхронный вызов не обязательно становится фоновым(не удачный термин), в общем асинхронный вызов вполне может быть выполнен в том же потоке :) Или может быть возвращен промайс который, будет установлен результатом IO, опять же без ухода в фон.


    1. Mikanor
      02.03.2016 01:11

      -не туда-


  1. Mikanor
    02.03.2016 01:12
    +2

    Хорошее обсуждение вот тут. Если вкратце это предположительный вариант (по мнению одного из членов комитета) о том, что войдет в C++17. Интересно, что про корутины там не слова.

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

    Честно говоря читая пэйпер, у меня возник вопрос — а ваша реализация то где? Во вторых, я соглашусь с тем, что текущий вариант обладает недостатками — но вариант с переключением контекста вручную (yield\longjmp) мало того, что создаст дорогу для кода который будет провоцировать баги, так еще и усложнит работу создателей компилятора, который будут вынуждены писать код под каждую платформу. Лично я против включения корутин в виде явного переключения контекста, в стандарт. Он не решает проблем, которые высказаны выше, а еще и своих добавляет.


    1. Mikanor
      02.03.2016 01:16

      Ссылка потерялась во время переноса.


    1. axden
      02.03.2016 12:42

      Также небезинтересное обсуждение документа "Coroutines belong in a TS" на reddit и здесь. Мнения разделились. Некоторые считают await неприемлемым, потому что корутины "non-composable", плохо сочетаются с синхронным кодом и STL.

      Другой пользователь возражает, что критика await имеет несколько FUD стиль (Fear, uncertainty and doubt), мало конкретики и неопределенные страхи. А явное указание await он считает хорошим подходом.


      1. stalkerg
        02.03.2016 12:44

        Мне кажется сочетание с STL будет проблемой тех кто решит использовать await. Сейчас ведь по факту так вообще нельзя делать.


        1. RPG18
          02.03.2016 12:51

          Про корутины вспомнили когда стали больше писать асинхронные сервера. И Гор Нишанов приводит пример из сетевого программирован. Лично я пока не понимаю как STL использовать с корутинами. Быстрая сортировка на корутинах?


        1. axden
          02.03.2016 12:56
          +1

          Да, конечно, проблемы возникнут только при вызове асинхронного кода из алгоритмов 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". Это будут "острова" по-мнению Кристофера.


          1. mayorovp
            02.03.2016 13:23

            Фокус в том, что в асинхронном программировании циклы бывают не только последовательными, но и параллельными.

            И если для подхода async/await понадобится писать новую реализацию последовательного цикла for_each — то для подхода boost понадобится писать новую параллельную реализацию.


          1. Mikanor
            02.03.2016 14:58

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

            Вариант с переключением контекста как предлагает Кристофер потребует O(n) доп памяти. Причем неявно. Плохо согласуется с zero cost abstractions.


        1. Mikanor
          02.03.2016 15:00

          А можно пример алгоритмов (кроме for_each) которые вам нужны для работы с корутинами?


          1. axden
            02.03.2016 15:21
            +2

            Лично для меня проблема с STL алгоритмами не стоит) Я бы и "рукопашные" циклы писал, чтобы пользоваться await вместо callback'ов. Поскольку сallback'и в наших асинхронных операциях значительно запутывают код, c await было бы проще.
            Я скорее рассматриваю аргументы Кристофера.

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


      1. Mikanor
        02.03.2016 15:12

        Там битва настоящая идет, между конкурирующими пейперами. Комитет чуть не "подрался" после очередного заседания по концептам. Очень накаленная беседа была.

        Одни сторонники "лучшее враг хорошего" включая Бьерна. Другие постоянно ищут способ, как сделать плюсы еще более мощными (читай сложными) для реализации. Больше функционала! И ведь хорошие идеи все, да только сложность языка растет все выше и выше(куда уже еще то?!).


        1. axden
          02.03.2016 15:22
          +1

          А где вы смотрите? Видео с заседания где-то выкладывают?


          1. Mikanor
            02.03.2016 17:21

            На реддите и почтовых рассылках члены комитета делятся впечатлениями =)


    1. stalkerg
      02.03.2016 12:43

      Yield позволит реализовать генераторы. ;)