Presentation is loading. Please wait.

Presentation is loading. Please wait.

.NET Programming Language MSR Cambridge Nick Benton Microsoft Research Cambridge.

Similar presentations


Presentation on theme: ".NET Programming Language MSR Cambridge Nick Benton Microsoft Research Cambridge."— Presentation transcript:

1 .NET Programming Language Research @ MSR Cambridge Nick Benton Microsoft Research Cambridge

2 MS.NET days March 2002 MSR Cambridge Programming Principles and Tools Group Luca Cardelli Nick Benton Cedric Fournet Andy Gordon Tony Hoare Andrew Kennedy Simon Peyton Jones Don Syme Simon Marlow Claudio Russo Mark Shields + visitors, PhD students, interns,…

3 MS.NET days March 2002.NET in MSR Programming Principles and Tools Group Verifier spec (POPL) CLR design feedback Project 7 (Babel) SML.NET (ICFP,ICFP,HOOTS) Generics for C# and.NET (PLDI) Extended IL (Babel) Polyphonic C# (FOOL, ECOOP) Stack-Walking Security analysis (POPL) Assertions

4 Part 1 SML.NET Nick Benton, Andrew Kennedy and Claudio Russo

5 MS.NET days March 2002 Advanced Programming Languages on the CLR.NET and the CLR provide a great opportunity for programming language designers and implementers: The runtime provides services (execution engine, garbage collection,…) which make producing a good implementation of your language easier The frameworks & libraries mean you can actually do useful things with your new language (graphics, networking, database access, web services,…) Multi-language component-based programming makes it much more practical for other people to use your language in their own projects

6 MS.NET days March 2002 Our favourite language: Standard ML A safe, modular, strict, higher-order, functional, polymorphic programming language with compile-time type checking and type inference, garbage collection, exception handling, immutable data types, pattern-matching and updatable references, abstract data types, and parametric modules. with several efficient implementations and a formal definition with a proof of soundness. (Scheme+types+real syntax, Haskell with call by value evaluation)

7 MS.NET days March 2002 Some SML datatype order = LESS | GREATER | EQUAL datatype 'a Tree = Empty | Node of 'a * ('a Tree) * ('a Tree) fun contains compare (x, Empty) = false | contains compare (x, Node(v, left, right)) = (case compare (x,v) of LESS => contains compare (x,left) | GREATER => contains compare (x, right) | EQUAL => true ) contains : ('a * 'a -> order) -> ('a * 'a Tree) -> bool

8 MS.NET days March 2002 What is SML.NET? Compiler for SML that targets verifiable CIL Research Issues: Can we compile a polymorphic functional language to a monomorphic, object-oriented runtime? Yes How can we make it produce fast, compact code? Whole-program optimising compiler. Monomorphisation. Representation tricks. Novel typed intermediate language using monads to track side-effects. How can we make cross-language working easy? We extend SML to support all of the.NET CLS (Common Language Specification), providing smooth bidirectional interoperability with.NET framework libraries & other.NET languages

9 MS.NET days March 2002 Previous approaches to interop 1. Bilateral interface with marshalling and explicit calling conventions (e.g. JNI, OCaml interface for C). Awkward, ugly, tied to one implementation, only for experts 2. Multilateral interface with IDL (e.g. COM, CORBA) together with particular language mappings (e.g. H/Direct, Caml COM, MCORBA). Have to write IDL and use tools, tend to be lowest- common denominator, memory management often tricky

10 MS.NET days March 2002 Interop in.NET Languages share a common higher-level infrastructure (CLR): shared heap means no tricky cross-heap pointers (cf reference counting in COM) shared type system means no marshalling (cf string char* marshalling for Java C) shared exception model supports cross-language exception handling

11 MS.NET days March 2002 SML.NET interop Sounds great, but SML is not object-oriented So we are going to have to do some work… Possible approaches to interop: do not extend language; instead provide wrappers that give a functional view of a CLS library (Haskell, Mercury). Powerful functional type systems can go a very long way towards modelling OO type systems redesign the language (OO-SML?) Our approach – a middle way: re-use existing features where appropriate (non-object-oriented subset) extend language for convenient interop when fit is bad (object- oriented features) live with the CLS at boundaries: dont try to export complex ML types to other languages (what would they do with them?)

