Coroutine

As de 2003, muitas das linguagens de programação mais populares, incluindo C e seus derivados, não têm suporte direto para coroutinas dentro da linguagem ou suas bibliotecas padrão. Isto é, em grande parte, devido às limitações da implementação de sub-rotinas baseadas em stack. Uma exceção é a biblioteca C++ Boost.Context, parte das bibliotecas de impulso, que suporta a troca de contexto no ARM, MIPS, PowerPC, SPARC e x86 no POSIX, Mac OS X e Windows. Coroutines podem ser construídas sobre Boost.Context.

Em situações onde um coroutine seria a implementação natural de um mecanismo, mas não está disponível, a resposta típica é usar um fechamento – uma subrotina com variáveis de estado (variáveis estáticas, muitas vezes booleanas) para manter um estado interno entre chamadas, e transferir o controle para o ponto correto. Condicionais dentro do código resultam na execução de diferentes caminhos de código em chamadas sucessivas, com base nos valores das variáveis de estado. Outra resposta típica é implementar uma máquina de estados explícita na forma de uma instrução de comutação grande e complexa ou através de uma instrução de goto, particularmente uma instrução de goto computado. Tais implementações são consideradas de difícil compreensão e manutenção, e uma motivação para o suporte a coroutinos.

Temas, e em menor extensão fibras, são uma alternativa aos coroutinos nos ambientes de programação mainstream atualmente. As roscas fornecem facilidades para o gerenciamento da interação cooperativa em tempo real da execução simultânea de peças de código. Os Threads estão amplamente disponíveis em ambientes que suportam C (e são suportados nativamente em muitas outras linguagens modernas), são familiares a muitos programadores, e são geralmente bem implementados, bem documentados e bem suportados. No entanto, ao resolverem um problema grande e difícil, incluem muitas instalações poderosas e complexas e têm uma curva de aprendizagem correspondentemente difícil. Como tal, quando um coroutino é tudo o que é necessário, o uso de um fio pode ser exagerado.

Uma diferença importante entre fios e coroutinas é que os fios são tipicamente programados de forma preventiva enquanto que os coroutinos não o são. Como as roscas podem ser remarcadas a qualquer instante e podem ser executadas simultaneamente, os programas que utilizam roscas devem ter cuidado com o travamento. Em contraste, como os coroutinos só podem ser reprogramados em pontos específicos do programa e não podem ser executados simultaneamente, os programas que utilizam coroutinos podem muitas vezes evitar completamente o bloqueio. Esta propriedade também é citada como um benefício da programação acionada por eventos ou assíncrona.

Desde que as fibras sejam programadas de forma cooperativa, elas fornecem uma base ideal para implementar os coroutinos acima. Entretanto, o suporte do sistema para fibras é muitas vezes inexistente em comparação com o suporte para threads.

Aplicações para CEdit

Para implementar coroutinas de propósito geral, uma segunda pilha de chamadas deve ser obtida, que é uma característica não suportada diretamente pela linguagem C. Uma maneira confiável (embora específica da plataforma) de conseguir isso é usar uma pequena quantidade de montagem em linha para manipular explicitamente o ponteiro da pilha durante a criação inicial do coroutine. Esta é a abordagem recomendada por Tom Duff em uma discussão sobre seus méritos relativos vs. o método usado por Protothreads. Em plataformas que fornecem a chamada do sistema POSIX sigaltstack, uma segunda pilha de chamadas pode ser obtida chamando uma função de trampolim de dentro de um manipulador de sinais para alcançar o mesmo objetivo em C portátil, ao custo de alguma complexidade extra. Bibliotecas C em conformidade com POSIX ou com a Especificação Single Unix (SUSv3), desde que rotinas como getcontext, setcontext, makecontext e swapcontext, mas estas funções foram declaradas obsoletas no POSIX 1.2008.

