This section gives a nearly-formal specification of the extensions to the C# grammar associated with Cω (Cω) concurrency constructs.
Note This section is part of the formal specification of the Cω language. It is not intended as a tutorial. For help in learning about how to use the Cω concurrency constructs in your code, see Cω Tutorials.
Asynchronous methods have a return type of async, which is a new keyword in Cω (similar to void).
Each asynchronous method in a class must have an explicit async-declaration of the form
attributesopt method-modifiersopt async member-name(formal-parameter-listopt);
Async-declarations can appear in interfaces and one can also declare asynchronous delegate types:
attributesopt delegate-modifiersopt delegate async identifier(formal-parameter-listopt);
Chords (or synchronization patterns) define the behaviours of asynchronous methods and of those (ordinary) synchronous methods which synchronize with asynchronous ones. The definition method-declaration in the C# grammar is replaced by the following in Cω:
sync-method-header sync-chord+
where sync-method-header is
attributesopt method-modifiersopt (void|type) member-name(formal-parameter-listopt)
and sync-chord is
(& async-header)* method-body
where async-header is
member-name(formal-parameter-listopt)
There are also another form of class-member-declaration in Cω, viz. async-chord, which is defined by
when async-header(&async-header)* method-body
where when is another new keyword.
ref or out parameters.member-name in any async-header within a class declaration must correspond to an async-declaration of the same name and parameter types within that class.sync-chord and/or async-chord, all the asynchronous methods within a single chord must be distinct. Within a single class, a particular synchronous method may only occur in a single method-declaration.sync-chord in a given method-declaration, the formal parameters to the synchronous method and the formal parameters in each async-header in the chord must all be distinct. Similarly, the parameters in each async-header in an async-chord must all be distinct.method-body in a method-declaration are the formal parameters of the synchronous method unioned with those of all the async-header s in the enclosing sync-chord. Similarly, the bound variables of a method-body in an async-chord are all the parameters of the associated async-header s.method-body in a method-declaration must match the declared return type of the synchronous method being defined. Method bodies in async-chords may not return a value.struct definition, only static methods may occur in non-trivial chords.C is marked override then any method which appears in a chord with the \emph{overridden} method (in the superclass where that is declared) must also be overridden in the class C.This last restriction means that, for example, the following is ill-formed:
public class C {
virtual async g();
virtual void f() & g() {
...
}
...
}
public class D : C {
override void f() {
...
}
...
}
Since in D, we have overridden the definition of f() in C but have not also overridden g() with which it appears in a chord. (If g() were non-virtual then the definition of C alone would generate a warning, since f() would be declared virtual but could never be legally overridden.)
We treat async as a subtype of void and allow covariant return types just in the case of these two (pseudo)types. Thus
async method may override a void one, void delegate may be created from an async method, andasync method may implement a void method in an interfacebut not conversely. This design makes intuitive sense (an async method is a void one, but has the extra property of returning `immediately') and also maximizes compatibility with existing C# code (superclasses, interfaces and delegate definitions) making use of void.
An asynchronous method will return (essentially) immediately to its caller without blocking and with no result. Asynchronous methods are automatically given the OneWay attribute, so that calls to them over remoting are genuinely non-blocking.
Each chord involves a non-empty set of methods. A chord is enabled on an object (or class, in the case of static chords) when at least one unconsumed call has been made to each of those methods on that object (or class). When an asynchronous method is called on an object (or class) and no chord is thereby enabled, the call is queued. When a synchronous method is called on an object (or class) and no chord is enabled, the calling thread is blocked and queued. When at least one chord is enabled on an object (or class) then (an unspecified) one of them will be chosen to fire, which consumes (dequeues) one (unspecified) call of each of the methods in the chord and executes the body of the chord with the formal parameters bound to the arguments of those calls. In the case of a synchronous chord, the body is executed in the reawoken thread which made the consumed synchronous call. In the case of an asynchronous chord, the body is executed in a new thread.