Coroutine

Coroutine

Coroutines are computer program components that generalize subroutines to allow multiple entry points for suspending and resuming execution at certain locations. Coroutines are well-suited for implementing more familiar program components such as cooperative tasks, iterators, infinite lists and pipes.

The term "coroutine" was originated by Melvin Conway in a 1963 paper.[1]

Contents

Comparison with subroutines

"Subroutines are special cases of ... coroutines." —Donald Knuth.[2] When subroutines are invoked, execution begins at the beginning and once a subroutine exits, it is finished; an instance of a subroutine only returns once. Coroutines are similar except they can also exit by yielding to, or calling, other coroutines, which allows them to be re-entered at that point again; from the coroutine's point of view, it is not exiting at all but simply calling another coroutine.[2]

Any subroutine can be implemented as a coroutine by simply not using the yielding feature of coroutines.[3]

To implement a programming language with subroutines, only a single stack that can be preallocated at the beginning of program execution is needed. In contrast, coroutines, able to call on other coroutines as peers, are best implemented using continuations.[citation needed] Continuations may require allocation of additional stacks and therefore are more commonly implemented in garbage-collected high-level languages.[citation needed] Coroutine creation can be done cheaply by preallocating stacks or caching previously allocated stacks.[citation needed]

Here is a simple example of how coroutines can be useful. Suppose you have a consumer-producer relationship where one routine creates items and adds them to a queue and another removes items from the queue and uses them. For reasons of efficiency, you want to add and remove several items at once. The code might look like this:

var q := new queue

coroutine produce
    loop
        while q is not full
            create some new items
            add the items to q
        yield to consume

coroutine consume
    loop
        while q is not empty
            remove some items from q
            use the items
        yield to produce

The queue is then completely filled or emptied before yielding control to the other coroutine using the yield command. The further coroutines calls are starting right after the yield, in the outer coroutine loop.

Although this example is often used to introduce multithreading, it is not necessary to have two threads to achieve this: the yield statement can be implemented by a branch directly from one routine into the other.

Coroutines and generators

Generators are also a generalisation of subroutines, but with at first sight less expressive power than coroutines; since generators are primarily used to simplify the writing of iterators, the yield statement in a generator does not specify a coroutine to jump to, but rather passes a value back to a parent routine. However, it is still possible to implement coroutines on top of a generator facility, with the aid of a top-level dispatcher routine (a trampoline, essentially) that passes control explicitly to child generators identified by tokens passed back from the generators:

var q := new queue

generator produce
    loop
        while q is not full
            create some new items
            add the items to q
        yield consume

generator consume
    loop
        while q is not empty
            remove some items from q
            use the items
        yield produce

subroutine dispatcher
    var d := new dictionary〈generatoriterator〉
    d[produce] := start produce
    d[consume] := start consume
    var current := produce
    loop
        current := next d[current]

A number of implementations of coroutines for languages with generator support but no native coroutines (e.g. Python[4] prior to 2.5) use this or a similar model.

Common uses of coroutines

Coroutines are useful to implement the following:

  • State machines within a single subroutine, where the state is determined by the current entry/exit point of the procedure; this can result in more readable code.
  • Actor model of concurrency, for instance in computer games. Each actor has its own procedures (this again logically separates the code), but they voluntarily give up control to central scheduler, which executes them sequentially (this is a form of cooperative multitasking).
  • Generators, and these are useful for input/output and for generic traversal of data structures.

Programming languages supporting coroutines

Since continuations can be used to implement coroutines, programming languages that support them can also quite easily support coroutines.

Coroutine alternatives and implementations

Coroutines originated as an assembly-language technique, but are supported in some high-level languages. Simula and Modula-2 are two early examples of languages supporting coroutines, Lua and Go are two recent examples.

As of 2003, many of the most popular programming languages, including C and its derivatives, do not have direct support for coroutines within the language or their standard libraries. (This is, in large part, due to the limitations of stack-based subroutine implementation).

In situations where a coroutine would be the natural implementation of a mechanism, but is not available, the typical response is to create a subroutine that uses an ad-hoc assemblage of boolean flags and other state variables to maintain an internal state between calls. Conditionals within the code result in the execution of different code paths on successive calls, based on the values of the state variables. Another typical response is to implement an explicit state machine in the form of a large and complex switch statement. Such implementations are difficult to understand and maintain[citation needed].