Após uma segunda pilha de chamadas ter sido obtida com um dos métodos listados acima, as funções setjmp e longjmp na biblioteca C padrão podem então ser usadas para implementar as comutações entre coroutinas. Estas funções salvam e restauram, respectivamente, o ponteiro da pilha, contador de programas, registros salvos por chamadas e qualquer outro estado interno conforme requerido pela ABI, de tal forma que retornar a um coroutino após ter rendido restaura todo o estado que seria restaurado ao retornar de uma chamada de função. Implementações minimalistas, que não piggyback fora das funções setjmp e longjmp, podem alcançar o mesmo resultado através de um pequeno bloco de montagem em linha que troca apenas o ponteiro da pilha e o contador de programa, e agarra todos os outros registros. Isto pode ser significativamente mais rápido, já que setjmp e longjmp devem armazenar conservadoramente todos os registros que podem estar em uso de acordo com a ABI, enquanto o método clobber permite que o compilador armazene (derramando na pilha) apenas o que ele sabe que está realmente em uso.

Devido à falta de suporte direto à linguagem, muitos autores escreveram suas próprias bibliotecas para coroutinas que escondem os detalhes acima. A biblioteca libtask de Russ Cox é um bom exemplo deste gênero. Ela usa as funções de contexto se elas forem fornecidas pela biblioteca C nativa; caso contrário, ela fornece suas próprias implementações para ARM, PowerPC, Sparc, e x86. Outras implementações notáveis incluem libpcl, coro, lthread, libCoroutine, libconcurrency, libcoro, ribs2, libdill., libaco, e libco.

Além da abordagem geral acima, várias tentativas foram feitas para aproximar coroutinas em C com combinações de sub-rotinas e macros. A contribuição de Simon Tatham, baseada no dispositivo de Duff, é um exemplo notável do gênero, e é a base para Protothreads e implementações similares. Além das objeções de Duff, os próprios comentários de Tatham fornecem uma avaliação franca das limitações desta abordagem: “Tanto quanto sei, esta é a pior peça de hackery C jamais vista em código de produção sério.” As principais deficiências desta aproximação são que, ao não manter um stack frame separado para cada coroteína, as variáveis locais não são preservadas através dos rendimentos da função, não é possível ter múltiplas entradas para a função, e o controle só pode ser produzido a partir da rotina de nível superior.

Implementações para C++Edit

  • C++ coroutines TS (Especificação Técnica), um padrão para extensões de linguagem C++ para um subconjunto sem pilha de comportamento semelhante à coroteína, está em desenvolvimento. Visual C++ e Clang já suportam porções importantes no namespace std::experimental. coroutines Especificação Técnica
  • Boost.Coroutine – criado por Oliver Kowalke, é a biblioteca de coroutinas portáteis de impulso lançada oficialmente desde a versão 1.53. A biblioteca depende do Boost.Context e suporta ARM, MIPS, PowerPC, SPARC e X86 no POSIX, Mac OS X e Windows.
  • Boost.Coroutine2 – também criado por Oliver Kowalke, é uma biblioteca portátil de coroutina modernizada desde a versão 1.59. Tira partido das funcionalidades do C++11, mas remove o suporte para coroutinas simétricas.
  • Mordor – Em 2010, a Mozy abriu uma biblioteca C++ implementando coroutinas, com ênfase na sua utilização para abstrair E/S assíncronas num modelo sequencial mais familiar.
  • CO2 – coroutinas sem empilhamento baseadas em truques de pré-processador C++, fornecendo emulação await/yield.
  • ScummVM – O projeto ScummVM implementa uma versão leve de coroutinas sem empilhamento baseada no artigo de Simon Tatham.
  • tonbit::coroutine – Implementação C++11 de corotina simples .h assimétrica via ucontext / fibra
  • Coroutinas aterrissadas em Clang em maio de 2017, com a implementação da libc++ em andamento.
  • elle by Docker
  • aveia-coroutinas – coroutinas sem empilhamento com programação projetada para operações de E/S de alto nível de moeda. Utilizado na experiência de 5 milhões de conexões WebSocket pela Oat++. Parte do framework web Oat++.

