Presentation is loading. Please wait.

Presentation is loading. Please wait.

1 Concurrency in C  Nick Benton. 2 PPT: People Luca Cardelli Nick Benton Karthik Bhargavan Gavin Bierman Byron Cook Cédric Fournet Georges Gonthier Andy.

Similar presentations


Presentation on theme: "1 Concurrency in C  Nick Benton. 2 PPT: People Luca Cardelli Nick Benton Karthik Bhargavan Gavin Bierman Byron Cook Cédric Fournet Georges Gonthier Andy."— Presentation transcript:

1 1 Concurrency in C  Nick Benton

2 2 PPT: People Luca Cardelli Nick Benton Karthik Bhargavan Gavin Bierman Byron Cook Cédric Fournet Georges Gonthier Andy Gordon Tony Hoare Andrew Kennedy Simon Marlow Simon Peyton Jones Claudio Russo Don Syme + visitors

3 3 Exciting Times… C# and Java are strongly typed, garbage collected languages with language-level security mechanisms this was the preserve of academic languages ten years ago! Parametric polymorphism in C# 2.0 and Java 5 Mixed-language programming is really happening Concurrency is going mainstream Using advanced analysis tools for finding bugs and checking correctness properties is becoming routine Contracts and behavioural types are hot topics – –Only go back ten years in academia Security matters

4 4 PPT: Some Other Projects Generics for C# and Microsoft ®.NET SML.NET, Haskell, F# Stack-walking Security in.NET Formal Tools for Securing Web Services Query Languages for Semi-structured Data First-class Functions for Microsoft ® Excel Languages for Systems Biology Software Transactional Memory

5 5 Why C? Target domain: distributed Web applications Common “three tier” architecture: 1. 1.Data services tier (database) 2. 2.Middle tier (main application) – C#/Java 3. 3.User interface tier (XML/HTML) Concurrency scenario: asynchronous events and message passing Web services, workflow orchestrations, …

6 6 The Reality It’s about both – –DATA Objects, XML, relations – –CONTROL Asynchrony, concurrency Even though our languages are “modern”, support for these features is pretty primitive Data access is weakly typed, mostly string based Control uses concepts from the ’60s (Monitors, etc.) Essence: these important features are left to APIs 

7 7 The Guiding Principle Provide better level of abstraction Make invariants and intentions more apparent (part of the interface) Give stronger compile-time guarantees (types) Enable different implementations and optimizations Expose structure for other tools to exploit (example: static analysis) Put (really) important features in the language itself, rather than in libraries

8 8 The C Project in a Slide We’re building an experimental language with – –C# at its core – –First-class support for common data models – –A better model of concurrency for both local and distributed concurrency Second principle: RISCy language extension – –Don’t bake complex solutions into language – –Choose minimal pieces from which solutions can be built – –Those pieces having already been shown effective in `purer’ more academic settings – –Impedance match to new setting (and even user profile)

9 9 Cw Data (very briefly) New types: 1. 1.Streams ( T* ) 2. 2.Choice ( choice{int;string;} ) 3. 3.Tuples ( struct{int i;string s;} ) 4. 4.Content classes 5. 5.Nullables ( T? ) Minimal extensions to allow (fairly) high fidelity, strongly typed, mapping of both relational and semistructured data XML literals (OK, so that’s not very RISCy) Generalized member access (cf. XPath) SQL-style select queries

10 10 But today’s topic is… Concurrency

11 11 Asynchrony Is Where It’s At Asynchrony Is Where It’s At Distribution → concurrency + latency → asynchrony → more concurrency Message-passing, event-based programming, dataflow models For programming languages, coordination (orchestration) languages and frameworks, workflow

12 12.NET Today Java-style “monitors” Operating system shared memory primitives Clunky delegate-based asynchronous calling model 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)

13 13 C Concurrency New concurrency model and language 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) It is different But it’s also simple

