Presentation is loading. Please wait.

Presentation is loading. Please wait.

Ownership Types for Safe Programming: Preventing Data Races and Deadlocks Chandrasekhar Boyapati Robert Lee Martin Rinard Laboratory for Computer Science.

Similar presentations


Presentation on theme: "Ownership Types for Safe Programming: Preventing Data Races and Deadlocks Chandrasekhar Boyapati Robert Lee Martin Rinard Laboratory for Computer Science."— Presentation transcript:

1 Ownership Types for Safe Programming: Preventing Data Races and Deadlocks Chandrasekhar Boyapati Robert Lee Martin Rinard Laboratory for Computer Science Massachusetts Institute of Technology {chandra, rhlee, rinard}@lcs.mit.edu

2 Thread 1: x = x + 1; Thread 2: x = x + 2; Two threads access same data Two threads access same data At least one access is a write At least one access is a write No synchronization to separate accesses No synchronization to separate accesses Data Races in Multithreaded Programs

3 Avoiding Data Races Thread 1: x = x + 1; Thread 2: x = x + 2;

4 Avoiding Data Races Associate locks with shared mutable data Associate locks with shared mutable data Acquire lock before data access Acquire lock before data access Release lock after data access Release lock after data access Thread 1: lock(l); x = x + 1; unlock(l); Thread 2: lock(l); x = x + 2; unlock(l);

5 Thread 1 Thread 2 Thread n … Lock 1 Lock n Lock 2 Lock 3 Cycle of the form Cycle of the form  Thread 1 holds Lock 1, waits for Lock 2  Thread 2 holds Lock 2, waits for Lock 3 …  Thread n holds Lock n, waits for Lock 1 Deadlocks in Multithreaded Programs

6 Avoiding Deadlocks Thread 1 Thread 2 Thread n … Lock 1 Lock n Lock 2 Lock 3

7 Avoiding Deadlocks Thread 1 Thread 2 Thread n … Lock 1 Lock n Lock 2 Lock 3 Associate a partial order among locks Associate a partial order among locks Acquire locks in order Acquire locks in order ~ ~

8 Problem With Current Practice Locking discipline is not enforced Locking discipline is not enforced Inadvertent programming errors Inadvertent programming errors  Can cause data races and deadlocks Consequences can be severe Consequences can be severe  Non-deterministic, timing dependent bugs  Difficult to detect, reproduce, eliminate

9 Our Solution Static type system Static type system  Prevents both data races and deadlocks

10 Our Solution Static type system Static type system  Prevents both data races and deadlocks Programmers specify Programmers specify  How each object is protected from races  Partial order among locks Type checker statically verifies Type checker statically verifies  Objects are used only as specified  Locks are acquired in order

11 Talk Outline Motivation Motivation Type system Type system  Preventing data races  Preventing deadlocks Experience Experience Related work Related work Conclusions Conclusions

12 Preventing Data Races Programmers specify for every object Programmers specify for every object  Lock protecting the object, or  That the object needs no locks because  Object is immutable  Object is thread-local  Object has a unique pointer

13 Preventing Deadlocks Programmers specify lock ordering using Programmers specify lock ordering using  Static lock levels  Recursive data structures  Mutable trees  Monotonic DAGs  Runtime ordering Type checker statically verifies Type checker statically verifies  Locks are acquired in descending order  Specified order is a partial order

14 Lock Level Based Partial Orders Lock levels are partially ordered Lock levels are partially ordered Locks belong to lock levels Locks belong to lock levels Threads must acquire locks in descending order of lock levels Threads must acquire locks in descending order of lock levels

15 Lock Level Based Partial Orders class CombinedAccount { final Account savingsAccount = new Account(); final Account savingsAccount = new Account(); final Account checkingAccount = new Account(); final Account checkingAccount = new Account(); int balance() { int balance() { synchronized (savingsAccount) { synchronized (savingsAccount) { synchronized (checkingAccount) { synchronized (checkingAccount) { return savingsAccount.balance + checkingAccount.balance; return savingsAccount.balance + checkingAccount.balance; }}} }}}}

16 class CombinedAccount { LockLevel savingsLevel; LockLevel savingsLevel; LockLevel checkingLevel < savingsLevel; LockLevel checkingLevel < savingsLevel; final Account  self : savingsLevel  savingsAccount = new Account(); final Account  self : savingsLevel  savingsAccount = new Account(); final Account  self : checkingLevel  checkingAccount = new Account(); final Account  self : checkingLevel  checkingAccount = new Account(); int balance() locks (savingsLevel) { int balance() locks (savingsLevel) { synchronized (savingsAccount) { synchronized (savingsAccount) { synchronized (checkingAccount) { synchronized (checkingAccount) { return savingsAccount.balance + checkingAccount.balance; return savingsAccount.balance + checkingAccount.balance; }}} }}}} Lock Level Based Partial Orders