Implementações para C#Edit

  • MindTouch Dream – O framework MindTouch Dream REST fornece uma implementação de coroutinos baseada no padrão C# 2.0 iterator
  • Caliburn – O framework de padrões de tela Caliburn para WPF usa iteradores C# 2.0 para facilitar a programação UI, particularmente em cenários assíncronos.
  • Power Threading Library – A Power Threading Library de Jeffrey Richter implementa um AsyncEnumerator que fornece um modelo simplificado de programação assíncrona usando coroutines baseados em iteradores.
  • O motor do jogo Unity implementa coroutines.
  • Servelat Pieces – O projeto Servelat Pieces de Yevhen Bobrov fornece assíncronia transparente para serviços Silverlight WCF e capacidade de chamar qualquer método assíncrono. A implementação é baseada no iterador Caliburn Coroutines e blocos de iteradores C#.
  • – O framework .NET 2.0+ agora fornece funcionalidade semi-corrotina (gerador) através do padrão de iterador e da palavra-chave yield.

C# 5.0 inclui suporte a sintaxe de espera.

Implementações para ClojureEdit

Cloroutine é uma biblioteca de terceiros que fornece suporte a coroutinas sem empilhamento em Clojure. Ela é implementada como uma macro, dividindo estaticamente um bloco de código arbitrário em chamadas de var arbitrárias e emitindo a coroutina como uma função de estado.

Implementações para DEdit

D implementa coroutinos como sua classe de biblioteca padrão Fiber A generator torna trivial expor uma função de fibra como um intervalo de entrada, tornando qualquer fibra compatível com algoritmos de intervalo existentes.

Implementações para JavaEdit

Existem várias implementações para coroutinos em Java. Apesar das restrições impostas pelas abstrações do Java, a JVM não exclui essa possibilidade. Há quatro métodos gerais utilizados, mas dois quebram a portabilidade bytecode entre JVMs compatíveis com padrões.

  • JVMs modificadas. É possível construir um JVM corrigido para suportar coroutinas mais nativamente. A JVM Da Vinci teve os patches criados.
  • Modificado bytecode. A funcionalidade Coroutine é possível através da reescrita de bytecodes Java regulares, seja em tempo real ou em tempo de compilação. Os toolkits incluem Javaflow, Java Coroutines e Coroutines.
  • Mecanismos JNI específicos da plataforma. Estes usam métodos JNI implementados no SO ou bibliotecas C para fornecer a funcionalidade à JVM.
  • Reabstrações de texto. As bibliotecas Coroutine que são implementadas usando threads podem ser pesadas, embora o desempenho varie com base na implementação de threads da JVM.

Implementações em JavaScriptEdit

  • node-fibers
    • Fibjs – fibjs é um JavaScript runtime construído sobre o motor JavaScript V8 do Chrome. fibjs usa o modelo fibers-switch, sync style, e non-blocking I/O para construir sistemas escaláveis.
  • Desde ECMAScript 2015, a funcionalidade coroutine sem empilhamento através de “geradores” e expressões de rendimento é fornecida.

Implementações para KotlinEdit

Kotlin implementa coroutinas como parte de uma biblioteca de primeira parte.

Implementações para Modula-2Edit

Modula-2 como definido por Wirth implementos coroutines como parte da biblioteca SYSTEM padrão.

O procedimento NEWPROCESS() preenche num contexto dado um bloco de código e espaço para uma pilha como parâmetros, e o procedimento TRANSFER() transfere o controle para uma coroutina dado o contexto da coroutina como seu parâmetro.

Implementação no MonoEdit

O Runtime da Linguagem Comum Mono tem suporte para continuações, a partir das quais coroutinas podem ser construídas.

Implementação no Framework .NET como fibersEdit

Durante o desenvolvimento do Framework .NET 2.0, a Microsoft ampliou o design das APIs de hospedagem da Linguagem Comum (CLR) para lidar com o agendamento baseado em fibra com vistas ao seu uso em modo de fibra para servidor SQL. Antes do lançamento, o suporte ao gancho de comutação de tarefas ICLRTask::SwitchOut foi removido devido a restrições de tempo. Consequentemente, o uso da API de fibra para comutar tarefas não é atualmente uma opção viável no .NET Framework.

Implementações para PerlEdit

  • Coro

Coroutines são nativamente implementadas em todos os backends Raku.

Implementações para PHPEdit

  • Amphp
  • Coroutine implementado de uma forma que se assemelha às funções Python, e alguns Go, muitos exemplos mostrando lá código convertido com o mesmo número de linhas e comportamento.

