Coroutine

Od roku 2003 nemá mnoho nejoblíbenějších programovacích jazyků, včetně jazyka C a jeho derivátů, přímou podporu pro coroutine v rámci jazyka nebo standardních knihoven. To je z velké části způsobeno omezeními implementace podprogramů založených na zásobníku. Výjimkou je knihovna C++ Boost.Context, součást knihoven boost, která podporuje výměnu kontextu na platformách ARM, MIPS, PowerPC, SPARC a x86 v systémech POSIX, Mac OS X a Windows. Na Boost.Context lze postavit koroutiny.

V situacích, kdy by koroutin byl přirozenou implementací mechanismu, ale není k dispozici, je typickou reakcí použití uzávěru – podprogramu se stavovými proměnnými (statické proměnné, často logické příznaky), které udržují vnitřní stav mezi voláními a předávají řízení na správné místo. Podmínky uvnitř kódu vedou k tomu, že se při následných voláních provedou různé cesty kódu na základě hodnot stavových proměnných. Další typickou reakcí je implementace explicitního stavového stroje v podobě rozsáhlého a složitého příkazu switch nebo prostřednictvím příkazu goto, zejména vypočteného goto. Takové implementace jsou považovány za náročné na pochopení a údržbu a jsou motivací pro podporu koroutinů.

Vlákna a v menší míře i vlákna jsou dnes alternativou ke koroutinům v běžných programovacích prostředích. Vlákna poskytují prostředky pro správu kooperativní interakce současně prováděných částí kódu v reálném čase. Vlákna jsou široce dostupná v prostředích, která podporují jazyk C (a jsou nativně podporována v mnoha dalších moderních jazycích), jsou známá mnoha programátorům a jsou obvykle dobře implementovaná, dobře dokumentovaná a dobře podporovaná. Protože však řeší rozsáhlý a složitý problém, obsahují mnoho výkonných a složitých prostředků a mají odpovídající obtížnou křivku učení. Proto v případech, kdy je potřeba pouze coroutine, může být použití vlákna nadbytečné.

Jedním z důležitých rozdílů mezi vlákny a coroutine je, že vlákna jsou obvykle preemptivně plánována, zatímco coroutine nikoliv. Protože vlákna lze kdykoli přeplánovat a mohou se vykonávat souběžně, musí si programy používající vlákna dávat pozor na zamykání. Naproti tomu, protože koroutiny lze přeplánovat pouze v určitých okamžicích programu a nevykonávají se souběžně, mohou se programy používající koroutiny často zamykání zcela vyhnout. Tato vlastnost je také uváděna jako výhoda událostmi řízeného nebo asynchronního programování.

Protože jsou vlákna plánována kooperativně, poskytují ideální základ pro implementaci výše uvedených coroutines. Systémová podpora pro vlákna však často chybí ve srovnání s podporou vláken.

Implementace pro CEdit

Pro implementaci koroutin pro obecné použití je třeba získat druhý zásobník volání, což je vlastnost, kterou jazyk C přímo nepodporuje. Spolehlivým (i když platformově specifickým) způsobem, jak toho dosáhnout, je použití malého množství inline assembleru pro explicitní manipulaci s ukazatelem zásobníku během počátečního vytváření koroutinu. Tento přístup doporučuje Tom Duff v diskusi o jeho relativních výhodách oproti metodě, kterou používá Protothreads. Na platformách, které poskytují systémové volání POSIX sigaltstack, lze druhý zásobník volání získat voláním odrazové funkce zevnitř obsluhy signálu a dosáhnout tak stejného cíle v přenositelném jazyce C, ovšem za cenu určité složitosti navíc. Knihovny C vyhovující POSIXu nebo Jednotné unixové specifikaci (SUSv3) poskytovaly takové rutiny jako getcontext, setcontext, makecontext a swapcontext, ale tyto funkce byly v POSIXu 1.2008 prohlášeny za zastaralé.