17 class CombinedAccount { LockLevel savingsLevel; LockLevel savingsLevel; LockLevel checkingLevel < savingsLevel; LockLevel checkingLevel < savingsLevel; final Account  self : savingsLevel  savingsAccount = new Account(); final Account  self : savingsLevel  savingsAccount = new Account(); final Account  self : checkingLevel  checkingAccount = new Account(); final Account  self : checkingLevel  checkingAccount = new Account(); int balance() locks (savingsLevel) { int balance() locks (savingsLevel) { synchronized (savingsAccount) { synchronized (savingsAccount) { synchronized (checkingAccount) { synchronized (checkingAccount) { return savingsAccount.balance + checkingAccount.balance; return savingsAccount.balance + checkingAccount.balance; }}} }}}} checkingLevel < savingsLevel Lock Level Based Partial Orders

18 class CombinedAccount { LockLevel savingsLevel; LockLevel savingsLevel; LockLevel checkingLevel < savingsLevel; LockLevel checkingLevel < savingsLevel; final Account  self : savingsLevel  savingsAccount = new Account(); final Account  self : savingsLevel  savingsAccount = new Account(); final Account  self : checkingLevel  checkingAccount = new Account(); final Account  self : checkingLevel  checkingAccount = new Account(); int balance() locks (savingsLevel) { int balance() locks (savingsLevel) { synchronized (savingsAccount) { synchronized (savingsAccount) { synchronized (checkingAccount) { synchronized (checkingAccount) { return savingsAccount.balance + checkingAccount.balance; return savingsAccount.balance + checkingAccount.balance; }}} }}}} savingsAccount belongs to savingsLevel checkingAccount belongs to checkingLevel Lock Level Based Partial Orders

19 class CombinedAccount { LockLevel savingsLevel; LockLevel savingsLevel; LockLevel checkingLevel < savingsLevel; LockLevel checkingLevel < savingsLevel; final Account  self : savingsLevel  savingsAccount = new Account(); final Account  self : savingsLevel  savingsAccount = new Account(); final Account  self : checkingLevel  checkingAccount = new Account(); final Account  self : checkingLevel  checkingAccount = new Account(); int balance() locks (savingsLevel) { int balance() locks (savingsLevel) { synchronized (savingsAccount) { synchronized (savingsAccount) { synchronized (checkingAccount) { synchronized (checkingAccount) { return savingsAccount.balance + checkingAccount.balance; return savingsAccount.balance + checkingAccount.balance; }}} }}}} locks are acquired in descending order Lock Level Based Partial Orders

20 class CombinedAccount { LockLevel savingsLevel; LockLevel savingsLevel; LockLevel checkingLevel < savingsLevel; LockLevel checkingLevel < savingsLevel; final Account  self : savingsLevel  savingsAccount = new Account(); final Account  self : savingsLevel  savingsAccount = new Account(); final Account  self : checkingLevel  checkingAccount = new Account(); final Account  self : checkingLevel  checkingAccount = new Account(); int balance() locks (savingsLevel) { int balance() locks (savingsLevel) { synchronized (savingsAccount) { synchronized (savingsAccount) { synchronized (checkingAccount) { synchronized (checkingAccount) { return savingsAccount.balance + checkingAccount.balance; return savingsAccount.balance + checkingAccount.balance; }}} }}}} locks held by callers > savingsLevel Lock Level Based Partial Orders

21 class CombinedAccount { LockLevel savingsLevel; LockLevel savingsLevel; LockLevel checkingLevel < savingsLevel; LockLevel checkingLevel < savingsLevel; final Account  self : savingsLevel  savingsAccount = new Account(); final Account  self : savingsLevel  savingsAccount = new Account(); final Account  self : checkingLevel  checkingAccount = new Account(); final Account  self : checkingLevel  checkingAccount = new Account(); int balance() locks (savingsLevel) { int balance() locks (savingsLevel) { synchronized (savingsAccount) { synchronized (savingsAccount) { synchronized (checkingAccount) { synchronized (checkingAccount) { return savingsAccount.balance + checkingAccount.balance; return savingsAccount.balance + checkingAccount.balance; }}} }}}} balance can acquire these locks Lock Level Based Partial Orders

