Presentation is loading. Please wait.

Presentation is loading. Please wait.

Chandrasekhar Boyapati Laboratory for Computer Science Massachusetts Institute of Technology Ownership Types for Safe Programming.

Similar presentations


Presentation on theme: "Chandrasekhar Boyapati Laboratory for Computer Science Massachusetts Institute of Technology Ownership Types for Safe Programming."— Presentation transcript:

1 Chandrasekhar Boyapati Laboratory for Computer Science Massachusetts Institute of Technology Ownership Types for Safe Programming

2 Motivation Making software reliable Is important Is important  Role in civil infrastructure  Effect on economy Is challenging because of complexity Is challenging because of complexity

3 This Talk Type system to increase software reliability Statically prevents many classes of errors Statically prevents many classes of errors  Prevents data races and deadlocks  Prevents representation exposure  Enables region-based memory management  Enables upgrades in persistent object stores Checking is fast and scalable Checking is fast and scalable Requires little programming overhead Requires little programming overhead Promising way for increasing reliability Promising way for increasing reliability

4 Outline Preventing data races Preventing data races Preventing deadlocks Preventing deadlocks Type inference Type inference Experience Experience Preventing other errors Preventing other errors

5 Preventing Data Races

6 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

7 Why Data Races are a Problem Some correct programs contain data races Some correct programs contain data races But most races are programming errors But most races are programming errors  Code intended to execute atomically  Synchronization omitted by mistake Consequences can be severe Consequences can be severe  Nondeterministic timing-dependent bugs  Difficult to detect, reproduce, eliminate

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

9 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);

10 Avoiding Data Races Problem: Locking is not enforced! Inadvertent programming errors… Thread 1: lock(l); x = x + 1; unlock(l); Thread 2: lock(l); x = x + 2; unlock(l);

11 Our Solution Type system for object-oriented languages Type system for object-oriented languages Statically prevents data races Statically prevents data races

12 Our Solution Type system for object-oriented languages Type system for object-oriented languages Statically prevents data races Statically prevents data races Programmers specify Programmers specify  How each object is protected from races  In types of variables pointing to objects Type checker statically verifies Type checker statically verifies  Objects are used only as specified

13 Protection Mechanism of an Object Specifies the lock protecting the object, or Specifies the lock protecting the object, or Specifies object needs no locks because Specifies object needs no locks because  Object is immutable  Object is thread-local  Object has a unique pointer

14 Protection Mechanism of an Object Specifies the lock protecting the object, or Specifies the lock protecting the object, or Specifies object needs no locks because Specifies object needs no locks because  Object is immutable  Object is thread-local  Object has a unique pointer

15 Preventing Data Races class Account { int balance = 0; int balance = 0; void deposit(int x) { balance += x; } void deposit(int x) { balance += x; }} Account a1 = new Account(); fork { synchronized (a1) { a1.deposit(10); } }; Account a2 = new Account(); a2.deposit(10);

16 class Account { int balance = 0; int balance = 0; void deposit(int x) requires (this) { balance += x; } void deposit(int x) requires (this) { balance += x; }} Account  self  a1 = new Account(); fork { synchronized (a1) { a1.deposit(10); } }; Account  thisThread  a2 = new Account(); a2.deposit(10); Preventing Data Races

17 class Account { int balance = 0; int balance = 0; void deposit(int x) requires (this) { balance += x; } void deposit(int x) requires (this) { balance += x; }} Account  self  a1 = new Account(); fork { synchronized (a1) { a1.deposit(10); } }; Account  thisThread  a2 = new Account(); a2.deposit(10); a1 is protected by its own lock a2 is thread-local Preventing Data Races

18 class Account { int balance = 0; int balance = 0; void deposit(int x) requires (this) { balance += x; } void deposit(int x) requires (this) { balance += x; }} Account  self  a1 = new Account(); fork { synchronized (a1) { a1.deposit(10); } }; Account  thisThread  a2 = new Account(); a2.deposit(10); deposit requires lock on “this” Preventing Data Races

19 class Account { int balance = 0; int balance = 0; void deposit(int x) requires (this) { balance += x; } void deposit(int x) requires (this) { balance += x; }} Account  self  a1 = new Account(); fork { synchronized (a1) { a1.deposit(10); } }; Account  thisThread  a2 = new Account(); a2.deposit(10); a1 is locked before calling deposit a2 need not be locked Preventing Data Races

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

