web-dev-qa-db-fra.com

Comment implémentez-vous Coroutines en C ++

Je doute que cela puisse être fait de manière portative, mais existe-t-il des solutions? Je pense que cela pourrait être fait en créant une pile alternative et en réinitialisant SP, BP et IP à l'entrée de la fonction, et en ayant un IP de sauvegarde et une restauration SP + BP. Les destructeurs et la sécurité d'exception semblent délicats mais résolubles.

Cela a-t-il été fait? Est-ce impossible?

62
Mike Elkins

Oui, cela peut être fait sans problème. Tout ce dont vous avez besoin est un petit code d'assembly pour déplacer la pile d'appels vers une pile nouvellement allouée sur le tas.

Je voudrais regarder la bibliothèque boost :: coroutine.

La seule chose à surveiller est un débordement de pile. Sur la plupart des systèmes d'exploitation, le débordement de la pile entraîne une erreur de segmentation car la page de mémoire virtuelle n'est pas mappée. Cependant, si vous allouez la pile sur le tas, vous n'obtenez aucune garantie. Gardez cela à l'esprit.

88
Ted

Sur POSIX, vous pouvez utiliser les routines makecontext ()/swapcontext () pour changer de contexte d'exécution de manière portative. Sous Windows, vous pouvez utiliser l'API fibre. Sinon, tout ce dont vous avez besoin est d'un peu de code d'assemblage de colle qui change le contexte de la machine. J'ai implémenté des coroutines à la fois avec ASM (pour AMD64) et avec swapcontext (); ni l'un ni l'autre n'est très dur.

18
zvrba

Pour la postérité,

Le site Web merveilleux de Dmitry Vyukov a une astuce intelligente en utilisant ucontext et setjump pour simuler des coroutines en c ++.

De plus, la bibliothèque de contexte d'Oliver Kowalke a été récemment acceptée dans Boost, donc j'espère que nous verrons bientôt une version mise à jour de boost.coroutine qui fonctionne sur x86_64.

15
tgoodhart

Il n'y a pas de moyen facile d'implémenter coroutine. Parce que la coroutine elle-même est hors de l'abstraction de pile de C/C++ tout comme le thread. Il ne peut donc pas être pris en charge sans que le niveau de langue soit modifié.

Actuellement (C++ 11), toutes les implémentations de coroutine C++ existantes sont toutes basées sur le piratage au niveau de l'assembly, qui est difficile à traverser en toute sécurité et fiabilité sur les plateformes. Pour être fiable, il doit être standard et géré par des compilateurs plutôt que par du piratage.

Il y a proposition standard - N3708 pour cela. Vérifiez-le si vous êtes intéressé.

10
Eonil

Vous pourriez être mieux avec un itérateur qu'avec une coroutine si possible. De cette façon, vous pouvez continuer d'appeler next() pour obtenir la valeur suivante, mais vous pouvez conserver votre état en tant que variables membres au lieu de variables locales.

Cela pourrait rendre les choses plus faciles à maintenir. Un autre développeur C++ pourrait ne pas comprendre immédiatement la coroutine alors qu'il pourrait être plus familier avec un itérateur.

7
Steve g

Pour ceux qui veulent savoir comment tirer parti des Coroutines de manière portable en C++ y̶o̶u̶ ̶w̶i̶l̶l̶ ̶h̶a̶v̶e̶ ̶t̶o̶ ̶w̶a̶i̶t̶ ̶f̶o̶r̶ ̶C̶ + ̶ + ̶1̶7̶ l'attente est terminée (voir ci-dessous)! Le comité des normes travaille sur la fonctionnalité voir le papier N3722 . Pour résumer le brouillon actuel du document, au lieu d'Async et Await, les mots clés seront repris et attendront.

Jetez un œil à l'implémentation expérimentale de Visual Studio 2015 pour jouer avec l'implémentation expérimentale de Microsoft. Il ne semble pas que clang ait encore une implémentation.

Il y a un bon discours de Cppcon Coroutines une abstraction de surcharge négative décrivez les avantages de l'utilisation de Coroutines en C++ et comment cela affecte la simplicité et les performances du code.

À l'heure actuelle, nous devons encore utiliser des implémentations de bibliothèque, mais dans un proche avenir, nous aurons des coroutines comme fonctionnalité C++ principale.