22 Types Impose No Dynamic Overhead Typechecker Translator (Removes extra types) Compiler JVM Java bytecodes + Extra types types Java

23 Lock Level Based Partial Orders Bounded number of lock levels Bounded number of lock levels Unbounded number of locks Unbounded number of locks Lock levels support programs where the maximum number of locks simultaneously held by a thread is bounded Lock levels support programs where the maximum number of locks simultaneously held by a thread is bounded We use other mechanisms for other cases We use other mechanisms for other cases

24 Type System Preventing data races Preventing data races Preventing deadlocks using Preventing deadlocks using  Static lock levels  Recursive data structures  Mutable trees  Monotonic DAGs  Runtime ordering

25 Tree Based Partial Orders Locks in a level can be tree-ordered Locks in a level can be tree-ordered Using data structures with tree backbones Using data structures with tree backbones  Doubly linked lists  Trees with parent/sibling pointers  Threaded trees…

26 class Node { Node left; Node left; Node right; Node right; synchronized void rotateRight() { synchronized void rotateRight() { Node x = this.right; synchronized (x) { Node x = this.right; synchronized (x) { Node v = x.left; synchronized (v) { Node v = x.left; synchronized (v) { Node w = v.right; Node w = v.right; v.right = null; v.right = null; x.left = w; x.left = w; this.right = v; this.right = v; v.right = x; v.right = x; }}} }}}} x this v w y this v x y u w u Tree Based Partial Orders

27 class Node  self : l  { tree Node  self : l  left; tree Node  self : l  left; tree Node  self : l  right; tree Node  self : l  right; synchronized void rotateRight() locks (this) { synchronized void rotateRight() locks (this) { Node x = this.right; synchronized (x) { Node x = this.right; synchronized (x) { Node v = x.left; synchronized (v) { Node v = x.left; synchronized (v) { Node w = v.right; Node w = v.right; v.right = null; v.right = null; x.left = w; x.left = w; this.right = v; this.right = v; v.right = x; v.right = x; }}} }}}} x this v w y this v x y u w u Tree Based Partial Orders nodes must be locked in tree order

28 class Node  self : l  { tree Node  self : l  left; tree Node  self : l  left; tree Node  self : l  right; tree Node  self : l  right; synchronized void rotateRight() locks (this) { synchronized void rotateRight() locks (this) { Node x = this.right; synchronized (x) { Node x = this.right; synchronized (x) { Node v = x.left; synchronized (v) { Node v = x.left; synchronized (v) { Node w = v.right; Node w = v.right; v.right = null; v.right = null; x.left = w; x.left = w; this.right = v; this.right = v; v.right = x; v.right = x; }}} }}}} x this v w y this v x y u w u nodes are locked in tree order Tree Based Partial Orders

29 Checking Tree Mutations A tree edge may be deleted A tree edge may be deleted A tree edge from x to y may be added iff A tree edge from x to y may be added iff  y is a Root  x is not in Tree(y) For onstage nodes x & y, analysis tracks For onstage nodes x & y, analysis tracks  If y is a Root  If x is not in Tree(y)  If x has a tree edge to y Lightweight shape analysis Lightweight shape analysis

30 class Node  self : l  { tree Node  self : l  left; tree Node  self : l  left; tree Node  self : l  right; tree Node  self : l  right; synchronized void rotateRight() locks (this) { synchronized void rotateRight() locks (this) { Node x = this.right; synchronized (x) { Node x = this.right; synchronized (x) { Node v = x.left; synchronized (v) { Node v = x.left; synchronized (v) { Node w = v.right; Node w = v.right; v.right = null; v.right = null; x.left = w; x.left = w; this.right = v; this.right = v; v.right = x; v.right = x; }}} }}}} Checking Tree Mutations x this v w y this v x y u w u

31 class Node  self : l  { tree Node  self : l  left; tree Node  self : l  left; tree Node  self : l  right; tree Node  self : l  right; synchronized void rotateRight() locks (this) { synchronized void rotateRight() locks (this) { Node x = this.right; synchronized (x) { Node x = this.right; synchronized (x) { Node v = x.left; synchronized (v) { Node v = x.left; synchronized (v) { Node w = v.right; Node w = v.right; v.right = null; v.right = null; x.left = w; x.left = w; this.right = v; this.right = v; v.right = x; v.right = x; }}} }}}} Checking Tree Mutations this v x y u w x = this.right v = x.left w = v.right