12 MS.NET days March 2002 Re-use SML features static field static method namespace void null multiple args mutabilityref open unit structure NONE val binding fun binding tuple CLS SML using private fieldslocal decls delegatefirst-class function

13 MS.NET days March 2002 Extend language instance method invocation instance field access custom attributes casts class definitions type test exp :> ty attributes in classtype classtype obj.#meth obj.#fld cast patterns CLS SML

14 MS.NET days March 2002 open System.Windows.Forms System.Drawing System.ComponentModel fun selectXML () = let val fileDialog = OpenFileDialog() in fileDialog.#set_DefaultExt("XML"); fileDialog.#set_Filter("XML files (*.xml) |*.xml"); if fileDialog.#ShowDialog() = DialogResult.OK then case fileDialog.#get_FileName() of NONE => () | SOME name => replaceTree (ReadXML.make name, "XML file '" ^ name ^ "'") else () end Extract from WinForms interop instance method invocation CLS Namespace = ML structure static method = ML function null value = NONE static constant field = ML value no args = ML unit value CLS string = ML string And note that there are no explicit types in this code

15 MS.NET days March 2002 Ray tracing in ML ICFP programming competition: build a ray tracer in under 3 days 39 entries in all kinds of languages: C, C++, Clean, Dylan, Eiffel, Haskell, Java, Mercury, ML, Perl, Python, Scheme, Smalltalk ML (Caml) was in 1 st and 2 nd place

16 MS.NET days March 2002 Ray tracing in SML.NET Translate winning entry to SML Add WinForms interop Run on.NET CLR Performance on this example twice as good as popular optimizing native compiler for SML (though on others were twice as bad)

17 MS.NET days March 2002 Visual Studio Integration Bootstrap the compiler to produce a.NET component for parsing, typechecking, etc. of SML Use interlanguage working extensions to expose that as a COM component which can be glued into Visual Studio Write new ML code to handle syntax highlighting, tooltips, error reporting, etc.

18 Part 2 Generics in the CLR and C# Andrew Kennedy and Don Syme

19 MS.NET days March 2002 Note: This is not in V1 of the CLR It will be in V2

20 MS.NET days March 2002 Part 1: Introduction

21 MS.NET days March 2002 What are generics? Types which are parameterized by other types, e.g. Stack, Stack Generics, templates, parametric polymorphism Ada, Eiffel, C++, ML, Haskell, Mercury, Component Pascal,… Promote code reuse, increase type safety, better performance Good for collections, higher-order programming and generally building higher-level, reusable abstractions

22 MS.NET days March 2002 Generic code in C# today (1) class Stack { private Object[] items; private int nitems; Stack() { nitems = 0; items = new Object[50]; } Object Pop() { if (nitems == 0) throw new EmptyException(); return items[--nitems]; } void Push(Object item) {... return items[nitems++]; } }

23 MS.NET days March 2002 Generic code in C# today (2) Stack s = new Stack(); s.Push(1); s.Push(2); int n = (int)(s.Pop()) + (int)(s.Pop()); Whats wrong with that? Its inexpressive. Type system doesnt document what should be in a particular stack. Its unsafe. Type errors ( s.Push(2); ) lead to runtime exceptions instead of being caught by compiler Its ugly. Casts everywhere. Its slow. Casts are slow, and converting base types (e.g. ints) to Objects involves expensive boxing.

24 MS.NET days March 2002 Generic code in C# tomorrow (1) class Stack { private T[] items; private int nitems; Stack() { nitems = 0; items = new T[50]; } T Pop() { if (nitems == 0) throw new EmptyException(); return items[--nitems]; } void Push(T item) { if (items.Length == nitems) { T[] temp = items; items = new T[nitems*2]; Array.Copy (temp, items, nitems); } items[nitems++] = item; } }

