A Parameterized Type System for Race-Free Java Programs

Similar documents
Safe Runtime Downcasts With Ownership Types

Ownership Types for Safe Programming: Preventing Data Races and Deadlocks

A Fundamental Permission Interpretation for Ownership Types

CMSC 132: Object-Oriented Programming II

HEAT: An Integrated Static and Dynamic Approach for Thread Escape Analysis

Extending the Java Language for the Prevention of Data Races. Robert Honway Lee

Ownership Types for Object Encapsulation

Generics in Java and Beyond

Java Memory Model. Jian Cao. Department of Electrical and Computer Engineering Rice University. Sep 22, 2016

Reasoning about Object Structures Using Ownership

Safe Instantiation in Generic Java

Using Block-local Atomicity to Detect Stale-value Concurrency Errors

CalFuzzer: An Extensible Active Testing Framework for Concurrent Programs Pallavi Joshi 1, Mayur Naik 2, Chang-Seo Park 1, and Koushik Sen 1

Ownership Types. Fabian Muehlboeck. April 11, 2012

Safe Concurrency for Aggregate Objects with Invariants

Verification of Multithreaded Object-Oriented Programs with Invariants

CSE 332: Data Structures & Parallelism Lecture 18: Race Conditions & Deadlock. Ruth Anderson Autumn 2017

+ Today. Lecture 26: Concurrency 3/31/14. n Reading. n Objectives. n Announcements. n P&C Section 7. n Race conditions.

Programmazione di sistemi multicore

Field Analysis. Last time Exploit encapsulation to improve memory system performance

Detecting Data Races in Multi-Threaded Programs

On the Structure of Sharing in Open Concurrent Java Programs

Computation Abstractions. Processes vs. Threads. So, What Is a Thread? CMSC 433 Programming Language Technologies and Paradigms Spring 2007

CSE332: Data Abstractions Lecture 19: Mutual Exclusion and Locking

MultiJav: A Distributed Shared Memory System Based on Multiple Java Virtual Machines. MultiJav: Introduction

3/25/14. Lecture 25: Concurrency. + Today. n Reading. n P&C Section 6. n Objectives. n Concurrency

Typed Assembly Language for Implementing OS Kernels in SMP/Multi-Core Environments with Interrupts

Synchronization SPL/2010 SPL/20 1

A Case for System Support for Concurrency Exceptions

11/19/2013. Imperative programs

Atomicity via Source-to-Source Translation

A Type and Effect System for Atomicity

CSE332: Data Abstractions Lecture 25: Deadlocks and Additional Concurrency Issues. Tyler Robison Summer 2010

Correctness of Partial Escape Analysis for Multithreading Optimization

Hybrid Static-Dynamic Analysis for Statically Bounded Region Serializability

Chimera: Hybrid Program Analysis for Determinism

Ownership Types for Object Synchronisation

A Type System for Safe Region-Based Memory Management in Real-Time Java

Cooperative Concurrency for a Multicore World

Semantic Atomicity for Multithreaded Programs!

Motivation & examples Threads, shared memory, & synchronization

Fully Automatic and Precise Detection of Thread Safety Violations

Automated Type-Based Analysis of Data Races and Atomicity

ASaP: Annotations for Safe Parallelism in Clang. Alexandros Tzannes, Vikram Adve, Michael Han, Richard Latham

Thread-Local. Lecture 27: Concurrency 3. Dealing with the Rest. Immutable. Whenever possible, don t share resources

Eraser : A dynamic data race detector for multithreaded programs By Stefan Savage et al. Krish 9/1/2011

The Java Memory Model

A Sophomoric Introduction to Shared-Memory Parallelism and Concurrency Lecture 5 Programming with Locks and Critical Sections

Toward Efficient Strong Memory Model Support for the Java Platform via Hybrid Synchronization

CSE 374 Programming Concepts & Tools

More BSOD Embarrassments. Moore s Law. Programming With Threads. Moore s Law is Over. Analysis of Concurrent Software. Types for Race Freedom

DoubleChecker: Efficient Sound and Precise Atomicity Checking

Software Analysis Techniques for Detecting Data Race

7/6/2015. Motivation & examples Threads, shared memory, & synchronization. Imperative programs

Software Transactions: A Programming-Languages Perspective

CS 2112 Lecture 20 Synchronization 5 April 2012 Lecturer: Andrew Myers

FastTrack: Efficient and Precise Dynamic Race Detection (+ identifying destructive races)

Optimized Run-Time Race Detection And Atomicity Checking Using Partial Discovered Types

Evaluating Research on Software Design and Productivity