Implementações para PythonEdit

  • Python 2.5 implementa um melhor suporte para funcionalidades parecidas com a coronha, baseado em geradores extendidos (PEP 342)
  • Python 3.3 melhora esta habilidade, suportando a delegação a um subgerador (PEP 380)
  • Python 3.4 introduz uma estrutura de E/S assíncrona abrangente, padronizada no PEP 3156, que inclui coroutinas que alavancam a delegação a um subgerador
  • Python 3.5 introduz suporte explícito para coroutinas com sintaxe async/await (PEP 0492).
  • Desde Python 3.7 async/await tornaram-se palavras-chave reservadas .
  • Eventlet
  • Greenlet
  • gevent
  • python sem empilhamento
  • Abandoned
    • Shrapnel (última versão 2015)
    • Kamaelia (última versão 2010)
    • cogen (última versão 2009)
    • multitask (última versão 2007)
    • chiral

Implementações para RubyEdit

  • Ruby 1.9 suporta coroutinas nativas que são implementadas como fibras, que são semi-coroutinas.
  • Uma implementação de Marc De Scheemaecker
  • Ruby 2.5 e superiores suporta coroutinas nativamente que são implementadas como fibras
  • Uma implementação de Thomas W Branson

Implementações para RustEdit

Coroutinas de suporte à ferrugem desde a versão 1.Existe também um tempo de execução assíncrono alternativo (projeto mais antigo que o tempo de execução padrão do rust) : tokio

Implementações para ScalaEdit

Scala Coroutines é uma implementação coroutina para Scala. Esta implementação é uma extensão em nível de biblioteca que se baseia no sistema macro Scala para transformar estaticamente seções do programa em objetos coroutinos. Como tal, esta implementação não requer modificações na JVM, portanto é totalmente portável entre diferentes JVMs e funciona com backends Scala alternativos, como Scala.js, que compila para JavaScript.

Scala Coroutines depende da macro coroutine que transforma um bloco normal de código em uma definição coroutina. Tal definição coroutina pode ser invocada com a operação call, que instancia um frame coroutina. Um frame coroutino pode ser retomado com o método resume, que retoma a execução do corpo do coroutino, até atingir uma palavra-chave yieldval, que suspende o frame coroutino. Scala Coroutines também expõe um método snapshot, que efetivamente duplica o coroutine. Uma descrição detalhada dos coroutinos Scala com instantâneos apareceu no ECOOP 2018, juntamente com o seu modelo formal.

Implementações para SchemeEdit

Desde que Scheme fornece suporte total para continuações, implementar coroutines é quase trivial, requerendo apenas que uma fila de continuações seja mantida.

Implementações para SmalltalkEdit

Desde que, na maioria dos ambientes Smalltalk, a pilha de execução é um cidadão de primeira classe, coroutines podem ser implementados sem biblioteca adicional ou suporte a VM.

Implementações para SwiftEdit

  • SwiftCoroutine – Biblioteca Swift Coroutines para iOS, macOS e Linux.

Implementação para linguagem de comando de ferramentas (Tcl)Edit

Desde a versão 8.6, a linguagem de comando Tool Command Language suporta coroutines na linguagem principal.

Implementações para ValaEdit

Vala implementa suporte nativo para coroutines. Eles são projetados para serem usados com um Loop Principal Gtk, mas podem ser usados sozinhos se for tomado o cuidado de garantir que o retorno final nunca terá que ser chamado antes de fazer, pelo menos, um retorno.

Implementações em linguagens de montagemEdit

Linguagens de montagem dependentes da máquina freqüentemente fornecem métodos diretos para execução de coroutinos. Por exemplo, no MACRO-11, a linguagem assembly da família de minicomputadores PDP-11, o comutador “clássico” coroutine é efectuado pela instrução “JSR PC,@(SP)+”, que salta para o endereço popped da pilha e empurra o endereço de instrução actual (i.e. o do próximo) para a pilha. No VAXen (em Macro-32) a instrução comparável é “JSB @(SP)+”. Mesmo em um Motorola 6809 existe a instrução “JSR “; note o “++”, pois 2 bytes (de endereço) são poppedidos da pilha. Esta instrução é muito usada no ‘monitor’ Assist 09.

(padrão).

Deixe um comentário