21 class Account { int balance = 0; int balance = 0; void deposit(int x) requires (this) { balance += x; } void deposit(int x) requires (this) { balance += x; }} Account  self  a1 = new Account(); fork { synchronized (a1) { a1.deposit(10); } }; Account  thisThread  a2 = new Account(); a2.deposit(10); Preventing Data Races

22 class Account { int balance = 0; int balance = 0; void deposit(int x) { balance += x; } void deposit(int x) { balance += x; }} Account a1 = new Account(); fork { synchronized (a1) { a1.deposit(10); } }; Account a2 = new Account(); a2.deposit(10);

23 Object Ownership

24 Every object is owned by Every object is owned by  Itself, or  Another object, or  Special per-thread owner called thisThread Ownership relation forms a forest of trees Ownership relation forms a forest of trees thisThreadthisThread Thread2 objects Thread1 objects Potentially shared objects

25 Object Ownership Objects with a thisThread as their root owner Objects with a thisThread as their root owner  Are local to the corresponding thread Objects with an object as their root owner Objects with an object as their root owner  Are potentially shared between threads thisThreadthisThread Thread2 objects Thread1 objects Potentially shared objects

26 Object Ownership Every object is protected by its root owner Every object is protected by its root owner For race-free access to an object For race-free access to an object  A thread must lock its root owner A thread implicitly holds lock on its thisThread A thread implicitly holds lock on its thisThread thisThreadthisThread Thread2 objects Thread1 objects Potentially shared objects

27 class TStack { TNode head; TNode head; void push(T value) {…} void push(T value) {…} T pop() {…} T pop() {…}} class TNode { TNode next; TNode next; T value; T value; …} class T {…} TStack Example value next head value next value next … … … TStack TNode T

28 class TStack  stackOwner, TOwner  { TNode  this, TOwner  head; TNode  this, TOwner  head; …} class TNode  nodeOwner, TOwner  { TNode  nodeOwner, TOwner  next; TNode  nodeOwner, TOwner  next; T  TOwner  value; T  TOwner  value; …} TStack TNode T

29 TStack Example Classes are parameterized with owners First owner owns the “this” object class TStack  stackOwner, TOwner  { TNode  this, TOwner  head; TNode  this, TOwner  head; …} class TNode  nodeOwner, TOwner  { TNode  nodeOwner, TOwner  next; TNode  nodeOwner, TOwner  next; T  TOwner  value; T  TOwner  value; …} TStack TNode T

30 TStack Example TStack owns the head TNode class TStack  stackOwner, TOwner  { TNode  this, TOwner  head; TNode  this, TOwner  head; …} class TNode  nodeOwner, TOwner  { TNode  nodeOwner, TOwner  next; TNode  nodeOwner, TOwner  next; T  TOwner  value; T  TOwner  value; …} TStack TNode T

31 TStack Example All TNodes have the same owner class TStack  stackOwner, TOwner  { TNode  this, TOwner  head; TNode  this, TOwner  head; …} class TNode  nodeOwner, TOwner  { TNode  nodeOwner, TOwner  next; TNode  nodeOwner, TOwner  next; T  TOwner  value; T  TOwner  value; …} TStack TNode T

32 TStack Example s1 is a thread-local stack with thread-local elements class TStack  stackOwner, TOwner  { TNode  this, TOwner  head; TNode  this, TOwner  head; …} class TNode  nodeOwner, TOwner  { TNode  nodeOwner, TOwner  next; TNode  nodeOwner, TOwner  next; T  TOwner  value; T  TOwner  value; …} TStack  thisThread, thisThread  s1; TStack  thisThread, self  s2; TStack  self, self  s3; thisThread TStack TNode T

33 TStack Example s2 is a thread-local stack with shared elements class TStack  stackOwner, TOwner  { TNode  this, TOwner  head; TNode  this, TOwner  head; …} class TNode  nodeOwner, TOwner  { TNode  nodeOwner, TOwner  next; TNode  nodeOwner, TOwner  next; T  TOwner  value; T  TOwner  value; …} TStack  thisThread, thisThread  s1; TStack  thisThread, self  s2; TStack  self, self  s3; thisThread TStack TNode T