25 MS.NET days March 2002 Generics: large design space Parameterized classes, interfaces, and methods e.g. // two-parameter class class Dict {... } // parameterized interface interface IComparable {... } // parameterized struct struct Pair {... } // generic method T[] Slice (T[] arr, int start, int count) What goes in the CLR? What goes in the compilers? Do we have exact runtime type information? if (x is Set ).... But can type instantiations can be non-reference? Set List Do you erase types at runtime? Do you share code? Constraints? class C {... }

26 MS.NET days March 2002 Generic interfaces interface IDictionary { D Lookup(K);... } class Dictionary : IDictionary { D Lookup(K);... } Dictionary

27 MS.NET days March 2002 Generic methods static void Sort (T[]) {... } int[] x = { 5,4,3,2 }; Sort(x); A generic method is just a family of methods, indexed by type.

28 MS.NET days March 2002 Explicit Constraints interface IComparable { static int Compare(T,T); } class BinaryTree > { void insert(T x) { switch (x.Compare(y)) {... } } T y; Tree left; Tree right; } Static constraints are available via interfaces.

29 MS.NET days March 2002 Part 2: Implementing Generics for the.NET CLR

30 MS.NET days March 2002 Generics: Our design Parameterized classes, interfaces, structs, methods, delegates Yes Exact runtime types: Yes if (x is Set ) {... } Instantiations at value types: Yes Set Set List Constraints: Yes (by interfaces) class C Variance: No Set is not subtype-related to Set

31 MS.NET days March 2002 How does it work? Stack si = new Stack (); Stack si2 = new Stack (); Stack ss = new Stack (); Stack so = new Stack (); si.Push(5); ss.Push(Generics); so.Push(myObj); 1a. Look for Stack 1b. Nothing exists yet, so create structures for Stack 2a. Look for Stack 2b. Type already loaded! 3a. Look for Stack 3b. not compatible with, so create structures for Stack 4a. Look for Stack 4b. compatible with, so re-use Stack

32 MS.NET days March 2002 How does it work? Stack si = new Stack (); Stack si2 = new Stack (); Stack ss = new Stack (); Stack so = new Stack (); si.Push(5); ss.Push(Generics); so.Push(myObj); Compile code for Stack.Push Re-use code from Stack.Push

33 MS.NET days March 2002 Implementing Generics (0) Dynamic loading & JIT compilation change many fundamental assumptions Can consider code generation at runtime Can use more dynamic data structures The current challenge is to implement all of: exact runtime types + code sharing + dynamic loading + non-uniform instantiations

34 MS.NET days March 2002 Implementing Generics (1) Our implementation: 1. Dynamic code expansion and sharing Instantiating generic classes and methods is handled by the CLR. Generate/compile code as necessary Use JIT compiler Share code for compatible instantiations Compatibility is determined by the implementation

35 MS.NET days March 2002 Implementing Generics (2) Our implementation: 1. Dynamic code expansion and sharing 2. Pass and store runtime type information Objects carry runtime type information We record this information by duplicating vtables. Other options are possible. Generic methods take an extra parameter The active type context is always recoverable

36 MS.NET days March 2002 Implementing Generics (3) Our implementation: 1. Dynamic code expansion and sharing 2. Pass and store runtime type information 3. Optimized use of runtime type information

37 MS.NET days March 2002 Generics: Performance 1. Instantiations at value types Stack with repeated push and pops of 0...N elements (a) List_int (b) List (i.e. existing collection class) (c) List 5X speedup (c) v. (b) 2. No casts 20% speed on similar micro-benchmarks 3. Runtime type computations Generic allocation ~10% slower in generic code

38 MS.NET days March 2002 Generics: Design Comparison C++ GJ Bracha, Odersky, Stoutamire, Wadler NextGen Cartwright, Steele PolyJ Bank, Liskov, Myers Agesen, Freund, Mitchell CLR Kennedy, Syme Dynamic Load Efficient non- reference instantiations ½½ Exact run-time types ½ Efficient via fewer runtime type tests ½ Code Sharing ½ Ship without source Debug (type pars visible at runtime) ½ Safe, efficient co- variance ½