32 class Node  self : l  { tree Node  self : l  left; tree Node  self : l  left; tree Node  self : l  right; tree Node  self : l  right; synchronized void rotateRight() locks (this) { synchronized void rotateRight() locks (this) { Node x = this.right; synchronized (x) { Node x = this.right; synchronized (x) { Node v = x.left; synchronized (v) { Node v = x.left; synchronized (v) { Node w = v.right; Node w = v.right; v.right = null; v.right = null; x.left = w; x.left = w; this.right = v; this.right = v; v.right = x; v.right = x; }}} }}}} Checking Tree Mutations this v x y u w x = this.right v = x.left w is Root v not in Tree(w) x not in Tree(w) this not in Tree(w)

33 class Node  self : l  { tree Node  self : l  left; tree Node  self : l  left; tree Node  self : l  right; tree Node  self : l  right; synchronized void rotateRight() locks (this) { synchronized void rotateRight() locks (this) { Node x = this.right; synchronized (x) { Node x = this.right; synchronized (x) { Node v = x.left; synchronized (v) { Node v = x.left; synchronized (v) { Node w = v.right; Node w = v.right; v.right = null; v.right = null; x.left = w; x.left = w; this.right = v; this.right = v; v.right = x; v.right = x; }}} }}}} Checking Tree Mutations this w x y x = this.right w = x.left v is Root x not in Tree(v) w not in Tree(v) this not in Tree(v) v u

34 class Node  self : l  { tree Node  self : l  left; tree Node  self : l  left; tree Node  self : l  right; tree Node  self : l  right; synchronized void rotateRight() locks (this) { synchronized void rotateRight() locks (this) { Node x = this.right; synchronized (x) { Node x = this.right; synchronized (x) { Node v = x.left; synchronized (v) { Node v = x.left; synchronized (v) { Node w = v.right; Node w = v.right; v.right = null; v.right = null; x.left = w; x.left = w; this.right = v; this.right = v; v.right = x; v.right = x; }}} }}}} Checking Tree Mutations this u v x v = this.right w = x.left x is Root this not in Tree(x) v not in Tree(x) w y

35 class Node  self : l  { tree Node  self : l  left; tree Node  self : l  left; tree Node  self : l  right; tree Node  self : l  right; synchronized void rotateRight() locks (this) { synchronized void rotateRight() locks (this) { Node x = this.right; synchronized (x) { Node x = this.right; synchronized (x) { Node v = x.left; synchronized (v) { Node v = x.left; synchronized (v) { Node w = v.right; Node w = v.right; v.right = null; v.right = null; x.left = w; x.left = w; this.right = v; this.right = v; v.right = x; v.right = x; }}} }}}} Checking Tree Mutations this u v x v = this.right w = x.left x = v.right w y

36 Type System Preventing data races Preventing data races Preventing deadlocks using Preventing deadlocks using  Static lock levels  Recursive data structures  Mutable trees  Monotonic DAGs  Runtime ordering

37 DAG Based Partial Orders Locks in a level can be DAG-ordered Locks in a level can be DAG-ordered DAGs cannot be arbitrarily modified DAGs cannot be arbitrarily modified DAGs can be built bottom-up by DAGs can be built bottom-up by  Allocating a new node  Initializing its DAG fields class Node  self : l  { dag Node  self : l  left; dag Node  self : l  left; dag Node  self : l  right; dag Node  self : l  right; …}

38 Type System Preventing data races Preventing data races Preventing deadlocks using Preventing deadlocks using  Static lock levels  Recursive data structures  Mutable trees  Monotonic DAGs  Runtime ordering

39 class Account { int balance = 0; int balance = 0; void deposit(int x) { balance += x; } void deposit(int x) { balance += x; } void withdraw(int x) { balance -= x; } void withdraw(int x) { balance -= x; }} void transfer(Account a1, Account a2, int x) { synchronized (a1, a2) in { a1.withdraw(x); a2.deposit(x); } synchronized (a1, a2) in { a1.withdraw(x); a2.deposit(x); }} Runtime Ordering of Locks

40 class Account implements Dynamic { int balance = 0; int balance = 0; void deposit(int x) requires (this) { balance += x; } void deposit(int x) requires (this) { balance += x; } void withdraw(int x) requires (this) { balance -= x; } void withdraw(int x) requires (this) { balance -= x; }} void transfer(Account  self : v  a1, Account  self : v  a2, int x) locks(v) { synchronized (a1, a2) in { a1.withdraw(x); a2.deposit(x); } synchronized (a1, a2) in { a1.withdraw(x); a2.deposit(x); }} Runtime Ordering of Locks

