Introduction to Polyphonic C#
Page: Prev 1, 2, 3 Next
The Basic Idea
In Polyphonic C#, methods can be defined as either synchronous or asynchronous. When a synchronous method is called, the caller is blocked until the method returns, as is normal in C#. However, when an asynchronous method is called, there is no result and the caller proceeds immediately without being blocked. Thus from the caller's point of view, an asynchronous method is like a void one, but with the useful extra guarantee of returning immediately. We often refer to asynchronous methods as messages, as they are a one-way communication from caller to receiver (think of posting a letter rather as opposed to asking a question during a face-to-face conversation).
By themselves, asynchronous method declarations are not particularly novel. Indeed, .NET already has a widely-used set of library classes which allow any method to be invoked asynchronously (though note that in this standard pattern it is the caller who decides to invoke a method asynchronously, whereas in Polyphonic C# it is the callee (defining) side which declares a particular method to be asynchronous). The significant innovation in Polyphonic C# is the way in which method bodies are defined.
In most languages, including C#, methods in the signature of a class are in bijective correspondence with the code of their implementations - for each method which is declared, there is a single, distinct definition of what happens when that method is called. In Polyphonic C#, however, a body may be associated with a set of (synchronous and/or asynchronous) methods. We call such a definition a chord, and a particular method may appear in the header of several chords. The body of a chord can only execute once all the methods in its header have been called. Thus, when a polyphonic method is called there may be zero, one, or more chords which are enabled:
Example: A Simple Buffer
Here is the simplest interesting example of a Polyphonic C# class:
This class declares two methods: a synchronous one, get(), which takes no arguments and returns a string, and an asynchronous one, put(), which takes a string argument and (like all asynchronous methods) returns no result. These two methods appear (separated by an ampersand) in the header of a single chord, the body of which consists of the return statement.
Now assume that b is an instance of Buffer and that producer and consumer threads wish to communicate via b. Producers make calls to put(), which, since the method is asynchronous, do not block. Consumers make calls to get(), which, since the method is synchronous, will block until or unless there is a matching call to put(). Once b has received both a put() and a get(), the body runs and the argument to the put() is returned as the result of the call to get(). Multiple calls to get() may be pending before a put() is received to reawaken one of them and multiple calls to put() may be made before their arguments are consumed by subsequent get()s. Note that
Page: Prev 1, 2, 3 Next