39 MS.NET days March 2002 Comparison with C++ templates What C++ gets wrong: Modularity need to see the template at link time Efficiency nearly all implementations code-expand Safety not checked at declaration & use, but at link time Simplicity very complicated

40 MS.NET days March 2002 Comparison with C++ templates What we get right: Modularity: no need to see the generic IL until runtime Efficiency: code expansion managed by the VM Safety: check at declaration & use Simplicity: there are some corners, but the mechanism is simple to explain & use

41 MS.NET days March 2002 Summary A world first: cross-language generics. A world first: generics in a high-level virtual machine design. Implementation: The virtual machine (runtime) level is the right place to implement generics for.NET Potential for: Other implementation techniques (more aggressive sharing, some boxing, etc.) Further extensions (type functions, variance, more expressive constraints) Good performance

42 Part 3 Polyphonic C# Nick Benton, Luca Cardelli and Cedric Fournet

43 MS.NET days March 2002 Introduction

44 MS.NET days March 2002 Programming in a networked world Developers now have to work in a Concurrent Distributed High latency (& low reliability, security sensitive, multi-everything) environment. Which is hard And theyre mostly not very good at it Try using Outlook over dialup

45 MS.NET days March 2002 Asynchronous communication Distribution => concurrency + latency => asynchrony => more concurrency Message-passing, event-based programming, dataflow models For programming languages, coordination (orchestration) languages & frameworks, workflow

46 MS.NET days March 2002 Language support for concurrency Make invariants and intentions more apparent (part of the interface) Good software engineering Allows the compiler much more freedom to choose different implementations Also helps other tools

47 MS.NET days March 2002.NET Today Multithreaded execution environment with lock per object C# has lock keyword, libraries include traditional shared-memory synchronization primitives (mutexes, monitors, r/w locks) Delegate-based asynchronous calling model, events, messaging Higher level frameworks built on that Hard to understand, use and get right Different models at different scales Support for asynchrony all on the caller side – little help building code to handle messages (must be thread-safe, reactive, and deadlock-free)

48 MS.NET days March 2002 Polyphonic C# An extension of the C# language with new concurrency constructs Based on the join calculus A foundational process calculus like the -calculus but better suited to asynchronous, distributed systems A single model which works both for local concurrency (multiple threads on a single machine) distributed concurrency (asynchronous messaging over LAN or WAN)

49 MS.NET days March 2002 The Language

50 MS.NET days March 2002 In one slide: Objects have both synchronous and asynchronous methods. Values are passed by ordinary method calls: If the method is synchronous, the caller blocks until the method returns some result (as usual). If the method is async, the call completes at once and returns void. A class defines a collection of synchronization patterns (chords), which define what happens once a particular set of methods have been invoked on an object: When pending method calls match a pattern, its body runs. If there is no match, the invocations are queued up. If there are several matches, an unspecified pattern is selected. If a pattern containing only async methods fires, the body runs in a new thread.

