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

Slides:



Advertisements
Similar presentations
1 Lecture 5 Towards a Verifying Compiler: Multithreading Wolfram Schulte Microsoft Research Formal Methods 2006 Race Conditions, Locks, Deadlocks, Invariants,
Advertisements

Chapter 22 Implementing lists: linked implementations.
Bounded Model Checking of Concurrent Data Types on Relaxed Memory Models: A Case Study Sebastian Burckhardt Rajeev Alur Milo M. K. Martin Department of.
Goldilocks: Efficiently Computing the Happens-Before Relation Using Locksets Tayfun Elmas 1, Shaz Qadeer 2, Serdar Tasiran 1 1 Koç University, İstanbul,
Verification of Multithreaded Object- Oriented Programs with Invariants Bart Jacobs, K. Rustan M. Leino, Wolfram Schulte.
A Randomized Dynamic Program Analysis for Detecting Real Deadlocks Koushik Sen CS 265.
A simple sequential reasoning approach for sound modular verification of mainstream multithreaded programs Wolfram Schulte & Bart Jacobs Microsoft Research.
D u k e S y s t e m s Time, clocks, and consistency and the JMM Jeff Chase Duke University.
1 Concurrency Specification. 2 Outline 4 Issues in concurrent systems 4 Programming language support for concurrency 4 Concurrency analysis - A specification.
Monitors Chapter 7. The semaphore is a low-level primitive because it is unstructured. If we were to build a large system using semaphores alone, the.
Lecture 8 CS203. Implementation of Data Structures 2 In the last couple of weeks, we have covered various data structures that are implemented in the.
Types for Atomicity in Multithreaded Software Shaz Qadeer Microsoft Research (Joint work with Cormac Flanagan)
A Parameterized Type System for Race-Free Java Programs Paper by Boyapti & Rinard, 2001 Christopher Dentel ETH, Concepts of Concurrent Computation, 2012.
Atomicity in Multi-Threaded Programs Prachi Tiwari University of California, Santa Cruz CMPS 203 Programming Languages, Fall 2004.
/ PSWLAB Atomizer: A Dynamic Atomicity Checker For Multithreaded Programs By Cormac Flanagan, Stephen N. Freund 24 th April, 2008 Hong,Shin.
CS 263 Course Project1 Survey: Type Systems for Race Detection and Atomicity Feng Zhou, 12/3/2003.
CS444/CS544 Operating Systems Synchronization 2/16/2006 Prof. Searleman
Threading Part 2 CS221 – 4/22/09. Where We Left Off Simple Threads Program: – Start a worker thread from the Main thread – Worker thread prints messages.
“THREADS CANNOT BE IMPLEMENTED AS A LIBRARY” HANS-J. BOEHM, HP LABS Presented by Seema Saijpaul CS-510.
1 Chapter 24 Lists Stacks and Queues. 2 Objectives F To design list with interface and abstract class (§24.2). F To design and implement a dynamic list.
C. FlanaganSAS’04: Type Inference Against Races1 Type Inference Against Races Cormac Flanagan UC Santa Cruz Stephen N. Freund Williams College.
Ownership Types for Safe Programming: Preventing Data Races and Deadlocks Chandrasekhar Boyapati Robert Lee Martin Rinard Laboratory for Computer Science.
Ownership Types for Object Encapsulation Barbara Liskov Chandrasekhar Boyapati Liuba Shrira Laboratory for Computer Science Massachusetts Institute of.
Programming Language Semantics Java Threads and Locks Informal Introduction The Java Specification Language Chapter 17.
A Type System for Preventing Data Races and Deadlocks in the Java Virtual Machine Language Pratibha Permandla Michael Roberson Chandrasekhar Boyapati University.
Laboratory for Computer Science Massachusetts Institute of Technology Ownership Types for Safe Region-Based Memory Management in Real-Time Java Chandrasekhar.
Software Reliability Methods Sorin Lerner. Software reliability methods: issues What are the issues?
Computer Architecture II 1 Computer architecture II Lecture 9.
Synchronization in Java Fawzi Emad Chau-Wen Tseng Department of Computer Science University of Maryland, College Park.
A Parameterized Type System for Race-Free Java Programs Chandrasekhar Boyapati Martin Rinard Laboratory for Computer Science Massachusetts Institute of.
1 Sharing Objects – Ch. 3 Visibility What is the source of the issue? Volatile Dekker’s algorithm Publication and Escape Thread Confinement Immutability.
0wn3rship Types John Whaley CS343 Stanford University May 19, 2004.
Ownership Types for Object Encapsulation Authors:Chandrasekhar Boyapati Barbara Liskov Liuba Shrira Presented by: Charles Lin Course: CMSC 631.
Semantics of Multithreaded Java Jeremy Manson and William Pugh Background Material Jack Newton University of Alberta
Memory Management for Real-Time Java Wes Beebee and Martin Rinard Laboratory for Computer Science Massachusetts Institute of Technology Supported by: DARPA.
University of Michigan Electrical Engineering and Computer Science 1 Practical Lock/Unlock Pairing for Concurrent Programs Hyoun Kyu Cho 1, Yin Wang 2,
C. FlanaganType Systems for Multithreaded Software1 Cormac Flanagan UC Santa Cruz Stephen N. Freund Williams College Shaz Qadeer Microsoft Research.
Chapters 7, 8, & 9 Quiz 3 Review 1. 2 Algorithms Algorithm A set of unambiguous instructions for solving a problem or subproblem in a finite amount of.
Eraser: A Dynamic Data Race Detector for Multithreaded Programs STEFAN SAVAGE, MICHAEL BURROWS, GREG NELSON, PATRICK SOBALVARRO, and THOMAS ANDERSON Ethan.
/ PSWLAB Type-Based Race Detection for J AVA by Cormac Flanagan, Stephen N. Freund 22 nd Feb 2008 presented by Hong,Shin Type-Based.
ROBERT BOCCHINO, ET AL. UNIVERSAL PARALLEL COMPUTING RESEARCH CENTER UNIVERSITY OF ILLINOIS A Type and Effect System for Deterministic Parallel Java *Based.
Colorama: Architectural Support for Data-Centric Synchronization Luis Ceze, Pablo Montesinos, Christoph von Praun, and Josep Torrellas, HPCA 2007 Shimin.
Aritra Sengupta, Swarnendu Biswas, Minjia Zhang, Michael D. Bond and Milind Kulkarni ASPLOS 2015, ISTANBUL, TURKEY Hybrid Static-Dynamic Analysis for Statically.
Sharing Objects  Synchronization  Atomicity  Specifying critical sections  Memory visibility  One thread’s modification seen by the other  Visibility.
Data races, informally [More formal definition to follow] “race condition” means two different things Data race: Two threads read/write, write/read, or.
November 2005Scott Stoller, Stony Brook University1 Detecting Potential Deadlocks with Static Analysis and Run-Time Monitoring Rahul Agarwal, Liqiang Wang,
SPL/2010 Synchronization 1. SPL/2010 Overview ● synchronization mechanisms in modern RTEs ● concurrency issues ● places where synchronization is needed.
Detecting and Eliminating Potential Violation of Sequential Consistency for concurrent C/C++ program Duan Yuelu, Feng Xiaobing, Pen-chung Yew.
Object-Oriented Programming Chapter Chapter
ICFEM 2002, Shanghai Reasoning about Hardware and Software Memory Models Abhik Roychoudhury School of Computing National University of Singapore.
CoreDet: A Compiler and Runtime System for Deterministic Multithreaded Execution Tom Bergan Owen Anderson, Joe Devietti, Luis Ceze, Dan Grossman To appear.
Pointer and Escape Analysis for Multithreaded Programs Alexandru Salcianu Martin Rinard Laboratory for Computer Science Massachusetts Institute of Technology.
/ PSWLAB Thread Modular Model Checking by Cormac Flanagan and Shaz Qadeer (published in Spin’03) Hong,Shin Thread Modular Model.
Aritra Sengupta, Man Cao, Michael D. Bond and Milind Kulkarni PPPJ 2015, Melbourne, Florida, USA Toward Efficient Strong Memory Model Support for the Java.
Specifying Multithreaded Java semantics for Program Verification Abhik Roychoudhury National University of Singapore (Joint work with Tulika Mitra)
Eraser: A dynamic Data Race Detector for Multithreaded Programs Stefan Savage, Michael Burrows, Greg Nelson, Patrick Sobalvarro, Thomas Anderson Presenter:
Using Escape Analysis in Dynamic Data Race Detection Emma Harrington `15 Williams College
Mergesort example: Merge as we return from recursive calls Merge Divide 1 element 829.
Sung-Dong Kim, Dept. of Computer Engineering, Hansung University Java - Introduction.
1 Towards Automated Verification Through Type Discovery Joint work with Rahul Agarwal Scott D. Stoller State University of New York at Stony Brook.
Healing Data Races On-The-Fly
Types for Programs and Proofs
Compositional Pointer and Escape Analysis for Java Programs
State your reasons or how to keep proofs while optimizing code
Concurrency Specification
Threads and Memory Models Hal Perkins Autumn 2011
Threads and Memory Models Hal Perkins Autumn 2009
Relaxed Consistency Finale
CSE 332: Concurrency and Locks
Object Encapsulation CSC 422 Dr. Spiegel.
Presentation transcript:

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

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

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

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

Preventing Data Races

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Object Ownership

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

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

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

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

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

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

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

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

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

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

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

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

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

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) {…} …}

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) {…} …}

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) {…} …}

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) {…} …}

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) {…} …}

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) {…} …}

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) {…} …}

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

Other Benefits of Race-free Types

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

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;

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

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

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 )

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

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

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

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

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

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…

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

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

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

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

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

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

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

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

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

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

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

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

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…

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

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

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

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

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

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

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

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)

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

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

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

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

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

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

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

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

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

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

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

Reducing Programming Overhead

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

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

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

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

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

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

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

Experience

Multithreaded Server Programs Program # Lines of code # Lines annotated SMTP Server (Apache) POP3 Mail Server (Apache) Discrete Event Simulator (ETH Zurich) HTTP Server Chat Server Stock Quote Server Game Server Database Server

Java Libraries Program # Lines of code # Lines annotated java.util.Hashtable java.util.HashMap java.util.Vector java.util.ArrayList java.io.PrintStream java.io.FilterOutputStream java.io.BufferedWriter java.io.OutputStreamWriter

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

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

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

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

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

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

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

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

thisThreadthisThread Thread2 objects Thread1 objects Potentially shared objects Preventing Representation Exposure

thisThreadthisThread Thread2 objects Thread1 objects Potentially shared objects Preventing Representation Exposure

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?

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?

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

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

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

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

Related Work

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

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

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 )

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 )

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 )

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

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