Po získání druhého zásobníku volání jednou z výše uvedených metod lze pak k implementaci přepínání mezi koroutiny použít funkce setjmp a longjmp ve standardní knihovně C. Tyto funkce ukládají, respektive obnovují ukazatel na zásobník, programový čítač, registry uložené při volání a jakýkoli jiný vnitřní stav podle požadavků ABI, takže návrat do koroutinu po odevzdání obnoví veškerý stav, který by byl obnoven při návratu z volání funkce. Minimalistické implementace, které nevyužívají funkce setjmp a longjmp, mohou stejného výsledku dosáhnout pomocí malého bloku inline assembleru, který vymění pouze ukazatel zásobníku a čítač programu a všechny ostatní registry vyřadí. To může být podstatně rychlejší, protože setjmp a longjmp musí konzervativně ukládat všechny registry, které mohou být podle ABI používány, zatímco metoda clobber umožňuje kompilátoru ukládat (přelitím na zásobník) pouze to, o čem ví, že je skutečně používáno.

Vzhledem k nedostatku přímé podpory jazyka napsalo mnoho autorů vlastní knihovny pro coroutines, které výše uvedené detaily skrývají. Dobrým příkladem tohoto žánru je knihovna libtask od Russe Coxe. Používá kontextové funkce, pokud je poskytuje nativní knihovna jazyka C; jinak poskytuje vlastní implementace pro ARM, PowerPC, Sparc a x86. Mezi další pozoruhodné implementace patří libpcl, coro, lthread, libCoroutine, libconcurrency, libcoro, ribs2, libdill., libaco a libco.

Kromě výše uvedeného obecného přístupu bylo učiněno několik pokusů přiblížit koroutiny v jazyce C pomocí kombinací podprogramů a maker. Příspěvek Simona Tathama, založený na Duffově zařízení, je pozoruhodným příkladem tohoto žánru a je základem pro Protothreads a podobné implementace. Kromě Duffových námitek poskytují Tathamovy vlastní komentáře upřímné zhodnocení omezení tohoto přístupu: „Pokud je mi známo, jedná se o nejhorší hackerský kousek v jazyce C, který byl kdy viděn v seriózním produkčním kódu.“ Hlavní nedostatky tohoto přiblížení spočívají v tom, že při neudržování samostatného rámce zásobníku pro každou koroutinu nejsou lokální proměnné zachovány napříč výstupy z funkce, není možné mít více vstupů do funkce a řízení lze předat pouze z rutiny nejvyšší úrovně.

Implementace pro C++Edit

  • C++ coroutines TS (Technical Specification), standard pro rozšíření jazyka C++ pro podmnožinu chování podobného koroutinám bez zásobníku, je ve vývoji. Visual C++ a Clang již podporují hlavní části ve jmenném prostoru std::experimental. coroutines Technical Specification
  • Boost.Coroutine – vytvořil Oliver Kowalke, od verze 1.53 je oficiálně vydanou přenosnou knihovnou coroutine boostu. Knihovna se opírá o Boost.Context a podporuje ARM, MIPS, PowerPC, SPARC a X86 v systémech POSIX, Mac OS X a Windows.
  • Boost.Coroutine2 – rovněž vytvořená Oliverem Kowalkem, je modernizovaná přenosná knihovna coroutine od verze boost 1.59. Využívá vlastností jazyka C++11, ale odstraňuje podporu symetrických koroutin.
  • Mordor – v roce 2010 Mozy otevřel knihovnu C++ implementující koroutiny s důrazem na jejich využití k abstrakci asynchronního I/O do známějšího sekvenčního modelu.
  • CO2 – stackless coroutine založený na tricích preprocesoru C++, poskytující emulaci await/yield.
  • ScummVM – Projekt ScummVM implementuje odlehčenou verzi stackless coroutines na základě článku Simona Tathama.
  • tonbit::coroutine – Implementace asymetrických coroutine v jazyce C++11 single .h prostřednictvím ucontext / fiber
  • Coroutines přistály v Clangu v květnu 2017, implementace v libc++ probíhá.
  • elle by Docker
  • oatpp-coroutines – coroutines bez stacků s plánováním určené pro I/O operace na úrovni vysoké konjunktury. Použito v experimentu s 5 miliony spojení WebSocket od Oat++. Součást webového frameworku Oat++.

