【正文】
virtual async h() { /? body2 ?/ } } } class D : C { override async g () { /? body3 ?/ } one would, by overriding g (), have also ?half ? overridden f (). More pragmatically, removing the restriction on inheritance makes it all too easy to introduce inadvertent deadlock (or ?async leakage?). If the above code were legal, then code writ。 when methods are overridden, all their chords must also be pletely overridden. If one takes the view that the implementation of a given method consists of all the synchronization and bodies of all the chords in which it appears then our inheritance restriction seems not unreasonable, since in (illegal) 4Neither ref nor out parameters make sense for asynchronous messages, since they are both passed as addresses of locals in a stack frame that may have disappeared when the message is processed. 5In C , methods that are intended to be overridable in subclasses are explicitly marked as such by use of the virtual modi?er, while methods that are intended to override ones inherited from a superclass must explicitly say so with the override modi?er. code such as class C { virtual void f () amp。 } } Now we have de?ned one method for getting data out of the buffer, but two methods for putting it in (which happen to be distinguished by type rather than name). A call to Get() can synchronize with a call to either of the Put() methods. If there are queued calls to both P ut()s, then which one synchronizes with a subsequent Get() is unspeci?ed. 3. INFORMAL SPECIFICATION Grammar The syntactic extensions to the C grammar [ECMA 2020, Appendix C] are very minor. We add a new keyword, async, and add it as an alternative return type: returntype : := type | void | async. This allows methods, delegates and interface methods to be declared asynchronous. In class member declarations, we replace method declaration with chord declaration: chorddeclaration : := Methodheader [amp。 } public string Get() amp。 this is an allowable implementation. ACM Transactions on Programming Languages and Systems, Vol. 26, No. 5, September 2020 (. all the methods in the chord are asynchronous) does the body run in a new thread, and in that case there is no value to be returned. It should also be pointed out that the Buffer code, trivial though it is, is threadsafe. The locking that is required (for example to prevent the argument to a single Put being returned to two distinct Gets) is generated automatically by the piler. More precisely, deciding whether any chord is enabled by a call and, if so, removing the other pending calls from the queues and scheduling the body for execution, is an atomic operation. Apart from this atomicity guarantee, however, there is no monitor like mutual exclusion between chord bodies. Any mutual exclusion that is required must be explicitly programmed in terms of synchronization conditions in chord headers. The Buffer example uses a single chord to de?ne two methods. It is also possible (and mon) to have multiple chords involving a given method. For example: public class Buffer { public string Get() amp。 (buff . Get() + buff . Get())。 buff . Put(“blue”)。 public async Put(string s) { return s。 they are declared by using the async keyword instead of void. Calling an asynchronous method is much like sending a message, or posting an event. Since asynchronous methods have to return immediately, the behaviour of a method such as async postEvent(EventInfo data) { // large method body } is the only thing it could reasonably be: the call returns immediately and ?large method body? is scheduled for execution in a different thread (either a new one spawned to service this call, or a worker from some pool). However, this kind of de?nition is actually rather rare in Polyphonic C . More monly, asynchronous methods are de?ned using chords, as described below, and do not necessarily require new threads. Chords. A chord (also called a ?synchronization pattern?, or ?join pattern?) consists of a header and a body. The header is a set of method declarations separated by ?amp。 Philippsen 1995], only monitors have gained widespread acceptance as programming constructs. An interesting new linguistic approach has emerged recently with Four and Gonthier?s [1996, 2020] join calculus, a process calculus well suited to direct implementation in a distributed setting. Other languages, such as JoCaml [Conchon and Le Fessant 1999] and Funnel [Odersky 2020], bine similar ideas with the functional programming model. Here we propose an adapta tion of join calculus ideas to an object oriented language that has an existing thread sand locks concurrency model. Itzstein and Kearney [2020] have recently described very similar extensions for Java. Asynchronous Programming Asynchronous events and message passing are increasingly used at all levels of software systems. At the lowest level, device drivers have to respond promptly to asynchronous device events, while being parsimonious on resource use. At the Graphical User Interface level, code and programming models are notoriously plex because of the asynchronous nature of user events。 Reppy 1992。 Kamin 1997] are an extreme example of this linguistic approach: new adhoc languages are routinely proposed not to replace general purpose language, but to facilitate domain speci?c code analysis by the simple fact of expressing domain related features as primitive language constructs. We believe that concurrency should be a language feature and a part of language speci?cations. Serious attempts in this direction were made beginning in the 1970s with the concept of monitors [Hoare 1974] and the Occam language [INMOS Limited 1984] (based on Communicating Sequential Processes [Hoare 1985]). The general notion of monitors has bee very