380 likes | 501 Views
Observations on the Assured Evolution of Concurrent Java Programs. Aaron Greenhouse, CMU SEI T. J. Halloran, CMU SCS William L. Scherlis, CMU SCS. Example: java.util.logging.Logger. public class Logger { ... private Filter filter; /** ...
E N D
Observations onthe Assured Evolution ofConcurrent Java Programs Aaron Greenhouse, CMU SEI T. J. Halloran, CMU SCS William L. Scherlis, CMU SCS
Example: java.util.logging.Logger public class Logger { ... private Filter filter; /** ... * @param newFilter a filter object (may be null) */ public void setFilter(Filter newFilter) ... { if (!anonymous) manager.checkAccess(); filter = newFilter; } public void log(LogRecord record) { ... synchronized (this) { if (filter != null && !filter.isLoggable(record)) return; } ... } ... } Aaron Greenhouse, CMU SEI
Example: java.util.logging.Logger public class Logger { ... private Filter filter; /** ... * @param newFilter a filter object (may be null) */ public void setFilter(Filter newFilter) ... { if (!anonymous) manager.checkAccess(); filter = newFilter; } public void log(LogRecord record) { ... synchronized (this) { if (filter != null && !filter.isLoggable(record)) return; } ... } ... } Aaron Greenhouse, CMU SEI
Example: java.util.logging.Logger public class Logger { ... private Filter filter; /** ... * @param newFilter a filter object (may be null) */ public void setFilter(Filter newFilter) ... { if (!anonymous) manager.checkAccess(); filter = newFilter; } public void log(LogRecord record) { ... synchronized (this) { if (filter != null && !filter.isLoggable(record)) return; } ... } ... } Aaron Greenhouse, CMU SEI
Example: java.util.logging.Logger /** @lock LogLock is this protects filter */public class Logger { ... private Filter filter; /** ... * @param newFilter a filter object (may be null) */ public synchronized void setFilter(Filter newFilter)…{ if (!anonymous) manager.checkAccess(); filter = newFilter; } public void log(LogRecord record) { ... synchronized (this) { if (filter != null && !filter.isLoggable(record)) return; } ... } ... } Aaron Greenhouse, CMU SEI
Concurrent Programming is Hard • Reasoning about concurrent programs • What is the design intent? • Is code consistent with the design intent? • Need answers to • Assure safety of program • Evolve program Aaron Greenhouse, CMU SEI
What is the Design Intent? Programmers ask • What data is shared by multiple threads? • What locks are used to protect it? • Whose responsibility is it to acquire the lock? • Is this delegate object owned by its referring object? Aaron Greenhouse, CMU SEI
What is the Design Intent? Programmers often use conventions • E.g., Objects protects themselves But intent can be more complex: • Lock also protects a delegate object • E.g., queue protects its underlying array • Certain methods expect caller to acquire lock • E.g., “transactional” call patterns • Call isEmpty() before getHead() • Not all fields require protection • E.g., single-threaded usage à la AWT Aaron Greenhouse, CMU SEI
Where is the Design Intent? Recovering design intent is hard: • Intent has non-local manifestations: • Distributed across method implementations • Distributed across class and its clients • No way to explicitly denote intent • Not part of language Aaron Greenhouse, CMU SEI
Is Code Consistent with Intent? • Infeasible to test: Non-determinism • Difficult to inspect: Non-locality • Traditional verification tools too painful • High “expression cost” • Uncertain scalability • Whole-program analysis • Where’s the whole program? • Composable static analysis + Models… Aaron Greenhouse, CMU SEI
Models and attributes “Mechanical” properties(non-functional) “this lock protects these fields” “single-threaded constructor” “unaliased field” Models provide cut points Locking responsibility Effects Aliasing Consequences Steps towards scalability Composability via cut points Incrementality Models Analysis Program components Reduced expressiveness No representation invariants Tension: Power–Scalability Some analyses almost global Composable Analysis + Models Aaron Greenhouse, CMU SEI
What’s Next… • Annotations and the Fluid Analysis Tool • Case study results • Refactorings for concurrency Aaron Greenhouse, CMU SEI
The Fluid Eclipse Plug-in Eclipse IDE Fluid Tool View Aaron Greenhouse, CMU SEI
The Fluid Eclipse Plug-in Aaron Greenhouse, CMU SEI
Reporting Code–Model Consistency Tool assures annotated models No annotations no assurance Three classes of results • Code–model consistency • Code–model inconsistency • Informative — Request for annotation Aaron Greenhouse, CMU SEI
Code–Model Inconsistency • Code is wrong — bug in the program • E.g., missing lock acquisition • Model is incomplete — bug in model • E.g., need to add more annotations • Model is incorrect — bug in model • E.g., wrong lock associated with shared state • Analysis is conservative — limitation of tool • E.g., analysis looses track of lock • E.g., memory model trickery Aaron Greenhouse, CMU SEI
Fluid Tool: Prior Work Annotation, analysis, and tool publications • ICSE ’98 • ECOOP ’99 • Software—Practice and Experience ’01 • ICSE ’02 • PASTE ’02 • Greenhouse Dissertation ‘03 • OOPSLA ’03 eclipse Tech eXchange http://www.fluid.cs.cmu.edu/ Aaron Greenhouse, CMU SEI
Example: BoundedFIFO Circular queue used for inter-thread communication in Log4j. /**@lock BufLock is this protects Instance*/ public class BoundedFIFO { … LoggingEvent[] buf; int numElts = 0, first = 0, next = 0, size; public BoundedFIFO(int size) { … } /**@requiresLock BufLock*/ public LoggingEvent get() { … } /**@requiresLock BufLock*/ public void put(LoggingEvent o) { … } // etc. } Aaron Greenhouse, CMU SEI
Example: BoundedFIFO Circular queue used for inter-thread communication in Log4j. /**@lock BufLock is this protects Instance*/ public class BoundedFIFO { … LoggingEvent[] buf; int numElts = 0, first = 0, next = 0, size; public BoundedFIFO(int size) { … } /**@requiresLock BufLock*/ public LoggingEvent get() { … } /**@requiresLock BufLock*/ public void put(LoggingEvent o) { … } // etc. } Instances use themselves as the lock Aaron Greenhouse, CMU SEI
Example: BoundedFIFO Circular queue used for inter-thread communication in Log4j. /**@lock BufLock is this protects Instance*/ public class BoundedFIFO { … LoggingEvent[] buf; int numElts = 0, first = 0, next = 0, size; public BoundedFIFO(int size) { … } /**@requiresLock BufLock*/ public LoggingEvent get() { … } /**@requiresLock BufLock*/ public void put(LoggingEvent o) { … } // etc. } Clients must acquire lock before calling method Aaron Greenhouse, CMU SEI
BoundedFIFO: Bad Callsite Aaron Greenhouse, CMU SEI
BoundedFIFO: Owned Array (1) Aaron Greenhouse, CMU SEI
BoundedFIFO: Owned Array (2) /**@lock BufLock is this protects Instance*/ public class BoundedFIFO { …/** @unshared * @aggregate [] into Instance */ LoggingEvent[] buf; // etc. } Aaron Greenhouse, CMU SEI
BoundedFIFO: Constructor (1) Aaron Greenhouse, CMU SEI
BoundedFIFO: Constructor (2) /**@lock BufLock is this protects Instance*/ public class BoundedFIFO { …/** @singleThreaded * @borrowed this */ public BoundedFIFO(int maxSize) { … } // etc. } Aaron Greenhouse, CMU SEI
BoundedFIFO: Assured Aaron Greenhouse, CMU SEI
Case Study: Util.Concurrent Aaron Greenhouse, CMU SEI
Case Studies Aaron Greenhouse, CMU SEI
Refactoring • Preconditions ensure transformation safety • [ How we got into analysis ] • Safe refactoring needs • Explicit models • Analysis of consistency • Refactorings that transform both code and model Aaron Greenhouse, CMU SEI
Split Lock: Before Start with an assuredlocking model /** @lock RectLock is this protects Instance */ public class Rectangle { private int x, y; private int w, h; public synchronized void move(int newX, int newY) { x = newX; y = newY; } public synchronized void resize(int newW, int newH) { w = newW; h = newH; } public synchronized void reconfigure( int newX, int newY, int newW, int newH) { x = newX; y = newY; w = newW; h = newH; } // etc. } {x,y} {w,h} {x,y, w,h} If move() and resize() are invoked more frequently than reconfigure(), splitting the lock will allow more aggressive concurrency within the program. Aaron Greenhouse, CMU SEI
Split Lock: After /** @region public Location * @lock LocationLock is this protects Location * @region public Dimension * @lock DimLock is dimLock protects Dimension */ public class Rectangle { private final Object dimLock = new Object(); /** @mapInto Location */ private int x, y; /** @mapInto Dimension */ private int w, h; public void move(int newX, int newY) { synchronized (this) { x = newX; y = newY; } } public void resize(int newW, int newH) { synchronized (dimLock) { w = newW; h = newH; } } public void reconfigure( int newX, int newY, int newW, int newH) { synchronized (this) { synchronized (dimLock) { x = newX; y = newY; w = newW; h = newH; } } } // etc. } Aaron Greenhouse, CMU SEI
Split Lock Caveats • Subclasses • Lock order to prevent deadlock • Updating @requiresLock annotations • Allowable interleavings Aaron Greenhouse, CMU SEI
Refactoring: Related Work • [Lea 2000] describes lock splitting as a design concept • Compiler optimizations that modify the scope of critical sections • Via escape analysis • [Blanchet ’99], [Bogda & Hoelzle ’99], [Choi, et al. ’99], [Whaley & Rinard ’99] • [Plevyak, Zhang & Chien ’95] • In automatically parallelized programs • [Diniz & Rinard ’96], [Diniz & Rinard ’97] Aaron Greenhouse, CMU SEI
Concurrency is difficult Defies testing and inspection Non-locality non-determinism Complex code patterns Driven by performance Reverse engineering is painful Capture results as models/annotations How can we be principled? Avoid concurrency Capture models Use refactoring Introduce complexity in a systematic manner Consistency of code and model Bad news Finds bugs Good news Assures absence of useful categories of bugs Enables safe refactoring Achieving success in practice Scalability Incrementality Late adoption Low barrier to entry Conclusion http://www.fluid.cs.cmu.edu Aaron Greenhouse, CMU SEI
Extra Slides Here Aaron Greenhouse, CMU SEI
Split Lock (1) Decreases lock granularity by moving “down” the region hierarchy. Inputs • Lock annotation @lock L is M protects R • Lock names { Li } and references { Mi } to use as the locks for subregions { Rj} of R Aaron Greenhouse, CMU SEI
Split Lock (2) • For each synchronized block that acquires L • Determine the regions { Rj } used within • Replace with nested synchronized blocks that acquire { Lj } • Remove original @lock annotation • Insert new annotations@lock Li is Mi protects Ri Aaron Greenhouse, CMU SEI
Split Lock: Before /** @lock RectLock is this protects Instance * @region public Location * @region public Dimension */ public class Rectangle { /** @mapInto Location */ private int x, y; /** @mapInto Dimension */ private int w, h; public synchronized void move(int newX, int newY) { x = newX; y = newY; } public synchronized void resize(int newW, int newH) { w = newW; h = newH; } public synchronized void reconfigure( int newX, int newY, int newW, int newH) { x = newX; y = newY; w = newW; h = newH; } // etc. } Aaron Greenhouse, CMU SEI