34 TStack Example s3 is a shared stack with shared elements class TStack  stackOwner, TOwner  { TNode  this, TOwner  head; TNode  this, TOwner  head; …} class TNode  nodeOwner, TOwner  { TNode  nodeOwner, TOwner  next; TNode  nodeOwner, TOwner  next; T  TOwner  value; T  TOwner  value; …} TStack  thisThread, thisThread  s1; TStack  thisThread, self  s2; TStack  self, self  s3; TStack TNode T

35 class TStack  stackOwner, TOwner  { TNode  this, TOwner  head; TNode  this, TOwner  head; … T  TOwner  pop() requires (this) { T  TOwner  pop() requires (this) { if (head == null) return null; if (head == null) return null; T  TOwner  value = head.value(); T  TOwner  value = head.value(); head = head.next(); head = head.next(); return value; return value; }} class TNode  nodeOwner, TOwner  { T  TOwner  value() requires (this) {…} T  TOwner  value() requires (this) {…} TNode  nodeOwner, TOwner  next() requires (this) {…} TNode  nodeOwner, TOwner  next() requires (this) {…} …} TStack Example Methods can require callers to hold locks on root owners

36 class TStack  stackOwner, TOwner  { TNode  this, TOwner  head; TNode  this, TOwner  head; … T  TOwner  pop() requires (this) { T  TOwner  pop() requires (this) { if (head == null) return null; if (head == null) return null; T  TOwner  value = head.value(); T  TOwner  value = head.value(); head = head.next(); head = head.next(); return value; return value; }} class TNode  nodeOwner, TOwner  { T  TOwner  value() requires (this) {…} T  TOwner  value() requires (this) {…} TNode  nodeOwner, TOwner  next() requires (this) {…} TNode  nodeOwner, TOwner  next() requires (this) {…} …} Type Checking Pop Method value next head value next value next … … … TStack TNode T

37 Locks held thisThread,RootOwner(this) class TStack  stackOwner, TOwner  { TNode  this, TOwner  head; TNode  this, TOwner  head; … T  TOwner  pop() requires (this) { T  TOwner  pop() requires (this) { if (head == null) return null; if (head == null) return null; T  TOwner  value = head.value(); T  TOwner  value = head.value(); head = head.next(); head = head.next(); return value; return value; }} class TNode  nodeOwner, TOwner  { T  TOwner  value() requires (this) {…} T  TOwner  value() requires (this) {…} TNode  nodeOwner, TOwner  next() requires (this) {…} TNode  nodeOwner, TOwner  next() requires (this) {…} …}

38 Type Checking Pop Method Locks held thisThread,RootOwner(this) Locks required RootOwner(this) class TStack  stackOwner, TOwner  { TNode  this, TOwner  head; TNode  this, TOwner  head; … T  TOwner  pop() requires (this) { T  TOwner  pop() requires (this) { if (head == null) return null; if (head == null) return null; T  TOwner  value = head.value(); T  TOwner  value = head.value(); head = head.next(); head = head.next(); return value; return value; }} class TNode  nodeOwner, TOwner  { T  TOwner  value() requires (this) {…} T  TOwner  value() requires (this) {…} TNode  nodeOwner, TOwner  next() requires (this) {…} TNode  nodeOwner, TOwner  next() requires (this) {…} …}

39 Type Checking Pop Method Locks held thisThread,RootOwner(this) Locks required ? class TStack  stackOwner, TOwner  { TNode  this, TOwner  head; TNode  this, TOwner  head; … T  TOwner  pop() requires (this) { T  TOwner  pop() requires (this) { if (head == null) return null; if (head == null) return null; T  TOwner  value = head.value(); T  TOwner  value = head.value(); head = head.next(); head = head.next(); return value; return value; }} class TNode  nodeOwner, TOwner  { T  TOwner  value() requires (this) {…} T  TOwner  value() requires (this) {…} TNode  nodeOwner, TOwner  next() requires (this) {…} TNode  nodeOwner, TOwner  next() requires (this) {…} …}

40 Type Checking Pop Method Locks held thisThread,RootOwner(this) Locks required RootOwner(head) class TStack  stackOwner, TOwner  { TNode  this, TOwner  head; TNode  this, TOwner  head; … T  TOwner  pop() requires (this) { T  TOwner  pop() requires (this) { if (head == null) return null; if (head == null) return null; T  TOwner  value = head.value(); T  TOwner  value = head.value(); head = head.next(); head = head.next(); return value; return value; }} class TNode  nodeOwner, TOwner  { T  TOwner  value() requires (this) {…} T  TOwner  value() requires (this) {…} TNode  nodeOwner, TOwner  next() requires (this) {…} TNode  nodeOwner, TOwner  next() requires (this) {…} …}

41 Type Checking Pop Method Locks held thisThread,RootOwner(this) Locks required RootOwner(head) = RootOwner(this) class TStack  stackOwner, TOwner  { TNode  this, TOwner  head; TNode  this, TOwner  head; … T  TOwner  pop() requires (this) { T  TOwner  pop() requires (this) { if (head == null) return null; if (head == null) return null; T  TOwner  value = head.value(); T  TOwner  value = head.value(); head = head.next(); head = head.next(); return value; return value; }} class TNode  nodeOwner, TOwner  { T  TOwner  value() requires (this) {…} T  TOwner  value() requires (this) {…} TNode  nodeOwner, TOwner  next() requires (this) {…} TNode  nodeOwner, TOwner  next() requires (this) {…} …}

42 Type Checking Pop Method Locks held thisThread,RootOwner(this) Locks required RootOwner(this),RootOwner(head) = RootOwner(this) class TStack  stackOwner, TOwner  { TNode  this, TOwner  head; TNode  this, TOwner  head; … T  TOwner  pop() requires (this) { T  TOwner  pop() requires (this) { if (head == null) return null; if (head == null) return null; T  TOwner  value = head.value(); T  TOwner  value = head.value(); head = head.next(); head = head.next(); return value; return value; }} class TNode  nodeOwner, TOwner  { T  TOwner  value() requires (this) {…} T  TOwner  value() requires (this) {…} TNode  nodeOwner, TOwner  next() requires (this) {…} TNode  nodeOwner, TOwner  next() requires (this) {…} …}

43 Type Checking Pop Method class TStack  stackOwner, TOwner  { TNode  this, TOwner  head; TNode  this, TOwner  head; … T  TOwner  pop() requires (this) { T  TOwner  pop() requires (this) { if (head == null) return null; if (head == null) return null; T  TOwner  value = head.value(); T  TOwner  value = head.value(); head = head.next(); head = head.next(); return value; return value; }} class TNode  nodeOwner, TOwner  { T  TOwner  value() requires (this) {…} T  TOwner  value() requires (this) {…} TNode  nodeOwner, TOwner  next() requires (this) {…} TNode  nodeOwner, TOwner  next() requires (this) {…} …}

44 Preventing Data Races Data races make programming difficult Data races make programming difficult Our type system prevents data races Our type system prevents data races Programmers specify Programmers specify  How each object is protected from races Type checker statically verifies Type checker statically verifies  Objects are used only as specified

45 Other Benefits of Race-free Types

46 Data races expose the effects of Data races expose the effects of  Weak memory consistency models  Standard compiler optimizations

47 What is the value of z? Thread 1: y=0; y=0; x=1; x=1; Thread 2: z=x+y; Initially: x=0; x=0; y=1; y=1;

48 What is the value of z? Thread 1: y=0; y=0; x=1; x=1; Thread 2: z=x+y; Initially: x=0; x=0; y=1; y=1; z=x+y;y=0;x=1;z=1y=0;z=x+y;x=1;z=0y=0;x=1;z=x+y;z=1 Possible Interleavings

49 What is the value of z? Thread 1: y=0; y=0; x=1; x=1; Thread 2: z=x+y; Initially: x=0; x=0; y=1; y=1; z=x+y;y=0;x=1;z=1y=0;z=x+y;x=1;z=0y=0;x=1;z=x+y;z=1 x=1;z=x+y;y=0;z=2 !!! Possible Interleavings Above instruction reordering legal in single-threaded programs Violates sequential consistency in multithreaded programs

50 Weak Memory Consistency Models Are complicated in presence of data races Are complicated in presence of data races Original Java memory model was Original Java memory model was  Ambiguous and buggy Formal semantics still under development Formal semantics still under development  Manson, Pugh ( Java Grande/ISCOPE ’01 )  Maessen, Arvind, Shen ( OOPSLA ’00 )

51 Other Benefits of Race-free Types Data races expose effects of Data races expose effects of  Weak memory consistency models  Standard compiler optimizations Races complicate program analysis Races complicate program analysis Races complicate human understanding Races complicate human understanding Race-free languages Race-free languages  Eliminate these issues  Make multithreaded programming tractable

52 Outline Preventing data races Preventing data races Preventing deadlocks Preventing deadlocks Type inference Type inference Experience Experience Preventing other errors Preventing other errors

53 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

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

55 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 ~ ~

56 Avoiding Deadlocks Thread 1 Thread 2 Thread n … Lock 1 Lock n Lock 2 Lock 3 ~ ~ Problem: Lock ordering is not enforced! Inadvertent programming errors…

57 Our Solution Static type system that prevents deadlocks Static type system that prevents deadlocks Programmers specify Programmers specify  Partial order among locks Type checker statically verifies Type checker statically verifies  Locks are acquired in descending order  Specified order is a partial order

58 Preventing Deadlocks Programmers specify lock ordering using Programmers specify lock ordering using  Locks levels  Recursive data structures  Tree-based data structures  DAG-based data structures  Runtime ordering

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

60 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; }}} }}}}

61 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

62 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

63 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

64 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

65 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

66 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

67 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

68 Preventing Deadlocks Programmers specify lock ordering using Programmers specify lock ordering using  Locks levels  Recursive data structures  Tree-based data structures  DAG-based data structures  Runtime ordering

69 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 or sibling pointers  Threaded trees…

70 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

71 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

72 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

73 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 flow sensitive analysis checks that tree order is preserved Tree Based Partial Orders

74 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

75 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

76 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

77 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)

78 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

79 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

80 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

81 Preventing Deadlocks Programmers specify lock ordering using Programmers specify lock ordering using  Locks levels  Recursive data structures  Tree-based data structures  DAG-based data structures  Runtime ordering

82 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; …}