Implementace pro C#Edit

  • MindTouch Dream – REST framework MindTouch Dream poskytuje implementaci coroutines založenou na vzoru iterátorů C# 2.0
  • Caliburn – framework vzorů obrazovek Caliburn pro WPF používá iterátory C# 2.0 pro usnadnění programování uživatelského rozhraní, zejména v asynchronních scénářích.
  • Power Threading Library – Knihovna Power Threading Library od Jeffreyho Richtera implementuje AsyncEnumerator, který poskytuje zjednodušený asynchronní programovací model pomocí koroutin založených na iterátorech.
  • Herní engine Unity implementuje koroutiny.
  • Servelat Pieces – Projekt Servelat Pieces od Jevhena Bobrova poskytuje transparentní asynchronnost pro služby WCF Silverlight a možnost asynchronního volání libovolné synchronní metody. Implementace je založena na iterátoru Coroutines společnosti Caliburn a blocích iterátorů jazyka C#
  • – Framework .NET 2.0+ nyní poskytuje funkčnost semi-coroutine (generátoru) prostřednictvím vzoru iterátor a klíčového slova yield.

C# 5.0 obsahuje podporu syntaxe await.

Implementace pro ClojureEdit

Cloroutine je knihovna třetí strany, která v jazyce Clojure poskytuje podporu pro coroutine bez zásobníku. Je implementována jako makro, staticky rozdělující libovolný blok kódu na libovolná volání var a emitující coroutine jako stavovou funkci.

Implementace pro DEdit

D implementuje koroutiny jako svou standardní knihovní třídu Vlákno Generátor umožňuje triviálně vystavit funkci vlákna jako vstupní rozsah, takže libovolné vlákno je kompatibilní s existujícími algoritmy rozsahu.

Implementace pro JavaEdit

V Javě existuje několik implementací pro koroutiny. Navzdory omezením daným abstrakcemi Javy nevylučuje JVM tuto možnost. Obecně se používají čtyři metody, ale dvě z nich narušují přenositelnost bajtkódu mezi JVM vyhovujícími standardům.

  • Modifikované JVM. Je možné sestavit opravený JVM tak, aby podporoval koroutiny nativněji. Pro JVM Da Vinci byly vytvořeny patche.
  • Změněn bajtový kód. Funkčnost koroutinů je možná přepsáním běžného bajtkódu Javy, a to buď za běhu, nebo při kompilaci. Sady nástrojů zahrnují Javaflow, Java Coroutines a Coroutines.
  • Meziplatformní mechanismy JNI. Ty využívají metody JNI implementované v operačním systému nebo knihovnách jazyka C k poskytování funkcí JVM.
  • Vláknové abstrakce. Knihovny koroutinů, které jsou implementovány pomocí vláken, mohou být těžké, ačkoli výkon se bude lišit v závislosti na implementaci vláken v JVM.

Implementace v JavaScriptuEdit

  • node-fibers
    • Fibjs – fibjs je běhové prostředí JavaScriptu postavené na JavaScriptovém enginu Chrome V8. Fibjs používá fibers-switch, synchronizační styl a neblokující I/O model pro vytváření škálovatelných systémů.
  • Od ECMAScriptu 2015 je k dispozici bezzásahová funkčnost coroutine prostřednictvím „generátorů“ a výrazů yield.

Implementace pro KotlinEdit

Kotlin implementuje coroutines jako součást knihovny první strany.

Implementace pro Modula-2Edit

Modula-2, jak ji definoval Wirth, implementuje koroutiny jako součást standardní knihovny SYSTEM.

Procedura NEWPROCESS() naplní kontext daný blokem kódu a prostorem pro zásobník jako parametry a procedura TRANSFER() předá řízení koroutinu danému kontextem koroutinu jako parametr.

Implementace v MonoEdit

Mono Common Language Runtime má podporu pro kontinuity, z nichž lze sestavit koroutiny.

Implementace v .NET Framework jako fibersEdit

Během vývoje .NET Framework 2.0 společnost Microsoft rozšířila návrh hostitelských rozhraní API Common Language Runtime (CLR) tak, aby zvládaly plánování založené na vláknech s ohledem na jejich použití v režimu vláken pro SQL server. Před vydáním byla z časových důvodů odstraněna podpora háčku pro přepínání úloh ICLRTask::SwitchOut, v důsledku čehož není použití rozhraní API fiber pro přepínání úloh v současné době v prostředí .NET Framework reálné.

Implementace pro PerlEdit

  • Coro

Coroutines jsou nativně implementovány ve všech backendech Raku.

Implementace pro PHPEdit

  • Amphp
  • Coroutine implementovány způsobem, který se podobá funkcím Pythonu a některým Go, mnoho příkladů ukazuje tamní kód převedený se stejným počtem řádků a chováním.