41 class Account implements Dynamic { int balance = 0; int balance = 0; void deposit(int x) requires (this) { balance += x; } void deposit(int x) requires (this) { balance += x; } void withdraw(int x) requires (this) { balance -= x; } void withdraw(int x) requires (this) { balance -= x; }} void transfer(Account  self : v  a1, Account  self : v  a2, int x) locks(v) { synchronized (a1, a2) in { a1.withdraw(x); a2.deposit(x); } synchronized (a1, a2) in { a1.withdraw(x); a2.deposit(x); }} Runtime Ordering of Locks Account objects are dynamically ordered

42 class Account implements Dynamic { int balance = 0; int balance = 0; void deposit(int x) requires (this) { balance += x; } void deposit(int x) requires (this) { balance += x; } void withdraw(int x) requires (this) { balance -= x; } void withdraw(int x) requires (this) { balance -= x; }} void transfer(Account  self : v  a1, Account  self : v  a2, int x) locks(v) { synchronized (a1, a2) in { a1.withdraw(x); a2.deposit(x); } synchronized (a1, a2) in { a1.withdraw(x); a2.deposit(x); }} Runtime Ordering of Locks locks are acquired in runtime order

43 Reducing Programming Overhead Type inference and default types significantly reduce programming overhead Type inference and default types significantly reduce programming overhead Single threaded programs need no annotations Single threaded programs need no annotations Our approach supports separate compilation Our approach supports separate compilation

44 Experience

45 Multithreaded Server Programs Program Lines of code Lines changed elevator52315 http server 56326 chat server 30822 stock quote server 24212 game server 87 8711 phone (database) server 30210

46 Java Libraries Program Lines of code Lines changed java.util.Hashtable101153java.util.HashMap 852 85246 java.util.Vector 992 99235 java.util.ArrayList 533 53318

47 Java Libraries Program Lines of code Lines changed java.io.OutputStream134 3java.io.BufferedWriter253 9 java.io.OutputStreamWriter26611 java.io.Writer177 6 java.io.PrintStream56814java.io.FilterOutputStream148 5

48 Related Work

49 Static tools Static tools  Korty ( USENIX ’89 )  Sterling ( USENIX ’93 )  Detlefs, Leino, Nelson, Saxe ( SRC ’98 )  Engler, Chen, Hallem, Chou, Chelf ( SOSP ’01 ) Dynamic tools Dynamic tools  Steele ( POPL ’90 )  Dinning, Schonberg ( PPoPP ’90 )  Savage,Burrows,Nelson,Sobalvarro,Anderson ( SOSP ’97 )  Praun, Gross ( OOPSLA ’01 )  Choi,Lee,Loginov,O’Callahan,Sarkar,Sridharan ( PLDI ’02 ) Related Work

50 Type systems Type systems  Flanagan, Freund ( PLDI ’00 )  Bacon, Strom, Tarafdar ( OOPSLA ’00 ) Related Work

51 Ownership types Ownership types  Clarke, Potter, Noble ( OOPSLA ’98 ), ( ECOOP ’01 )  Clarke, Drossopoulou ( OOPSLA ’02 )  Aldrich, Kostadinov, Chambers ( OOPSLA ’02 )  Boyapati, Rinard ( OOPSLA ’01 )  Boyapati, Lee, Rinard ( OOPSLA ’02 )  Boyapati, Liskov, Shrira ( MIT ’02 )  Boyapati, Salcianu, Beebee, Rinard ( MIT ’02 ) Related Work

52 Ownership Types We have used ownership types for We have used ownership types for  Object encapsulation  Constraining heap aliasing  Modular effects clauses with subtyping  Preventing data races and deadlocks  Safe lazy upgrades in OODBs  Safe region-based memory management Ownership types can serve as a foundation for future OO languages Ownership types can serve as a foundation for future OO languages

53 Conclusions Data races and deadlocks make multithreaded programming difficult Data races and deadlocks make multithreaded programming difficult We presented a static type system that prevents data races and deadlocks We presented a static type system that prevents data races and deadlocks Our type system is expressive Our type system is expressive Programs can be efficient and reliable Programs can be efficient and reliable

54 Ownership Types for Safe Programming: Preventing Data Races and Deadlocks Chandrasekhar Boyapati Robert Lee Martin Rinard Laboratory for Computer Science Massachusetts Institute of Technology {chandra, rhlee, rinard}@lcs.mit.edu


Download ppt "Ownership Types for Safe Programming: Preventing Data Races and Deadlocks Chandrasekhar Boyapati Robert Lee Martin Rinard Laboratory for Computer Science."

Similar presentations


Ads by Google