51 MS.NET days March 2002 A Simple Buffer class Buffer { String get() & async put(String s) { return s; }

52 MS.NET days March 2002 A Simple Buffer class Buffer { String get() & async put(String s) { return s; } An ordinary (synchronous) method with no arguments, returning a string

53 MS.NET days March 2002 A Simple Buffer class Buffer { String get() & async put(String s) { return s; } An ordinary (synchronous) method with no arguments, returning a string An asynchronous method (hence returning no result), with a string argument

54 MS.NET days March 2002 A Simple Buffer class Buffer { String get() & async put(String s) { return s; } An ordinary (synchronous) method with no arguments, returning a string An asynchronous method (hence returning no result), with a string argument Joined together in a chord

55 MS.NET days March 2002 A Simple Buffer class Buffer { String get() & async put(String s) { return s; } Calls to put() return immediately (but are internally queued if theres no waiting get() ). Calls to get() block until/unless theres a matching put() When theres a match the body runs, returning the argument of the put() to the caller of get(). Exactly which pairs of calls are matched up is unspecified.

56 MS.NET days March 2002 A Simple Buffer class Buffer { String get() & async put(String s) { return s; } Does example this involve spawning any threads? No. Though the calls will usually come from different pre- existing threads. So is it thread-safe? You dont seem to have locked anything… Yes. The chord compiles into code which uses locks. (And that doesnt mean everything is synchronized on the object.) Which method gets the returned result? The synchronous one. And there can be at most one of those in a chord.

57 MS.NET days March 2002 Reader/Writer …using threads and mutexes in Modula 3 An introduction to programming with threadsAn introduction to programming with threads. Andrew D. Birrell, January 1989.

58 MS.NET days March 2002 class ReaderWriter { void Exclusive() & private async Idle() {} void ReleaseExclusive() { Idle(); } void Shared() & private async Idle() { S(1); } void Shared() & private async S(int n) { S(n+1); } void ReleaseShared() & private async S(int n) { if (n == 1) Idle(); else S(n-1); } ReaderWriter() { Idle(); } } A single private message represents the state: none Idle() S(1) S(2) S(3) … Reader/Writer in five chords

59 MS.NET days March 2002 Asynchronous Service Requests and Responses The service might export an async method which takes parameters and somewhere to put the result: a buffer, or a channel, or a delegate (O-O function pointer) delegate async IntCB(int v); class Service { public async request(String arg, IntCB callback) { int result; // do something interesting… callback(result); }

60 MS.NET days March 2002 Asynchronous Service Requests and Responses - join class Join2 { void wait(out int i, out int j) & async first(int r1) & async second(int r2) { i = r1; j = r2; return; } // client code: int i,j; Join2 x = new Join2(); service1.request(arg1, new IntCB(x.first)); service2.request(arg2, new IntCB(x.second)); // do something useful // now wait until both results have come back x.wait(i,j); // do something with i and j

61 MS.NET days March 2002 Asynchronous Service Requests and Responses - select class Select { int wait() & async reply(int r) { return r; } // client code: int i; Select x = new Select(); service1.request(arg1, new IntCB(x.reply)); service2.request(arg2, new IntCB(x.reply)); // do something useful // now wait until one result has come back i = x.wait(); // do something with i

62 MS.NET days March 2002 Extending C# with chords Classes can declare methods using generalized chord-declarations instead of method-declarations. chord-declaration ::= method-header [ & method-header ]* body method-header ::= attributes modifiers [return-type | async] name (parms) Interesting well-formedness conditions: 1. At most one header can have a return type (i.e. be synchronous). 2. The inheritance restriction. 3. ref and out parameters cannot appear in async headers.

63 MS.NET days March 2002 Implementation

64 MS.NET days March 2002 Example: Sum of Squares SumOfSquares total(0,8 ) add(1) add(4) add(9) add(64 )

65 MS.NET days March 2002 Sum of Squares SumOfSquares total(1,7 ) add(4) add(9) add(64 )

66 MS.NET days March 2002 Sum of Squares Code class SumOfSquares { private async loop(int i) { if (i > 0) { add(i * i); loop(i - 1); } private int total(int r, int i) & private async add(int dr) { int rp = r + dr; if (i > 1) return total(rp, i - 1); return rp; } public SumOfSquares(int x) { loop(x); int i = total(0, x); System.Console.WriteLine("The result is {0}.", i); }

67 MS.NET days March 2002 Sum of Squares Translation using System; using System.Collections; using System.Threading; class SyncQueueEntry{ public int pattern; public System.Threading.Thread mythread; public System.Object joinedentries; } class SumOfSquares{ Queue Q_totalint_int_ = new Queue(); Queue Q_addint_ = new Queue(); class loopint__runner{ SumOfSquares parent; int field_0; public loopint__runner(SumOfSquares p_p,int p_0) { parent = p_p; field_0 = p_0; Thread t = new Thread(new ThreadStart(this.doit)); t.Start(); } void doit() { parent.loopint__worker(field_0); } private void loopint__worker(int i) { if (i >= 1) {add(i * i); loop(i - 1); } static void Main() { SumOfSquares s = new SumOfSquares(); int thesum = s.sum(10); Console.WriteLine(thesum); } public int sum(int x) { loop(x); return total(0, x); } private int total(int sync_p_0,int sync_p_1) { SyncQueueEntry qe = new SyncQueueEntry(); int matchindex=0; System.Threading.Monitor.Enter(Q_totalint_int_); if (!(Q_addint_.Count ==0)) { qe.joinedentries = (int) (Q_addint_.Dequeue()); System.Threading.Monitor.Exit(Q_totalint_int_); matchindex = 0; goto joinlabel; }// enqueue myself and sleep; qe.mythread = Thread.CurrentThread; Q_totalint_int_.Enqueue(qe); System.Threading.Monitor.Exit(Q_totalint_int_); try { Thread.Sleep(Timeout.Infinite); } catch (ThreadInterruptedException) {} // wake up here matchindex = qe.pattern; joinlabel: switch (matchindex) { case 0: int r = sync_p_0; int i = sync_p_1; int dr = (int)(qe.joinedentries); int rp = r + dr; if (i > 1) {return total(rp, i - 1); } return rp; } throw new System.Exception(); } private void add(int p_0) { Object qe = p_0; System.Threading.Monitor.Enter(Q_totalint_int_); if (!(Q_totalint_int_.Count ==0)) { SyncQueueEntry sqe = (SyncQueueEntry) (Q_totalint_int_.Dequeue()); sqe.joinedentries = qe; System.Threading.Monitor.Exit(Q_totalint_int_); sqe.pattern = 0; sqe.mythread.Interrupt(); return; } Q_addint_.Enqueue(qe); System.Threading.Monitor.Exit(Q_totalint_int_); return; } private void loop(int i) { loopint__runner r = new loopint__runner(this,i); }

68 MS.NET days March 2002 Sum of Squares Translation using System; using System.Collections; using System.Threading; class SyncQueueEntry{ public int pattern; public System.Threading.Thread mythread; public System.Object joinedentries; } class SumOfSquares{ Queue Q_totalint_int_ = new Queue(); Queue Q_addint_ = new Queue(); class loopint__runner{ SumOfSquares parent; int field_0; public loopint__runner(SumOfSquares p_p,int p_0) { parent = p_p; field_0 = p_0; Thread t = new Thread(new ThreadStart(this.doit)); t.Start(); } void doit() { parent.loopint__worker(field_0); } private void loopint__worker(int i) { if (i >= 1) {add(i * i); loop(i - 1); } static void Main() { SumOfSquares s = new SumOfSquares(); int thesum = s.sum(10); Console.WriteLine(thesum); } public int sum(int x) { loop(x); return total(0, x); } private int total(int sync_p_0,int sync_p_1) { SyncQueueEntry qe = new SyncQueueEntry(); int matchindex=0; System.Threading.Monitor.Enter(Q_totalint_int_); if (!(Q_addint_.Count ==0)) { qe.joinedentries = (int) (Q_addint_.Dequeue()); System.Threading.Monitor.Exit(Q_totalint_int_); matchindex = 0; goto joinlabel; }// enqueue myself and sleep; qe.mythread = Thread.CurrentThread; Q_totalint_int_.Enqueue(qe); System.Threading.Monitor.Exit(Q_totalint_int_); try { Thread.Sleep(Timeout.Infinite); } catch (ThreadInterruptedException) {} // wake up here matchindex = qe.pattern; joinlabel: switch (matchindex) { case 0: int r = sync_p_0; int i = sync_p_1; int dr = (int)(qe.joinedentries); int rp = r + dr; if (i > 1) {return total(rp, i - 1); } return rp; } throw new System.Exception(); } private void add(int p_0) { Object qe = p_0; System.Threading.Monitor.Enter(Q_totalint_int_); if (!(Q_totalint_int_.Count ==0)) { SyncQueueEntry sqe = (SyncQueueEntry) (Q_totalint_int_.Dequeue()); sqe.joinedentries = qe; System.Threading.Monitor.Exit(Q_totalint_int_); sqe.pattern = 0; sqe.mythread.Interrupt(); return; } Q_addint_.Enqueue(qe); System.Threading.Monitor.Exit(Q_totalint_int_); return; } private void loop(int i) { loopint__runner r = new loopint__runner(this,i); } class SumOfSquares{ Queue Q_totalint_int_ = new Queue(); Queue Q_addint_ = new Queue(); …

69 MS.NET days March 2002 Sum of Squares Translation using System; using System.Collections; using System.Threading; class SyncQueueEntry{ public int pattern; public System.Threading.Thread mythread; public System.Object joinedentries; } class SumOfSquares{ Queue Q_totalint_int_ = new Queue(); Queue Q_addint_ = new Queue(); class loopint__runner{ SumOfSquares parent; int field_0; public loopint__runner(SumOfSquares p_p,int p_0) { parent = p_p; field_0 = p_0; Thread t = new Thread(new ThreadStart(this.doit)); t.Start(); } void doit() { parent.loopint__worker(field_0); } private void loopint__worker(int i) { if (i >= 1) {add(i * i); loop(i - 1); } static void Main() { SumOfSquares s = new SumOfSquares(); int thesum = s.sum(10); Console.WriteLine(thesum); } public int sum(int x) { loop(x); return total(0, x); } private int total(int sync_p_0,int sync_p_1) { SyncQueueEntry qe = new SyncQueueEntry(); int matchindex=0; System.Threading.Monitor.Enter(Q_totalint_int_); if (!(Q_addint_.Count ==0)) { qe.joinedentries = (int) (Q_addint_.Dequeue()); System.Threading.Monitor.Exit(Q_totalint_int_); matchindex = 0; goto joinlabel; }// enqueue myself and sleep; qe.mythread = Thread.CurrentThread; Q_totalint_int_.Enqueue(qe); System.Threading.Monitor.Exit(Q_totalint_int_); try { Thread.Sleep(Timeout.Infinite); } catch (ThreadInterruptedException) {} // wake up here matchindex = qe.pattern; joinlabel: switch (matchindex) { case 0: int r = sync_p_0; int i = sync_p_1; int dr = (int)(qe.joinedentries); int rp = r + dr; if (i > 1) {return total(rp, i - 1); } return rp; } throw new System.Exception(); } private void add(int p_0) { Object qe = p_0; System.Threading.Monitor.Enter(Q_totalint_int_); if (!(Q_totalint_int_.Count ==0)) { SyncQueueEntry sqe = (SyncQueueEntry) (Q_totalint_int_.Dequeue()); sqe.joinedentries = qe; System.Threading.Monitor.Exit(Q_totalint_int_); sqe.pattern = 0; sqe.mythread.Interrupt(); return; } Q_addint_.Enqueue(qe); System.Threading.Monitor.Exit(Q_totalint_int_); return; } private void loop(int i) { loopint__runner r = new loopint__runner(this,i); } private void add(int p_0) { System.Threading.Monitor.Enter(Q_totalint_int_); if (!(Q_totalint_int_.Count ==0)) { SyncQueueEntry sqe = (SyncQueueEntry)(Q_totalint_int_.Dequeue()); sqe.joinedentries = p_0; System.Threading.Monitor.Exit(Q_totalint_int_); sqe.pattern = 0; sqe.mythread.Interrupt(); return; } Q_addint_.Enqueue(p_0); System.Threading.Monitor.Exit(Q_totalint_int_); return; }

70 MS.NET days March 2002 Current Work Examples and test cases Web combinators, adaptive scheduler, web services (Terraserver), active objects and remoting (stock trader) Generally looking at integration with existing mechanisms and frameworks Language design Direct syntactic support for timeouts Solid Implementation

71 MS.NET days March 2002 Predictable Demo: Dining Philosophers eating waiting to eat waiting to eat thinking

72 MS.NET days March 2002 Code extract class Room { public Room (int size) { hasspaces(size); } public void enter() & private async hasspaces(int n) { if (n > 1)hasspaces(n-1); elseisfull(); } public void leave() & private async hasspaces(int n) { hasspaces(n+1); } public void leave() & private async isfull() { hasspaces(1); } }

73 MS.NET days March 2002 Conclusions A clean, simple, new model for asynchronous concurrency in C# Declarative, local synchronization Applicable in both local and distributed settings Efficiently compiled to queues and automata Easier to express and enforce concurrency invariants Compatible with existing constructs, though they constrain our design somewhat Solid foundations Works well in practice

74 MS.NET days March 2002 Thats all For more information, contact me nick@microsoft.com or see nick@microsoft.com http://research.microsoft.com/~{nick,akenn,dsyme}

75 MS.NET days March 2002 TimeoutBuffer class TimeoutBuffer { TimeoutBuffer(int delay) { Timer t = new Timer(new TimerCallBack(this.tick), delay); empty(); } async empty() & void put(Object o) {has(o);} async empty() & void tick() {timeout();} async timeout() & void put(Object o) {timeout();} async timeout() & Object get() {timeout(); throw new TimeOutExn();} async has(Object o) & Object get() {has(o); return o;} async has(Object o) & void tick() {has(o);} }

76 MS.NET days March 2002 JoCaml allows multiple synchronous methods to be joined, as in the following rendezvous But in which thread does the body run? In C#, thread identity is very observable, since threads are the holders of particular re-entrant locks. So we rule this out in the interests of keeping & commutative. (Of course, its still easy to code up an asymmetric rendezvous in Polyphonic C#.) Why only one synchronous method in a chord? int f(int x) & int g(int y) { return y to f; return x to y; }

77 MS.NET days March 2002 The problem with inheritance Weve half overridden f Too easy to create deadlock or async leakage class C { virtual void f() & virtual async g() {…} virtual void f() & virtual async h() {…} } class D : C { override async g() { …} } void m(C x) { x.g(); x.f();} … m(new D());

78 MS.NET days March 2002 The Inheritance Restriction Inheritance may be used as usual, with a restriction to prevent the partial overriding of patterns: For a given class, two methods f ang g are co-declared if there is a chord in which they are both declared. Whenever a method is overriden, every codeclared method must also be overriden. Hence, the compiler rejects patterns such as public virtual void f() & private async g() {…} In general, inheritance and concurrency do not mix well. Our restriction is simple; it could be made less restrictive.

79 MS.NET days March 2002 Types etc. async is a subtype of void Allow covariant return types on those two: An async method may override a void one A void delegate may be created from an async method An async method may implement a void method in an interface async methods are automatically given the [OneWay] attribute, so remote calls are non- blocking

80 MS.NET days March 2002 Compiling chords Since synchronization is statically defined for every class, we can compile it efficiently (state automata). We cache the synchronization state in a single word. We use a bit for every (polyphonic) method. We pre-compute bitmasks for every pattern. Simple version just looks up queue state directly For every polyphonic method, we allocate a queue for storing delayed threads (or pending messages). The compilation scheme can be optimized: Some states are not reachable. Empty messages only need to be counted. The content of (single, private) messages can be stored in local variables. Requires some analysis.

81 MS.NET days March 2002 Implementation issues When compiling a polyphonic class, we add private fields for the synchronization state and the queues; private methods for the body of asynchronous patterns; some initialization code. The code handling the join patterns must be thread-safe. We use a single lock (from the first queue) to protect the state word and all queues. This is independent from the object lock and only held briefly whilst queues are being manipulated. For asynchronous methods, theres an auxiliary class for storing the pending messages.

82 MS.NET days March 2002 Adding synchronization code When an asynchronous method is called: add the message content to the queue; if the method bit is 0, set it to 1 in the synchronization state and check for a completed pattern: For every pattern containing the method, compare the new state to the pattern mask. If there is a match, then wake up a delayed thread (or start a new thread if the pattern is entirely asynchronous). When a synchronous method is called: if the method bit is 0, set it to 1 in the synchronization state and check for a completed pattern For every pattern containing the method, compare the new state to the pattern mask. If there is a match, dequeue the asynchronous arguments, adjust the mask, and run the body for that pattern. Otherwise, enqueue the thread, go to sleep, then retry.


Download ppt ".NET Programming Language MSR Cambridge Nick Benton Microsoft Research Cambridge."

Similar presentations


Ads by Google