Implementace pro PythonEdit

  • Python 2.5 implementuje lepší podporu pro funkce podobné koroutinům, založené na rozšířených generátorech (PEP 342)
  • Python 3.3 zlepšuje tuto schopnost tím, že podporuje delegování na subgenerátor (PEP 380)
  • Python 3.4 zavádí komplexní asynchronní I/O rámec standardizovaný v PEP 3156, který zahrnuje koroutiny využívající delegování na subgenerátor
  • Python 3.5 zavádí explicitní podporu koroutin se syntaxí async/await (PEP 0492).
  • Od verze Python 3.7 se async/await staly vyhrazenými klíčovými slovy .
  • Eventlet
  • Greenlet
  • gevent
  • stackless python
  • Abandoned
    • Shrapnel (poslední vydání 2015)
    • Kamaelia (poslední vydání 2010)
    • cogen (poslední vydání 2009)
    • multitask (poslední vydání 2007)
    • chiral

Implementace pro RubyEdit

  • Ruby 1.9 nativně podporuje koroutiny, které jsou implementovány jako vlákna, což jsou polokoroutiny.
  • Implementace od Marca De Scheemaeckera
  • Ruby 2.5 a vyšší podporuje nativně koroutiny, které jsou implementovány jako vlákna
  • Implementace od Thomase W. Bransona

Implementace pro RustEdit

Rust podporuje koroutiny od verze 1.39 .existuje také alternativní asynchronní runtime (starší projekt než standardní runtime rustu) : tokio

Implementace pro ScalaEdit

Scala Coroutines je implementace coroutine pro Scalu. Tato implementace je rozšíření na úrovni knihovny, které se spoléhá na systém maker Scala při statické transformaci částí programu na objekty coroutine. Tato implementace jako taková nevyžaduje úpravy v JVM, takže je plně přenositelná mezi různými JVM a funguje s alternativními backendy Scaly, jako je Scala.js, který se kompiluje do JavaScriptu.

Scala Coroutines spoléhá na makro coroutine, které transformuje běžný blok kódu na definici coroutine. Takovou definici coroutine lze vyvolat pomocí operace call, která instancuje rámec coroutine. Rámec coroutine lze obnovit pomocí metody resume, která obnoví provádění těla coroutine, dokud se nedosáhne klíčového slova yieldval, které pozastaví rámec coroutine. Koroutiny Scala také vystavují metodu snapshot, která efektivně duplikuje koroutinu. Podrobný popis koroutin Scala se snímky se objevil na konferenci ECOOP 2018 spolu s jejich formálním modelem.

Implementace pro SchemeEdit

Protože Scheme poskytuje plnou podporu pro kontinuity, je implementace koroutin téměř triviální a vyžaduje pouze udržování fronty kontinuit.

Implementace pro SmalltalkEdit

Protože ve většině prostředí Smalltalku je prováděcí zásobník občanem první třídy, lze koroutiny implementovat bez další podpory knihovny nebo virtuálního stroje.

Implementace pro SwiftEdit

  • SwiftCoroutine – knihovna koroutin ve Swiftu pro iOS, macOS a Linux.

Implementace pro Tool Command Language (Tcl)Edit

Od verze 8.6 podporuje jazyk Tool Command Language koroutiny v jádře jazyka.

Implementace pro ValaEdit

Vala implementuje nativní podporu koroutin. Jsou navrženy pro použití s hlavní smyčkou Gtk, ale lze je použít i samostatně, pokud se zajistí, že koncové zpětné volání nebude muset být nikdy zavoláno před provedením alespoň jednoho yield.

Implementace v jazycích assembleruEdit

Jazyky assembleru závislé na stroji často poskytují přímé metody pro provádění koroutin. Například v MACRO-11, jazyku assembleru rodiny minipočítačů PDP-11, se „klasické“ přepnutí koroutiny provádí instrukcí „JSR PC,@(SP)+“, která skočí na adresu vyskočenou ze zásobníku a na zásobník posune aktuální (tj. adresu následující) instrukce. Na VAXenu (v Macro-32) je srovnatelná instrukce „JSB @(SP)+“. I na Motorole 6809 existuje instrukce „JSR „; všimněte si „++“, protože ze zásobníku jsou vyskočeny 2 bajty (adresy). Tato instrukce se hodně používá v (standardním) „monitoru“ Assist 09.

.

Napsat komentář