Concurrency CSCI 201 Principles of Software Development

The New Java Technology Memory Model

Atomicity for Reliable Concurrent Software. Race Conditions. Motivations for Atomicity. Race Conditions. Race Conditions. 1. Beyond Race Conditions

CSE332: Data Abstractions Lecture 23: Programming with Locks and Critical Sections. Tyler Robison Summer 2010

Advanced Threads & Monitor-Style Programming 1/24

Processor speed. Concurrency Structure and Interpretation of Computer Programs. Multiple processors. Processor speed. Mike Phillips <mpp>

Testing Concurrent Java Programs Using Randomized Scheduling

Introducing Shared-Memory Concurrency

CMSC 433 Programming Language Technologies and Paradigms. Concurrency

Parallelism Marco Serafini

C. Flanagan Dynamic Analysis for Atomicity 1

Foundations of the C++ Concurrency Memory Model

A Lightweight and Portable Approach to Making Concurrent Failures Reproducible

Multi-threading in Java. Jeff HUANG

Active Testing for Concurrent Programs

Race Directed Random Testing of Concurrent Programs

Practical Mostly-Static Information Flow Control. Andrew Myers MIT Lab for Computer Science

Towards a Resilient Operating System for Wireless Sensor Networks

Safety SPL/2010 SPL/20 1

Detecting Read-Only Methods in Java

Active Testing for Concurrent Programs

Threads Cannot Be Implemented As a Library

Data-flow Analysis for Interruptdriven Microcontroller Software

Synchronization

Concurrency Control with Java and Relational Databases

AUTOMATICALLY INFERRING STRUCTURE CORRELATED VARIABLE SET FOR CONCURRENT ATOMICITY SAFETY

Program Graph. Lecture 25: Parallelism & Concurrency. Performance. What does it mean?

Programming Language Memory Models: What do Shared Variables Mean?

Applications of Ownership Types. Bertinoro Summer School Ownership Types page 1 out of 23

Audience. Revising the Java Thread/Memory Model. Java Thread Specification. Revising the Thread Spec. Proposed Changes. When s the JSR?

A Modular Checker for Multithreaded Programs

Practical Affine Types and Typestate-Oriented Programming

Eventrons: A Safe Programming Construct for High-Frequency Hard Real-Time Applications

Object-Specific Redundancy Elimination Techniques

Programmazione di sistemi multicore

Development of Technique for Healing Data Races based on Software Transactional Memory

Part II: Atomicity for Software Model Checking. Analysis of concurrent programs is difficult (1) Transaction. The theory of movers (Lipton 75)

Java Bibliography. [3] Ole Agesen, Stephen N. Freund, and John C. Mitchell. Adding Type Parameterization

Data Races and Locks

Ownership, Encapsulation and the Disjointness of Type and Effect. Dave Clarke Utrecht University Sophia Drossopoulou Imperial College, London

CSE 451: Operating Systems Winter Lecture 7 Synchronization. Steve Gribble. Synchronization. Threads cooperate in multithreaded programs

Transcription:

A Parameterized Type System for Race-Free Java Programs Chandrasekhar Boyapati Martin Rinard Laboratory for Computer Science Massachusetts Institute of Technology {chandra, rinard@lcs.mit.edu

Data races in multithreaded programs Two threads concurrently access same data At least one access is a write No synchronization to separate accesses Thread 1: Thread 2: x = x + 1; x = x + 2;

Why data races are a problem Some correct programs contain data races But most races are programming errors Code intended to execute atomically Synchronization omitted by mistake Consequences can be severe Non-deterministic, timing-dependent bugs Difficult to detect, reproduce, eliminate

Avoiding data races Thread 1: Thread 2: x = x + 1; x = x + 2;

Avoiding data races Thread 1: lock(l); x = x + 1; unlock(l); Thread 2: lock(l); x = x + 2; unlock(l); Associate a lock with every shared mutable data Acquire lock before data access Release lock after data access

Avoiding data races Thread 1: lock(l); x = x + 1; unlock(l); Thread 2: lock(l); x = x + 2; unlock(l); Problem: Locking is not enforced! Inadvertent programming errors

Our solution A static type system for OO programs Well-typed programs are free of races

Our solution A static type system for OO programs Well-typed programs are free of races Programmers specify How each object is protected from races In types of variables pointing to objects Type checkers statically verify That objects are used only as specified

Protection mechanism of an object Specifies the lock protecting the object, or Specifies that object needs no locks b cos The object is immutable, or The object is not shared, or There is a unique pointer to the object

Types are proofs Java + Extra types Type checker Translator (Removes extra types) Java Compiler bytecodes JVM

Outline Motivation Type system Experience Related work Conclusions

Race-free Account program class Account { int balance = 0; int deposit(int x) { this.balance += x; Account a1 = new Account; fork (a1) { synchronized (a1) in { a1.deposit(10); ; fork (a1) { synchronized (a1) in { a1.deposit(10); ; Account a2 = new Account; a2.deposit(10);

Race-free Account program class Account { int balance = 0; int deposit(int x) { this.balance += x; Java: Thread t; t.start(); Concurrent Java: fork (t) { t.start(); Account a1 = new Account; fork (a1) { synchronized (a1) in { a1.deposit(10); ; fork (a1) { synchronized (a1) in { a1.deposit(10); ; Account a2 = new Account; a2.deposit(10);

Race-free Account program class Account { int balance = 0; int deposit(int x) { this.balance += x; Account a1 = new Account; fork (a1) { synchronized (a1) in { a1.deposit(10); ; fork (a1) { synchronized (a1) in { a1.deposit(10); ; Account a2 = new Account; a2.deposit(10);

Statically verifiable race-free program class Account thisowner { int balance = 0; int deposit(int x) requires (this) { this.balance += x; final Account self a1 = new Account self ; fork (a1) { synchronized (a1) in { a1.deposit(10); ; fork (a1) { synchronized (a1) in { a1.deposit(10); ; Account thisthread a2 = new Account thisthread ; a2.deposit(10);

Statically verifiable race-free program class Account thisowner { thisowner protects the Account int balance = 0; int deposit(int x) requires (this) { this.balance += x; final Account self a1 = new Account self ; fork (a1) { synchronized (a1) in { a1.deposit(10); ; fork (a1) { synchronized (a1) in { a1.deposit(10); ; Account thisthread a2 = new Account thisthread ; a2.deposit(10);

Statically verifiable race-free program class Account thisowner { int balance = 0; int deposit(int x) requires (this) { this.balance += x; a1 is protected by its lock a2 is thread-local final Account self a1 = new Account self ; fork (a1) { synchronized (a1) in { a1.deposit(10); ; fork (a1) { synchronized (a1) in { a1.deposit(10); ; Account thisthread a2 = new Account thisthread ; a2.deposit(10);

Statically verifiable race-free program class Account thisowner { deposit requires lock on this int balance = 0; int deposit(int x) requires (this) { this.balance += x; final Account self a1 = new Account self ; fork (a1) { synchronized (a1) in { a1.deposit(10); ; fork (a1) { synchronized (a1) in { a1.deposit(10); ; Account thisthread a2 = new Account thisthread ; a2.deposit(10);

Statically verifiable race-free program class Account thisowner { a1 is locked before calling deposit int balance = 0; a2 need not be locked int deposit(int x) requires (this) { this.balance += x; final Account self a1 = new Account self ; fork (a1) { synchronized (a1) in { a1.deposit(10); ; fork (a1) { synchronized (a1) in { a1.deposit(10); ; Account thisthread a2 = new Account thisthread ; a2.deposit(10);

Type system Basic type system: Locks, thread-local objects Object ownership Type system Type inference Extensions: Unique pointers, read-only objects

Object ownership Every object has an owner An object can be owned by Itself Another object Special per-thread owner called thisthread thisthread thisthread Thread1 objects Thread2 objects Potentially shared objects

Ownership properties Owner of an object does not change over time Ownership relation forms a forest of rooted trees Roots can have self loops thisthread thisthread Thread1 objects Thread2 objects Potentially shared objects

Ownership properties Every object is protected by its root owner To gain exclusive access to an object, it is Necessary and sufficient to lock its root owner A thread implicitly holds the lock on its thisthread thisthread thisthread Thread1 objects Thread2 objects Potentially shared objects

Object ownership Basic type system Type system Type inference

TStack program class TStack { TNode head; TStack head void push(t value) { T pop() { class TNode { TNode next; T value; TNode s T s next value next value next value class T {

TStack program class TStack thisowner, TOwner { TNode this, TOwner head; class TNode thisowner, TOwner { TNode thisowner, TOwner next; T TOwner value; TStack thisthread, thisthread s1; TStack thisthread, self s2; TStack TNode s T s

Parameterizing classes class TStack thisowner, TOwner { TNode this, TOwner head; class TNode thisowner, TOwner { TNode thisowner, TOwner next; T TOwner value; TStack thisthread, thisthread s1; TStack thisthread, self s2; TStack TNode s T s Classes are parameterized with one or more owners First owner owns the this object

Instantiating classes class TStack thisowner, TOwner { TNode this, TOwner head; class TNode thisowner, TOwner { TNode thisowner, TOwner next; T TOwner value; TStack thisthread, thisthread s1; TStack thisthread, self s2; TStack TNode s T s Classes can be instantiated with final expressions E.g., with this

Instantiating classes class TStack thisowner, TOwner { TNode this, TOwner head; class TNode thisowner, TOwner { TNode thisowner, TOwner next; T TOwner value; TStack thisthread, thisthread s1; TStack thisthread, self s2; TStack TNode s T s Classes can be instantiated with formal parameters E.g., with thisowner or TOwner

Instantiating classes class TStack thisowner, TOwner { TNode this, TOwner head; class TNode thisowner, TOwner { TNode thisowner, TOwner next; T TOwner value; TStack thisthread, thisthread s1; TStack thisthread, self s2; TStack TNode s T s thisthread Classes can be instantiated with thisthread

Instantiating classes class TStack thisowner, TOwner { TNode this, TOwner head; class TNode thisowner, TOwner { TNode thisowner, TOwner next; T TOwner value; TStack thisthread, thisthread s1; TStack thisthread, self s2; TStack TNode s T s thisthread Classes can be instantiated with self

Requires clauses class TStack thisowner, TOwner { Methods can require threads TNode this, TOwner head; to have locks on root owners of objects T TOwner pop() requires (this) { if (head == null) return null; T TOwner value = head.value(); head = head.next(); return value; class TNode thisowner, TOwner { T TOwner value() requires (this) { TNode thisowner, TOwner next() requires (this) {

Type checking pop method class TStack thisowner, TOwner { TStack head TNode this, TOwner head; next T TOwner pop() requires (this) { TNode s value if (head == null) return null; T TOwner value = head.value(); T s head = head.next(); return value; class TNode thisowner, TOwner { T TOwner value() requires (this) { TNode thisowner, TOwner next() requires (this) { next value next value

Type checking pop method class TStack thisowner, TOwner { Locks held TNode this, TOwner head; thisthread, T TOwner pop() requires (this) { RootOwner(this) if (head == null) return null; T TOwner value = head.value(); head = head.next(); return value; class TNode thisowner, TOwner { T TOwner value() requires (this) { TNode thisowner, TOwner next() requires (this) {

Type checking pop method class TStack thisowner, TOwner { Locks held TNode this, TOwner head; thisthread, T TOwner pop() requires (this) { RootOwner(this) if (head == null) return null; T TOwner value = head.value(); head = head.next(); Locks required return value; RootOwner(this) class TNode thisowner, TOwner { T TOwner value() requires (this) { TNode thisowner, TOwner next() requires (this) {

Type checking pop method class TStack thisowner, TOwner { Locks held TNode this, TOwner head; thisthread, T TOwner pop() requires (this) { RootOwner(this) if (head == null) return null; T TOwner value = head.value(); head = head.next(); Locks required return value;? class TNode thisowner, TOwner { T TOwner value() requires (this) { TNode thisowner, TOwner next() requires (this) {

Type checking pop method class TStack thisowner, TOwner { Locks held TNode this, TOwner head; thisthread, T TOwner pop() requires (this) { RootOwner(this) if (head == null) return null; T TOwner value = head.value(); head = head.next(); Locks required return value; RootOwner(head) class TNode thisowner, TOwner { T TOwner value() requires (this) { TNode thisowner, TOwner next() requires (this) {

Type checking pop method class TStack thisowner, TOwner { TNode this, TOwner head; T TOwner pop() requires (this) { if (head == null) return null; T TOwner value = head.value(); head = head.next(); return value; class TNode thisowner, TOwner { T TOwner value() requires (this) { TNode thisowner, TOwner next() requires (this) { Locks held thisthread, RootOwner(this) Locks required RootOwner(head) = RootOwner(this)

Type checking pop method class TStack thisowner, TOwner { TNode this, TOwner head; T TOwner pop() requires (this) { if (head == null) return null; T TOwner value = head.value(); head = head.next(); return value; class TNode thisowner, TOwner { T TOwner value() requires (this) { TNode thisowner, TOwner next() requires (this) { Locks held thisthread, RootOwner(this) Locks required RootOwner(this), RootOwner(head) = RootOwner(this)

Type checking pop method class TStack thisowner, TOwner { TNode this, TOwner head; T TOwner pop() requires (this) { if (head == null) return null; T TOwner value = head.value(); head = head.next(); return value; class TNode thisowner, TOwner { T TOwner value() requires (this) { TNode thisowner, TOwner next() requires (this) {

Type checking client code class TStack thisowner, TOwner { T TOwner pop() requires (this) { final TStack self, self s = ; fork (s) { synchronized (s) in { s.pop(); ;

Type checking client code class TStack thisowner, TOwner { T TOwner pop() requires (this) { Locks held thisthread, s final TStack self, self s = ; fork (s) { synchronized (s) in { s.pop(); ;

Type checking client code class TStack thisowner, TOwner { T TOwner pop() requires (this) { Locks held thisthread, s final TStack self, self s = ; fork (s) { synchronized (s) in { s.pop(); ; Locks required RootOwner(s) = s

Object ownership Basic type system Type system Type inference

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) { A a1; B b1; b1 = b; 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) { A x1, x2 a1; B x3, x4, x5 b1; b1 = b; a1 = b1; Augment unknown types with owners

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) { A x1, x2 a1; B x3, x4, x5 b1; b1 = b; a1 = b1; Gather constraints x3 = this x4 = oc1 x5 = thisthread

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) { A x1, x2 a1; B x3, x4, x5 b1; b1 = b; a1 = b1; Gather constraints x3 = this x4 = oc1 x5 = thisthread x1 = x3 x2 = x5

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) { A this, thisthread a1; B this, oc1, thisthread b1; b1 = b; a1 = b1; Solve constraints x3 = this x4 = oc1 x5 = thisthread x1 = x3 x2 = x5

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) { A this, thisthread a1; B this, oc1, thisthread b1; b1 = b; a1 = b1; Solve constraints x3 = this x4 = oc1 x5 = thisthread x1 = x3 x2 = x5 Only equality constraints between owners Takes almost linear time to solve

Default types To further reduce programming overhead Single threaded programs require almost no programming overhead

Outline Motivation Type system Experience Related work Conclusions

Multithreaded server programs Program http server chat server stock quote server game server phone (database) server Lines of code 563 308 242 87 302 Lines changed 26 21 12 10 10

Program Java libraries Lines of code Lines changed java.util.vector java.util.arraylist 992 533 35 18 java.io.printstream 568 14 java.io.filteroutputstream java.io.outputstream java.io.bufferedwriter java.io.outputstreamwriter java.io.writer 148 134 253 266 177 05 03 09 11 06

Java libraries 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 Safe, but no unnecessary overhead

Java libraries Java programs contain unnecessary locking Much analysis work to remove unnecessary locking Aldrich, Chambers, Sirer, Eggers (SAS 99) Whaley, Rinard (OOPSLA 99) Choi, Gupta, Serrano, Sreedhar, Midkiff (OOPSLA 99) Blanchet (OOPSLA 99) Bogda, Holzle (OOPSLA 99) Ruf (PLDI 00) Our implementation Avoids unnecessary locking Without sacrificing safety

Additional benefits of race-free types Data races expose the effects of Weak memory consistency models Standard compiler optimizations

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

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

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

Additional benefits of race-free types Data races expose effects of Weak memory consistency models Standard compiler optimizations Data races complicate program analysis Data races complicate human understanding Race-free languages Eliminate these issues Make multithreaded programming more tractable

Outline Motivation Type system Experience Related work Conclusions

Tools to detect races Static race detection systems Sterling (USENIX 93) Detlefs, Leino, Nelson, Saxe (SRC 98) Engler, Chen, Hallem, Chou, Chelf (SOSP 01) Dynamic race detection systems Steele (POPL 90) Dinning, Schonberg (PPoPP 90) Savage, Burrows, Nelson, Sobalvarro, Anderson (SOSP 97) Praun, Gross (OOPSLA 01)

Type systems to prevent races Race-free Java Flanagan and Freund (PLDI 00) Guava Bacon, Strom, Tarafdar (OOPSLA 00)

Other related type systems Ownership types Clarke, Potter, Noble (OOPSLA 98), (ECOOP 01) Region types Grossman, Morrisett, Jim, Hicks, Wang, Cheney (Cornell 01) Parameterized types for Java Myers, Bank, Liskov (POPL 97) Agesen, Freund, Mitchell (OOPSLA 97) Bracha, Odersky, Stoutamire, Wadler (OOPSLA 98) Cartwright, Steele (OOPSLA 98)

Conclusions Data races make programs hard to debug We presented race-free static type system Our type system is expressive Programs can be reliable and efficient

A Parameterized Type System for Race-Free Java Programs Chandrasekhar Boyapati Martin Rinard Laboratory for Computer Science Massachusetts Institute of Technology {chandra, rinard@lcs.mit.edu