Mise à jour: Il semble que l'implémentation coroutine soit prévue pour C++ 20, mais a été publiée en tant que spécification technique avec C++ 17 ( p0057r2 ). Visual C++, clang et gcc vous permettent d'activer l'utilisation d'un indicateur de temps de compilation.

6
Atifm

COROUTINE une bibliothèque C++ portable pour le séquençage coroutine vous pointe-t-il dans la bonne direction? Cela semble être une solution élégante qui a duré l'épreuve du temps ..... elle a 9 ans!

Dans le dossier DOC se trouve un document pdf Une bibliothèque C++ portable pour le séquençage Coroutine par Keld Helsgaun qui décrit la bibliothèque et fournit de courts exemples d'utilisation.

[mise à jour] J'en fais moi-même une utilisation réussie. La curiosité a pris le dessus sur moi, j'ai donc cherché cette solution et j'ai trouvé qu'elle convenait parfaitement à un problème sur lequel je travaillais depuis un certain temps!

5
Dan

Je ne pense pas qu'il existe de nombreuses implémentations complètes et propres en C++. Un essai que j'aime est la bibliothèque de protothread d'Adam Dunkels .

Voir aussi Protothreads: simplification de la programmation événementielle des systèmes embarqués à contrainte de mémoire dans la bibliothèque numérique ACM et discussion dans la rubrique Wikipedia Protothread ,

5
yrp

Une nouvelle bibliothèque, Boost.Context, a été publiée aujourd'hui avec des fonctionnalités portables pour implémenter les coroutines.

3
Jeff Trull

Ceci est un vieux fil, mais je voudrais suggérer un hack utilisant l'appareil de Duff qui ne dépend pas du système d'exploitation (pour autant que je m'en souvienne):

C coroutines utilisant l'appareil de Duff

Et à titre d'exemple, voici une bibliothèque telnet que j'ai modifiée pour utiliser des coroutines au lieu de fork/threads: bibliothèque Telnet cli utilisant des coroutines

Et puisque le C standard avant C99 est essentiellement un vrai sous-ensemble de C++, cela fonctionne bien aussi en C++.

3
Erik Alapää

J'ai trouvé une implémentation sans code asm . L'idée est d'utiliser la fonction de création de threads du système pour initialiser la pile et le contexte, et d'utiliser setjmp/longjmp pour changer de contexte. Mais ce n'est pas portable, voir version pthread délicate si vous êtes intéressé.

2
rox

Il est basé sur des macros (grinçantes), mais le site suivant fournit une implémentation de générateur facile à utiliser: http://www.codeproject.com/KB/cpp/cpp_generators.aspx

2
Mark

Découvrez mon implémentation, elle illustre le point de piratage asm et est simple:

https://github.com/user1095108/generic/blob/master/coroutine.hpp

1
user1095108

https://github.com/tonbit/coroutine est une implémentation de coroutine asymétrique simple C++ 11 prenant en charge les primitives de reprise/rendement/attente et le modèle de canal. Il est implémenté via ucontext/fibre, ne dépend pas du boost, fonctionne sur linux/windows/macOS. C'est un bon point de départ pour apprendre à implémenter coroutine en c ++.

1
atto_mu

Vous devriez toujours envisager d'utiliser des threads à la place; en particulier dans le matériel moderne. Si vous avez du travail qui peut être logiquement séparé dans des co-routines, l'utilisation de threads signifie que le travail peut en fait être effectué simultanément, par des unités d'exécution distinctes (cœurs de processeur).

Mais, vous voulez peut-être utiliser des coroutines, peut-être parce que vous avez un algorithme bien testé qui a déjà été écrit et testé de cette façon, ou parce que vous portez du code écrit de cette façon.

Si vous travaillez sous Windows, vous devriez jeter un œil à fibres . Les fibres vous donneront un cadre de type coroutine avec le support du système d'exploitation.

Je ne connais pas les autres systèmes d'exploitation pour y recommander des alternatives.

0
Euro Micelli

WvCont est une partie de WvStreams qui implémente les soi-disant semi-coroutines. Celles-ci sont un peu plus faciles à gérer que les coroutines complètes: vous l'appelez, et cela revient à la personne qui l'a appelé.

Il est implémenté à l'aide de la WvTask plus flexible, qui prend en charge les coroutines complètes; vous pouvez le trouver dans la même bibliothèque.

Fonctionne sur win32 et Linux, au moins, et probablement sur tout autre système Unix.

0
apenwarr