Threads (and to a lesser extent fibers) are an alternative to coroutines in mainstream programming environments today. Threads provide facilities for managing the realtime cooperative interaction of "simultaneously" executing pieces of code. Threads are widely available in environments that support C (and are supported natively in many other modern languages), are familiar to many programmers, and are usually well-implemented, well-documented and well-supported. However, as they solve a large and difficult problem they include many powerful and complex facilities and have a correspondingly difficult learning curve. As such, when a coroutine is all that is needed, using a thread can be overkill.

One important difference between threads and coroutines is that threads are typically preemptively scheduled while coroutines are not. Because threads can be rescheduled at any instant and can execute concurrently, programs using threads must be careful about locking. In contrast, because coroutines can only be rescheduled at specific points in the program and do not execute concurrently, programs using coroutines can often avoid locking entirely. (This property is also cited as a benefit of event-driven or asynchronous programming.)

Since fibers are cooperatively scheduled, they provide an ideal base for implementing coroutines above.[9] However, system support for fibers is often lacking compared to that for threads.

Implementation in the .NET Framework as fibers

During the development of the .NET Framework 2.0, Microsoft extended the design of the CLR hosting APIs to handle fiber-based scheduling with an eye towards its use in fiber-mode for SQL server.[10] Prior to release, support for the task switching hook ICLRTask::SwitchOut was removed due to time constraints.[11] Consequently the use of the fiber API to switch tasks is currently not a viable option in the .NET framework.

Implementation in Mono

The Mono CLR has support for continuations,[12] from which coroutines can be built.

Implementations for Java

There are several implementations for Coroutines in java, despite the difficulty imposed by the abstractions within the Java language, the JVM does not preclude the possibility[13]. There are four general methods used.

  • Modified JVMs. It is possible to build a patched JVM to support Coroutines more natively. The Da Vinci JVM has had patches created[14].
  • Modified Bytecode. Coroutine functionality is possible by rewriting regular java bytecode, either on the fly or at compile time. One Java Coroutines Project.
  • Platform-specific JNI mechanisms. These use JNI methods implemented in the OS or C libraries to provide the functionality to the JVM.
  • Thread abstractions. Coroutine libraries which are implemented using threads may be heavyweight, though performance will vary based on the JVM's thread implementation.

Implementations for C

Several attempts have been made, with varying degrees of success, to implement coroutines in C with combinations of subroutines and macros. Simon Tatham's contribution[15] is a good example of the genre, and his own comments provide a good evaluation of the limitations of this approach. The use of such a device truly can improve the writability, readability and maintainability of a piece of code, but is likely to prove controversial. In Tatham's words: "Of course, this trick violates every coding standard in the book... [but] any coding standard which insists on syntactic clarity at the expense of algorithmic clarity should be rewritten. If your employer fires you for using this trick, tell them that repeatedly as the security staff drag you out of the building."

A more reliable approach to implementing coroutines in C is to give up on absolute portability and write processor-family-specific implementations, in assembly, of functions to save and restore a coroutine context. The standard C library includes functions named setjmp and longjmp which can be used to implement a form of coroutine. Unfortunately, as Harbison and Steele note, "the setjmp and longjmp functions are notoriously difficult to implement, and the programmer would do well to make minimal assumptions about them."[16] What this means is if Harbison and Steele's many cautions and caveats are not carefully heeded, uses of setjmp and longjmp that appear to work in one environment may not work in another. Worse yet, faulty implementations of these routines are not rare.[citation needed] Indeed, setjmp/longjmp, because it only countenances a single stack, cannot be used to implement natural coroutines, as variables located on the stack will be overwritten as another coroutine uses the same stack space.[17]

Thus for stack-based coroutines in C, functions are needed to create and jump between alternate stacks. A third function, which can usually be written in machine-specific C, is needed to create the context for a new coroutine. C libraries complying to POSIX or the Single Unix Specification provide such routines as getcontext, setcontext, makecontext and swapcontext. The setcontext family of functions is thus considerably more powerful than setjmp/longjmp, but conforming implementations are as rare if not rarer. The main shortcoming of this approach is that the coroutine's stack is a fixed size and cannot be grown during execution. Thus, programs tend to allocate much more stack than they actually need in order to avoid the potential for stack overflow.

Due to the limitations of standard libraries, some authors have written their own libraries for coroutines. Russ Cox's libtask library[18] is a good example of this genre. It uses the context functions if they are provided by the native C library; otherwise it provides its own implementations for ARM, PowerPC, Sparc, and x86. Other notable implementations include libpcl,[19] coro,[20] libCoroutine,[21] libconcurrency[22] and libcoro.[23]