14 14 C Concurrency in One Slide Objects have both synchronous and asynchronous methodsObjects have both synchronous and asynchronous methods Values are passed by ordinary method calls: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 chords (synchronization patterns), which define what happens once a particular set of methods have been invoked. One method may appear in several chords.A class defines a collection of chords (synchronization patterns), which define what happens once a particular set of methods have been invoked. One method may appear in several chords. –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.

15 15 A Simple Buffer (for use by producer/consumer threads) class Buffer { async put(string s); string get() & put(string s) { return s; }

16 16 A Simple Buffer class Buffer { async put(string s); string get() & put(string s) { return s; } An ordinary (synchronous) method with no arguments, returning a string An ordinary (synchronous) method with no arguments, returning a stringAn ordinary (synchronous) method with no arguments, returning a string

17 17 A Simple Buffer class Buffer { async put(string s); string get() & 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 argumentAn asynchronous method (hence returning no result), with a string argument

18 18 A Simple Buffer class Buffer { async put(string s); string get() & 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 chordJoined together in a chord

19 19 A Simple Buffer class Buffer { async put(string s); string get() & put(string s) { return s; } Calls to put() return immediately, but are internally queued if there’s no waiting get() Calls to put() return immediately, but are internally queued if there’s no waiting get() Calls to get() block until/unless there’s a matching put() Calls to get() block until/unless there’s a matching put() When there’s a match the body runs, returning the argument of the put() to the caller of get() When there’s 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 Exactly which pairs of calls are matched up is unspecified

20 20 A Simple Buffer class Buffer { async put(string s); string get() & put(string s) { return s; } Does this example involve spawning any threads? Does this example involve spawning any threads? No. Though the calls will usually come from different pre- existing threads. No. Though the calls will usually come from different pre- existing threads. Is it thread-safe? You don’t seem to have locked anything… Is it thread-safe? You don’t seem to have locked anything… Yes. The chord compiles into code which uses locks. (And that doesn’t mean everything is synchronized on the object.) Yes. The chord compiles into code which uses locks. (And that doesn’t mean everything is synchronized on the object.) Which method gets the returned result? Which method gets the returned result? The synchronous one; there is at most one of these in a chord. The synchronous one; there is at most one of these in a chord.

21 21 Reader/Writer …using threads and mutexes in Modula 3 An introduction to programming with threads Andrew D. Birrell, January 1989

22 22 Reader/Writer in Five Chords public class ReaderWriter { private async idle(); private async s(int n); public ReaderWriter() {idle();} public void Exclusive() & idle() {} public void ReleaseExclusive() { idle(); } public void Shared() & idle() { s(1);} & s(int n) { s(n+1);} public void ReleaseShared() & s(int n) { if (n == 1) idle(); else s(n-1); } A single private message represents the state: none ↔ idle() ↔ s(1) ↔ s(2) ↔ s(3) …

23 23 Asynch Requests/Responses Service exposes an async method which takes parameters and somewhere to put the result: – –a buffer, or a channel, or – –here, an asynchronous delegate, used as a callback public delegate async IntCB(int v); // the callback public class Service { public async request(String arg, IntCB callback){ … // compute result callback(result); // respond! }

24 24 Joining Responses class Join2{ async first(int r); async second(int r); struct{int;int;} waitAll() & first(int r1) & second(intr2) { returnnew{r1,r2}; } // client code: struct{int;int;} results; Join2 x = new Join2(); service1.request(arg1, new IntCB(x.first)); service2.request(arg2, new IntCB(x.second)); // do something useful in the meantime // now wait until both results have come back results = x.waitAll(); // do something with results

25 25 Selecting Responses class Select2 { async reply(int r); int waitOne() & reply(int r) { return r; } // client code: int result; Select2 x = new Select2(); service1.request(arg1, new IntCB(x.reply)); service2.request(arg2, new IntCB(x.reply)); // do something useful in the meantime // now wait until one result has come back result = x.waitOne(); // do something with result

26 26 Active Objects public abstract class ActiveObject : MarshalByRefObject { protected bool done; abstract protected void processmessage(); public ActiveObject () { done = false; mainloop(); } async mainloop() { while (!done) { processmessage(); } }

27 27 …continued class Stock : ActiveObject { public async bid(BidOffer thebid); public async register(Client who); override protected void processmessage() & bid(BidOffer thebid) { // process bid messages } & register(Client who) { // process registration requests } … }

28 28 Extending C# with chords Syntax extensions:Syntax extensions: –Declarations for asynchronous methods –Definitions of (synchronous) methods generalized to chord definitions –when for purely asynchronous chords Interesting well-formedness conditions:Interesting well-formedness conditions: 1.Can’t have two synchronous methods in a single pattern 2.The inheritance restriction 3.“ref” and “out” parameters cannot appear in async headers

29 29 JoCaml allows multiple synchronous methods to be joined, as in the following rendezvousJoCaml 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, it’s still easy to code up an asymmetric rendezvous in Polyphonic C#.)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, it’s still easy to code up an asymmetric rendezvous in Polyphonic C#.) Why ≤1 synchronous method in a chord? int f(int x) & int g(int y) { return y to f; return x to g; }

30 30 The problem with inheritance class C { virtual async g(); virtual async h(); virtual void f() & g() {…} & h() {…} } class D : C { override async g(); } void m(C x) { x.g(); x.f();} … m(new D()); We’ve “half” overridden fWe’ve “half” overridden f Too easy to create deadlock or async leakageToo easy to create deadlock or async leakage

31 31 The inheritance restriction Two methods are co-declared if there’s a chord with both of them inTwo methods are co-declared if there’s a chord with both of them in Inheritance and concurrency don’t mix well. Our restriction is simple; it could be made less restrictive.Inheritance and concurrency don’t mix well. Our restriction is simple; it could be made less restrictive. Whenever a method is overridden, every co-declared method must also be overridden private async g() public virtual void f() & g() {…}

32 32 Types etc. is a subtype of voidasync is a subtype of void Allow covariant return types on those two: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 given the [OneWay] attribute, so remote calls are non-blockingasync methods are given the [OneWay] attribute, so remote calls are non-blocking

33 33 Implementation 4 th version…4 th version… Built on CCI (also used for Spec#), runs in VSBuilt on CCI (also used for Spec#), runs in VS Introduce queues for pending calls (holding blocked threads for sync methods, arguments for asyncs)Introduce queues for pending calls (holding blocked threads for sync methods, arguments for asyncs) Generated code (using brief lock to protect queue state) looks for matches and then eitherGenerated code (using brief lock to protect queue state) looks for matches and then either –Enqueues args (async no match) –Enqueues thread and blocks (sync no match) –Dequeues other args and continues (sync match) –Wakes up blocked thread (async match with sync) –Spawns new thread (async match all async) Efficient – bitmasks to look for matches, no PulseAlls,…Efficient – bitmasks to look for matches, no PulseAlls,… We’ve changed the underlying implementation to match changes in CLR performance (sleeping/interrupting -> waiting/pulsing)We’ve changed the underlying implementation to match changes in CLR performance (sleeping/interrupting -> waiting/pulsing)

34 34 Predictable Demo: Dining Philosophers eating waiting to eat waiting to eat thinking

35 35 Code Extract class Room { private async hasspaces(int n); private async isfull(); public Room (int size) { hasspaces(size); } public void enter() & hasspaces(int n) { if (n > 1) hasspaces(n-1); else isfull(); } public void leave() & hasspaces(int n) { hasspaces(n+1); } & isfull() { hasspaces(1); }

36 36 Demonstration Dining philosophers

37 37 Other Samples Web service combinators (Cardelli & Davies)Web service combinators (Cardelli & Davies) Adaptive scheduler (cf. Larus & Parkes),Adaptive scheduler (cf. Larus & Parkes), Accessing web services (Terraserver),Accessing web services (Terraserver), Active objects and remoting (stock trader)Active objects and remoting (stock trader) Santa Claus problem (Trono)Santa Claus problem (Trono)

38 38 Influence 3 internal projects use joins of some form3 internal projects use joins of some form ExternalExternal –MC# –Nemerle –QUT web service language –Spry

39 39 Current and future work Extension with genericsExtension with generics Limited pattern-matching on message contentsLimited pattern-matching on message contents –Been prototyped (Melgratti) but very hard to compile efficiently –Direct syntactic support for timeouts along this line would be nice… Adding joinable transactions with explicit compensations (Bruni, Montanari)Adding joinable transactions with explicit compensations (Bruni, Montanari) Behavioural typesBehavioural types Integration/compilation with STM???Integration/compilation with STM???

40 40 Summary A clean, simple, new model for asynchronous concurrency in C#A clean, simple, new model for asynchronous concurrency in C# –Declarative, local synchronization –Model good for 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 –Minimalist design – pieces to build whatever complex synchronization behaviours you need –Solid foundations –Works well in practice

41 41 Compiler Release Freely available (v.1.0.2) from http://research.microsoft.com/comega http://research.microsoft.com/comega Over 7000 downloads so far Please download and play!

42 42 For More Information http://research.microsoft.com/comega – –Papers, compiler, links http://channel9.msdn.com/ ShowPost.aspx?PostID=23947http://channel9.msdn.com/ ShowPost.aspx?PostID=23947 – –Channel 9 video http://www.omegaengine.com/ – –A public discussion forum – –Not run by Microsoft/Microsoft Research

43 43 Credits Nick Benton Gavin Bierman Luca Cardelli Cédric Fournet Erik Meijer Claudio Russo Wolfram Schulte Herman Venter Mark Shinwell, Hernan Melgratti, and all the other folks who’ve contributed to the implementation

44 44 Questions?

45 45 One-shot TimeoutBuffer class TimeoutBuffer { async empty(); async has(Object o); async timeout(); TimeoutBuffer(int delay) { Timer t = new Timer(new TimerCallBack(this.tick),delay); empty(); } void put(Object o) & empty() {has(o);} & timeout() {timeout();} void tick() & empty() {timeout();} & has(Object o) {has(o);} Object get() & timeout() {timeout(); throw new TimeOutExn();} & has(Object o) {has(o); return o;} }

46 46 Fairer reader/writer lock class ReaderWriterFair { ReaderWriter() { idle(); } ReaderWriter() { idle(); } private int n = 0; // protected by s() or t() private int n = 0; // protected by s() or t() public void Shared() & async idle() { n=1; s(); } public void Shared() & async idle() { n=1; s(); } public void Shared() & async s() { n++; s(); } public void Shared() & async s() { n++; s(); } public void ReleaseShared() & async s() { public void ReleaseShared() & async s() { if (--n == 0) idle(); else s(); if (--n == 0) idle(); else s(); } public void Exclusive() & async idle() {} public void Exclusive() & async idle() {} public void ReleaseExclusive() { idle(); } public void ReleaseExclusive() { idle(); } public void ReleaseShared() & async t() { public void ReleaseShared() & async t() { if (--n == 0) idleExclusive(); else t(); if (--n == 0) idleExclusive(); else t(); } public void Exclusive() & async s() { public void Exclusive() & async s() { t(); wait(); t(); wait(); } void wait() & async idleExclusive() {} void wait() & async idleExclusive() {} }

47 47 Current Data Access in C# SqlConnection conn = new SqlConnection(…); conn.Open(); Console.WriteLine("Please enter a city: "); string input = Console.ReadLine(); SqlCommand cmd = new SqlCommand("SELECT * FROM Customers WHERE city='"+input+"'", conn); SqlDataReader results = cmd.ExecuteReader(); Console.WriteLine("Customers from: "+input); while (results.Read()) { string Name = (string)results["ContactName"]; string Phone = (string)results["Phone"]; Console.WriteLine(Name + " -- "+Phone); } conn.Close(); WeakTypeWeakType Queries stored as strings Queries stored as strings Runtime type conversion conversion Using string to project attributes Using string to project attributes

48 48 API Access (Continued) Storing queries as strings is not only ugly but also a security risk – –Imagine where city=“’ OR 1=1 --” This will return the entire relation!! This is the source of many security loopholes in Web-based databases

49 49 Use Parameters and Typed Datasets… SqlDataAdapter da = new SqlDataAdapter( "SELECT * FROM Customers WHERE City= @city", conn ); SqlParameter cityParam = da.SelectCommand.Parameters.Add("@city", SqlDbType.VarChar, 80); cityParam.Value = input; NorthwindDataSet ds = new NorthwindDataSet(); da.Fill(ds, ds.Customers.TableName); foreach (NorthwindDataSet.CustomersRow dr in ds.Customers.Rows) { string Name = dr.ContactName; string Phone = dr.Phone; Console.WriteLine( Name + " -- " + Phone); }

50 50 Or Use Stored Procedures… CREATE PROCEDURE CustomersForCity @City nvarchar(80) AS SELECT * FROM Customers WHERE City = @City SqlCommand cmd = new SqlCommand("dbo.CustomersForCity",conn ); cmd.CommandType = CommandType.StoredProcedure; SqlParameter cityParam = cmd.Parameters.Add("@City", SqlDbType.VarChar, 80); cityParam.Value = input; SqlDataAdapter da = new SqlDataAdapter( cmd ); NorthwindDataSet ds = new NorthwindDataSet(); da.Fill(ds, ds.Customers.TableName ); foreach (NorthwindDataSet.CustomersRow dr in ds.Customers.Rows) { string Name = dr.ContactName; string Phone = dr.Phone; Console.WriteLine( Name + " -- " + Phone); }

51 51 Current XML Access in C# static void UseXPathDocument(string reviewsLocation, string bibLocation, XmlTextWriter xtw) { XPathDocument bibDoc = new XPathDocument(bibLocation); XPathDocument reviewsDoc = new XPathDocument(reviewsLocation); xtw.WriteStartElement("books-with-prices"); XPathNodeIterator books = bibDoc.CreateNavigator().Select("/bib/book"); while (books.MoveNext()) { XPathNodeIterator entries = reviewsDoc.CreateNavigator().Select("/reviews/entry"); XPathNodeIterator bookTitleItr = books.Current.SelectChildren("title",""); bookTitleItr.MoveNext(); XPathNodeIterator bookPriceItr = books.Current.SelectChildren("price",""); bookPriceItr.MoveNext(); while(entries.MoveNext()) { XPathNodeIterator entryTitleItr = entries.Current.SelectChildren("title",""); entryTitleItr.MoveNext(); XPathNodeIterator entryPriceItr = entries.Current.SelectChildren("price",""); entryPriceItr.MoveNext(); if (entryTitleItr.Current.Value == bookTitleItr.Current.Value) { WriteBook(xtw,bookTitleItr.Current.Value,entryPriceItr.Current.Value,bookPriceItr.Current.Value); } } } xtw.WriteEndElement(); }

52 52 Can’t We Do Better?

53 53 Minimal Extensions to C# New types: 1. 1.Streams ( T* ) 2. 2.Choice ( choice{int;string;} ) 3. 3.Tuples ( struct{int i;string s;} ) 4. 4.Content classes 5. 5.Nullables ( T? ) 6. 6. async (subtype of void ) XML literals Chords Generalized member access (cf. XPath) SQL-style select queries

54 54 Streams New type: T* (sequence of T ) Close relative of IEnumerable Streams generated by statement blocks that yield values Streams consumed by foreach loop No nested streams (no T** ) – –Just like in XQuery/XPath

55 55 Streams Example public static int* FromTo(int s, int e){ for (i = s; i <= e; i++) yield return i; } int* OneToTen = FromTo(1,10); foreach(intj in OneToTen) { Console.WriteLine(j); };

56 56 Why Streams? We use streams to represent – –The (homogenous) sequence of rows from a database table – –The iteration operator from XML Schema/DTDs

57 57 Anonymous structs New type: Constructed in obvious way: Project using dot notation: Like ML records but ordered, duplicates allowed struct{int i; string s; string s;} tup = new{i=42, s=“Gavin”, s=“Nick”}; int temp = tup.i; struct{int i; string s; string s;}

58 58 Why Anonymous structs? We use anonymous structs to represent – –A row (of heterogeneous fields) from a SQL table – –Ordered sequences of XML Schema/DTD elements, example:

59 59 Union Types New type: choice{T;S;} A value of this type is either a value of type T or a value of type S, example: Why? – –To represent choice in XML Schema/DTD choice{string; Button; int;} u = “hello”; u = new Button(); u = 42;

60 60 Content Class Like a normal class, except it contains a single unnamed member Useful for XML fidelity public class book { struct{ string title; choice{struct{editor editor;}*; struct{author author;}*; } string publisher; decimal price; }

61 61 bib = TCP/IP Illustrated Stevens W. Addison-Wesley 65.95 The Economics of Technology and Content for Digital TV Gerbarg Darcy CITI Kluwer Academic 129.95 ; XML Literals Now we have the types, we can write (strongly typed) XML literals in our code: <!ELEMENT book (title, (author* | editor*), publisher, price)> public class bib { struct{ book book; }*; } public class book { struct {string title; choice { struct { editor editor;}*; struct { author author;}*;} string publisher; decimal price;} }

62 62 Generalized Member Access (GMA) A key design feature of C Observe: C can encode XML-like values Want: some query-like facility Solution: extend the dot notation behaviour (to match “ / ” from XPath)

63 63 Generalized Member Access 1. 1.Sequences 2. 2.Anonymous struct types 3. 3.Choice types string* ss; ss.ToUpper(); struct{int i; string s; string s;} x; x.i; x.s; choice{string;Person;int;} y; y.Length; choice{int;Float;}?choice{int;Float;}? string*string* intint string*string*

64 64 XQuery Use Case Three For each book in the bibliography, list the title and authors, grouped inside a “result” element for $b in $bs/book return {$b/title} {$b/author} foreach (b in bs.book) { yield return {b.title} {b.author} ; } XQUERY Cω

65 65 Path Expressions GMA allows us to write OQL-like path expressions: static int* FromTo(int s, int e) { for (i = s; i <= e; i++) yield return i; } FromTo(0,100).{return it.ToString("x");}.ToUpper().{Console.WriteLine(it);};

66 66 Demonstration One Data access in C

67 67 SQL-Like Queries We support first-class SQL queries String conn = “...Initial Catalog=Northwind; "; Database DB = new Database(conn); Console.WriteLine("Please enter a city: "); string input = Console.ReadLine(); res = select * from DB.Customers where City==input ; Console.WriteLine("Customers from: "+input); res.{Console.WriteLine(it.ContactName + " --- " + it.Phone);};

68 68 Other Work The type system and operational semantics of (the data access fragment of) C have been formally (i.e. mathematically) specified – –To be presented at ECOOP 2005 C has other data access goodies – –More XPath-like extensions – –More SQL extensions

69 69 Summary C  extends C# with first class support for data access and asynchronous concurrencyC  extends C# with first class support for data access and asynchronous concurrency Data access extensionsData access extensions –Model good for both relational (SQL) and semi-structured (XML) data –Compile to either in-memory operations or external queries –Based on ideas from functional programming, but tweaked for compatibility with objects and XPath Concurrency modelConcurrency model –Model good for both local and distributed settings –Efficiently compiled to queues and automata –Based on join-calculus, but tweaked for compatibility with objects

70 70 Relationship with C# Note: C is not C# 3/4/5… ! – –We’re a research project – –We’re not sponsored by the C# or VS team – –[Actually, the C# team look at lots of research internal and external] "The C# team is excited about C  and other C#-based research projects that MSR-Cambridge is working on. We have no current plans to extend the C# language in this direction, but will continue to observe the progress of C  and other MSR-Cambridge projects." Scott Wiltamuth, C# Product Unit Manager


Download ppt "1 Concurrency in C  Nick Benton. 2 PPT: People Luca Cardelli Nick Benton Karthik Bhargavan Gavin Bierman Byron Cook Cédric Fournet Georges Gonthier Andy."

Similar presentations


Ads by Google