83 Preventing Deadlocks Programmers specify lock ordering using Programmers specify lock ordering using  Locks levels  Recursive data structures  Tree-based data structures  DAG-based data structures  Runtime ordering

84 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

85 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

86 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

87 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

88 Preventing Deadlocks Static type system that prevents deadlocks Static type system that prevents deadlocks Programmers specify Programmers specify  Partial order among locks Type checker statically verifies Type checker statically verifies  Locks are acquired in descending order  Specified order is a partial order

89 Reducing Programming Overhead

90 class A  oa1, oa2  {…} class B  ob1, ob2, ob3  extends A  ob1, ob3  {…} class C { void m(B  this, oc1, thisThread  b) { void m(B  this, oc1, thisThread  b) { A a1; A a1; B b1; B b1; b1 = b; b1 = b; a1 = b1; a1 = b1; }} Inferring Owners of Local Variables

91 class A  oa1, oa2  {…} class B  ob1, ob2, ob3  extends A  ob1, ob3  {…} class C { void m(B  this, oc1, thisThread  b) { void m(B  this, oc1, thisThread  b) { A  x1, x2  a1; A  x1, x2  a1; B  x3, x4, x5  b1; B  x3, x4, x5  b1; b1 = b; b1 = b; a1 = b1; a1 = b1; }} Inferring Owners of Local Variables Augment unknown types with owners

92 class A  oa1, oa2  {…} class B  ob1, ob2, ob3  extends A  ob1, ob3  {…} class C { void m(B  this, oc1, thisThread  b) { void m(B  this, oc1, thisThread  b) { A  x1, x2  a1; A  x1, x2  a1; B  x3, x4, x5  b1; B  x3, x4, x5  b1; b1 = b; b1 = b; a1 = b1; a1 = b1; }} Inferring Owners of Local Variables Gather constraints x3 = this x4 = oc1 x5 = thisThread

93 class A  oa1, oa2  {…} class B  ob1, ob2, ob3  extends A  ob1, ob3  {…} class C { void m(B  this, oc1, thisThread  b) { void m(B  this, oc1, thisThread  b) { A  x1, x2  a1; A  x1, x2  a1; B  x3, x4, x5  b1; B  x3, x4, x5  b1; b1 = b; b1 = b; a1 = b1; a1 = b1; }} Inferring Owners of Local Variables Gather constraints x3 = this x4 = oc1 x5 = thisThread x1 = x3 x2 = x5

94 class A  oa1, oa2  {…} class B  ob1, ob2, ob3  extends A  ob1, ob3  {…} class C { void m(B  this, oc1, thisThread  b) { void m(B  this, oc1, thisThread  b) { A  this, thisThread  a1; A  this, thisThread  a1; B  this, oc1, thisThread  b1; B  this, oc1, thisThread  b1; b1 = b; b1 = b; a1 = b1; a1 = b1; }} Inferring Owners of Local Variables Solve constraints x3 = this x4 = oc1 x5 = thisThread x1 = x3 x2 = x5

95 class A  oa1, oa2  {…} class B  ob1, ob2, ob3  extends A  ob1, ob3  {…} class C { void m(B  this, oc1, thisThread  b) { void m(B  this, oc1, thisThread  b) { A  this, thisThread  a1; A  this, thisThread  a1; B  this, oc1, thisThread  b1; B  this, oc1, thisThread  b1; b1 = b; b1 = b; a1 = b1; a1 = b1; }} Inferring Owners of Local Variables Solve constraints x3 = this x4 = oc1 x5 = thisThread x1 = x3 x2 = x5 Only equality constraints between ownersOnly equality constraints between owners Takes almost linear time to solveTakes almost linear time to solve

96 Reducing Programming Overhead Type inference for method local variables Type inference for method local variables Default types for method signatures & fields Default types for method signatures & fields User defined defaults as well User defined defaults as well Significantly reduces programming overhead Significantly reduces programming overhead Approach supports separate compilation Approach supports separate compilation

97 Experience

98 Multithreaded Server Programs Program # Lines of code # Lines annotated SMTP Server (Apache) 210546 POP3 Mail Server (Apache) 136431 Discrete Event Simulator (ETH Zurich) 523 52315 HTTP Server 563 56326 Chat Server 308 30822 Stock Quote Server 242 24212 Game Server 87 8711 Database Server 302 30210

99 Java Libraries Program # Lines of code # Lines annotated java.util.Hashtable101153 java.util.HashMap 852 85246 java.util.Vector 992 99235 java.util.ArrayList 533 53318 java.io.PrintStream 568 56814 java.io.FilterOutputStream 148 148 5 java.io.BufferedWriter 253 253 9 java.io.OutputStreamWriter 266 26611

100 Java Libraries Java has two classes for resizable arrays Java has two classes for resizable arrays  java.util.Vector  Self synchronized, do not create races  Always incur synchronization overhead  java.util.ArrayList  No unnecessary synchronization overhead  Could be used unsafely to create races We provide generic resizable arrays We provide generic resizable arrays  Safe, but no unnecessary overhead Programs can be both reliable and efficient Programs can be both reliable and efficient

101 Prevent data races and deadlocks Prevent data races and deadlocks  Boyapati, Rinard ( OOPSLA ’01 )  Boyapati, Lee, Rinard ( OOPSLA ’02 ) Prevent representation exposure Prevent representation exposure  Boyapati, Liskov, Shrira ( POPL ’03 ) Enable safe region-based memory management Enable safe region-based memory management  Boyapati, Salcianu, Beebee, Rinard ( PLDI ’03 ) Enable safe upgrades in persistent object stores Enable safe upgrades in persistent object stores  Boyapati, Liskov, Shrira, Moh, Richman ( OOPSLA ’03 ) Ownership Types

102 Goal is local reasoning about correctness Goal is local reasoning about correctness  Prove a class meets its specification, using specifications but not code of other classes Crucial when dealing with large programs Crucial when dealing with large programs Requires no interference from outside Requires no interference from outside  Internal sub-objects must be encapsulated Preventing Representation Exposure

103 Say Stack s is implemented with linked list Say Stack s is implemented with linked list Outside objects must not access list nodes Outside objects must not access list nodes Preventing Representation Exposure s o ~ ~

104 Say Stack s is implemented with linked list Say Stack s is implemented with linked list Outside objects must not access list nodes Outside objects must not access list nodes Preventing Representation Exposure Program can declare s owns list nodes Program can declare s owns list nodes System ensures list is encapsulated in s System ensures list is encapsulated in s s o ~ ~

105 class TStack { TNode head; TNode head; void push(T value) {…} void push(T value) {…} T pop() {…} T pop() {…}} class TNode { TNode next; TNode next; T value; T value; …} class T {…} value next head value next value next … … … TStack TNode T Preventing Representation Exposure

106 class TStack  stackOwner, TOwner  { TNode  this, TOwner  head; TNode  this, TOwner  head; …} class TNode  nodeOwner, TOwner  { TNode  nodeOwner, TOwner  next; TNode  nodeOwner, TOwner  next; T  TOwner  value; T  TOwner  value; …} TStack TNode T Preventing Representation Exposure

107 TNode objects are encapsulated in TStack object class TStack  stackOwner, TOwner  { TNode  this, TOwner  head; TNode  this, TOwner  head; …} class TNode  nodeOwner, TOwner  { TNode  nodeOwner, TOwner  next; TNode  nodeOwner, TOwner  next; T  TOwner  value; T  TOwner  value; …} TStack TNode T Preventing Representation Exposure

108 thisThreadthisThread Thread2 objects Thread1 objects Potentially shared objects Preventing Representation Exposure

109 thisThreadthisThread Thread2 objects Thread1 objects Potentially shared objects Preventing Representation Exposure

110 class IntVector { int size () {…} … int size () {…} …} class IntStack { void push (int x) {…} … void push (int x) {…} …} void m (IntStack s, IntVector v) { int n = v.size(); s.push(3); assert( n == v.size() ); int n = v.size(); s.push(3); assert( n == v.size() );} Example of Local Reasoning Is the condition in the assert true?

111 class IntVector { int size () reads (this) {…} … int size () reads (this) {…} …} class IntStack { void push (int x) writes (this) {…} … void push (int x) writes (this) {…} …} void m (IntStack s, IntVector v) where !(v <= s) !(s <= v) { int n = v.size(); s.push(3); assert( n == v.size() ); int n = v.size(); s.push(3); assert( n == v.size() );} Example of Local Reasoning Is the condition in the assert true?

112 class IntVector { int size () reads (this) {…} … int size () reads (this) {…} …} class IntStack { void push (int x) writes (this) {…} … void push (int x) writes (this) {…} …} void m (IntStack s, IntVector v) where !(v <= s) !(s <= v) { int n = v.size(); s.push(3); assert( n == v.size() ); int n = v.size(); s.push(3); assert( n == v.size() );} Example of Local Reasoning size only reads v and its encapsulated objects push only writes s and its encapsulated objects

113 class IntVector { int size () reads (this) {…} … int size () reads (this) {…} …} class IntStack { void push (int x) writes (this) {…} … void push (int x) writes (this) {…} …} void m (IntStack s, IntVector v) where !(v <= s) !(s <= v) { int n = v.size(); s.push(3); assert( n == v.size() ); int n = v.size(); s.push(3); assert( n == v.size() );} Example of Local Reasoning s is not encapsulated in v, and v is not encapsulated in s

114 class IntVector { int size () reads (this) {…} … int size () reads (this) {…} …} class IntStack { void push (int x) writes (this) {…} … void push (int x) writes (this) {…} …} void m (IntStack s, IntVector v) where !(v <= s) !(s <= v) { int n = v.size(); s.push(3); assert( n == v.size() ); int n = v.size(); s.push(3); assert( n == v.size() );} Example of Local Reasoning So size and push cannot interfere So the condition in the assert must be true

115 Prevent data races and deadlocks Prevent data races and deadlocks  Boyapati, Rinard ( OOPSLA ’01 )  Boyapati, Lee, Rinard ( OOPSLA ’02 ) Prevent representation exposure Prevent representation exposure  Boyapati, Liskov, Shrira ( POPL ’03 ) Enable safe region-based memory management Enable safe region-based memory management  Boyapati, Salcianu, Beebee, Rinard ( PLDI ’03 ) Enable safe upgrades in persistent object stores Enable safe upgrades in persistent object stores  Boyapati, Liskov, Shrira, Moh, Richman ( OOPSLA ’03 ) Ownership Types

116 Related Work

117 Static tools for preventing races and deadlocks Static tools for preventing races and deadlocks  Korty ( USENIX ’89 )  Sterling ( USENIX ’93 )  Detlefs, Leino, Nelson, Saxe ( SRC ’98 )  Engler, Chen, Hallem, Chou, Chelf ( SOSP ’01 ) Dynamic tools for preventing races and deadlocks Dynamic tools for preventing races and deadlocks  Steele ( POPL ’90 )  Dinning, Schonberg ( PPoPP ’90 )  Savage, Burrows, Nelson, Sobalvarro, Anderson ( SOSP ’97 )  Cheng, Feng, Leiserson, Randall, Stark ( SPAA ‘98 )  Praun, Gross ( OOPSLA ’01 )  Choi, Lee, Loginov, O’Callahan, Sarkar, Sridharan ( PLDI ’02 ) Related Work Useful but unsound

118 Related Work Types for preventing data races Types for preventing data races  Flanagan, Freund ( PLDI ’00 )  Bacon, Strom, Tarafdar ( OOPSLA ’00 )

119 Related Work Types for preventing data races Types for preventing data races  Flanagan, Freund ( PLDI ’00 )  Bacon, Strom, Tarafdar ( OOPSLA ’00 ) Types for preventing representation exposure Types for preventing representation exposure  Clarke, Potter, Noble ( OOPSLA ’98 ), ( ECOOP ’01 )  Clarke, Drossopoulou ( OOPSLA ’02 )  Aldrich, Kostadinov, Chambers ( OOPSLA ’02 )

120 Related Work Types for preventing data races Types for preventing data races  Flanagan, Freund ( PLDI ’00 )  Bacon, Strom, Tarafdar ( OOPSLA ’00 ) Types for preventing representation exposure Types for preventing representation exposure  Clarke, Potter, Noble ( OOPSLA ’98 ), ( ECOOP ’01 )  Clarke, Drossopoulou ( OOPSLA ’02 )  Aldrich, Kostadinov, Chambers ( OOPSLA ’02 ) Types for region-based memory management Types for region-based memory management  Tofte, Talpin ( POPL ’94 )  Christiansen, Henglein, Niss, Velschow ( DIKU ’98 )  Crary, Walker, Morrisett ( POPL ’99 )  Grossman, Morrisett, Jim, Hicks, Wang, Cheney ( PLDI ’02 )

121 Related Work Our work unifies these areas Types for preventing data races Types for preventing data races  Flanagan, Freund ( PLDI ’00 )  Bacon, Strom, Tarafdar ( OOPSLA ’00 ) Types for preventing representation exposure Types for preventing representation exposure  Clarke, Potter, Noble ( OOPSLA ’98 ), ( ECOOP ’01 )  Clarke, Drossopoulou ( OOPSLA ’02 )  Aldrich, Kostadinov, Chambers ( OOPSLA ’02 ) Types for region-based memory management Types for region-based memory management  Tofte, Talpin ( POPL ’94 )  Christiansen, Henglein, Niss, Velschow ( DIKU ’98 )  Crary, Walker, Morrisett ( POPL ’99 )  Grossman, Morrisett, Jim, Hicks, Wang, Cheney ( PLDI ’02 )

122 Conclusions Ownership types for object-oriented programs Statically prevent several classes of errors Statically prevent several classes of errors  Prevent data races and deadlocks  Prevent representation exposure  Enable region-based memory management  Enable upgrades in persistent object stores Provide documentation that lives with code Provide documentation that lives with code Require little programming overhead Require little programming overhead Promising way to make programs reliable Promising way to make programs reliable

123 Chandrasekhar Boyapati Laboratory for Computer Science Massachusetts Institute of Technology Ownership Types for Safe Programming


Download ppt "Chandrasekhar Boyapati Laboratory for Computer Science Massachusetts Institute of Technology Ownership Types for Safe Programming."

Similar presentations


Ads by Google