Implementations for C++

  • Boost.Coroutine - Giovanni P. Deretta created while his "Google Summer of Code 2006" project this portable coroutine library which relies heavily on boost and C++ templates. The Boost.Coroutine Documentation is hosted on a non-boost site because the library is unfinished and not part of the official boost release.
  • Mordor - In 2010, Mozy open sourced a C++ library implementing coroutines, with an emphasis on using them to abstract Asynchronous I/O into a more familiar sequential model.[24]

Implementations for C#

  • MindTouch Dream - The MindTouch Dream REST framework provides an implementation of Coroutines based on the C# 2.0 iterator pattern
  • Caliburn - The Caliburn screen patterns framework for WPF uses C# 2.0 iterators to ease UI programming, particularly in asynchronous scenarios.
  • Power Threading Library - The Power Threading Library by Jeffrey Richter implements an AsyncEnumerator that provides simplified Asynchronous Programming Model using iterator-based Coroutines.
  • Servelat Pieces - The Servelat Pieces project by Yevhen Bobrov provides transparent asynchrony for Silverlight WCF services and ability to asynchronously call any synchronous method. The implementation is based on Caliburn's Coroutines iterator and C# iterator blocks.
  • [12] - The .NET 2.0+ Framework now provides coroutine functionality through the iterator pattern and yield keyword.

Implementations for Python

Implementations for Ruby

Implementations for Perl

Coroutines will also be a part of Perl 6.[citation needed]

Implementations for Smalltalk

Since, in most Smalltalk environments, the execution stack is a first-class citizen, coroutines can be implemented without additional library or VM support.

Implementations for Scheme

Since Scheme provides full support for continuations, the implementation of coroutines is nearly trivial, requiring only that a queue of continuations be maintained.

Implementations for Delphi

Implementations in assembly languages

Machine-dependent assembly languages often provide direct methods for coroutine execution. For example, in MACRO-11, the assembly language of the PDP-11 family of minicomputers, the “classic” coroutine switch is effected by the instruction "JSR PC,@(SP)+" (which assembles as octal "004736") which jumps to the address popped from the stack and pushes the current (i.e that of the next) instruction address onto the stack. On VAXen (in Macro-32) the comparable instruction is "JSB @(SP)+" (which assembles as hex "9E 16" as the assembler shows it (with in effect bytes reversed). Even on a Motorola 6809 there is the instruction "JSR [,S++]", which assembles as (hex) "AD F1"; note the "++", as 2 bytes (of address) are popped from the stack. This instruction is much used in the (standard) 'monitor' Assist 09.

Simply calling back the routine whose address is on the top of the stack, does not, of course, exhaust the possibilities in assembly language(s)!

See also

  • Unix pipes – a kind of coroutine used for communicating between programs[25]

References

  1. ^ Conway, Melvin E. (July 1963). "Design of a Separable Transition-Diagram Compiler". Communications of the ACM (New York, NY, USA: Association for Computing Machinery) 6 (7): 396–408. doi:10.1145/366663.366704.  edit
  2. ^ a b Knuth, Donald Ervin (1997). Fundamental Algorithms. The Art of Computer Programming. 1 (3rd ed.). Addison-Wesley. Section 1.4.2: Coroutines, pp. 193–200. ISBN 0-201-89683-4.. 
  3. ^ Perlis, Alan J. (September 1982). "Epigrams on programming". ACM SIGPLAN Notices (New York, NY, USA: Association for Computing Machinery) 17 (9): 7–13. doi:10.1145/947955.1083808. Archived from the original on January 17, 1999. http://web.archive.org/web/19990117034445/http://www-pu.informatik.uni-tuebingen.de/users/klaeren/epigrams.html. "6. Symmetry is a complexity reducing concept (co-routines include sub-routines); seek it everywhere" 
  4. ^ Mertz, David (July 1, 2002). "Generator-based State Machines". Charming Python. IBM developerWorks. Archived from the original on February 2, 2011. http://www.webcitation.org/5wCZa062h. Retrieved Feb 2, 2011. 
  5. ^ "Coroutine: Type-safe coroutines using lightweight session types". http://hackage.haskell.org/cgi-bin/hackage-scripts/package/Coroutine. 
  6. ^ "The Coroutines Module (coroutines.hhf)". HLA Standard Library Manual. http://webster.cs.ucr.edu/AsmTools/HLA/HLADoc/HLAstdlib/hlastdliba12.html. 
  7. ^ "New in JavaScript 1.7". http://developer.mozilla.org/en/docs/New_in_JavaScript_1.7. 
  8. ^ McCartney, J. "Rethinking the Computer Music Programming Language: SuperCollider". Computer Music Journal, 26(4):61-68. MIT Press, 2002.
  9. ^ Implementing Coroutines for .NET by Wrapping the Unmanaged Fiber API, Ajai Shankar, MSDN Magazine
  10. ^ [1], Chris Brumme, cbrumme's WebLog
  11. ^ [2], Dino Viehland, Dino's Blog
  12. ^ [3] Mono Continuations
  13. ^ [4] Lukas Stadler's JVM Continuations (pdf)
  14. ^ [5] Remi Forax post on JVM coroutine/continuation/fiber
  15. ^ Simon Tatham's implementation of coroutines in C.
  16. ^ C: A Reference Manual. Samuel P. Harbison and Guy L. Steele, Jr. Third edition; Prentice-Hall, 1991, ISBN 0-13-110933-2
  17. ^ Building coroutines. Dr. C.-K. Shene, Michigan Technical University
  18. ^ [6] - Russ Cox's libtask coroutine library for FreeBSD, Linux, Mac OS X, and SunOS
  19. ^ Portable Coroutine Library - C library using POSIX/SUSv3 facilities
  20. ^ [7] - Edgar Toernig's coro library for x86, Linux & FreeBSD
  21. ^ [8] - libCoroutine for FreeBSD, Linux, OS X PPC and x86, SunOS, Symbian and others.
  22. ^ [9] - libconcurrency is a simple C library for portable stack-switching coroutines
  23. ^ [10] - portable coroutines in C, used as the basis for the Coro perl module.
  24. ^ [11] - Open Source and Mozy: The Debut of Mozy Code
  25. ^ Ritchie, Dennis M. (1980). "The Evolution of the Unix Time-sharing System". Lecture Notes in Computer Science 79 (Language Design and Programming Methodology): 25–35. http://cm.bell-labs.com/cm/cs/who/dmr/hist.html. 

External links


Wikimedia Foundation. 2010.

Игры ⚽ Нужна курсовая?

Look at other dictionaries:

  • Coroutine — Dans un programme, une coroutine est une unité de traitement qui s apparente à une routine. À ceci près, que la sortie d une routine met fin à la routine, alors que la sortie de la coroutine peut être le résultat d une suspension de son… …   Wikipédia en Français

  • Coroutine — In der Informatik sind Koroutinen (auch Coroutinen) eine Verallgemeinerung des Konzepts einer Prozedur oder Funktion. Der prinzipielle Unterschied zwischen Koroutinen und Prozeduren ist, dass Koroutinen ihren Ablauf unterbrechen und später wieder …   Deutsch Wikipedia

  • coroutine — koprogramė statusas T sritis informatika apibrėžtis Programos komponentas, veikiantis panašiai kaip ↑paprogramė, tiktai, į jį kreipiantis pakartotinai, pradedama vykdyti nuo tos vietos, kurioje baigėsi ankstesnio kreipimosi į jį vykdymas.… …   Enciklopedinis kompiuterijos žodynas

  • coroutine — noun A piece of code that performs a task, and that can be passed new input and return output more than once. Although a powerful tool, coroutines can be hard to understand due to the way data can flow back and forth between sections of the code …   Wiktionary

  • coroutine — ● n. m. ►PROG routine qui débute son exécution à l endroit où le programme a été interrompu pour la dernière fois, et qui n a pas besoin de rendre la main …   Dictionnaire d'informatique francophone

  • Modula-2 — Modula Apparu en 1977 Auteur Niklaus Wirth Paradigme générique, procédural, impératif …   Wikipédia en Français

  • Modula-3 — Modula 2 Modula Apparu en 1977 Auteur …   Wikipédia en Français

  • Modula-II — Modula 2 Modula Apparu en 1977 Auteur …   Wikipédia en Français

  • Simula — (Simple universal language) a été créé en 1962 sous la dénomination Simula I par Ole Johan Dahl et Kristen Nygaard à partir d Algol 60. Le langage évolua en 1967 sous le nom de Simula 67 en implantant le premier le modèle de classe de Hoare… …   Wikipédia en Français

  • Сопрограмма — (англ. coroutine) компонент программы, обобщающий понятие подпрограммы, который дополнительно поддерживает множество входных точек (а не одну как подпрограмм …   Википедия

Share the article and excerpts

Direct link
Do a right-click on the link above
and select “Copy Link”