1 / 81

Static Deadlock Detection for Java Libraries

Static Deadlock Detection for Java Libraries. Amy Williams , William Thies, and Michael D. Ernst. Massachusetts Institute of Technology. Deadlock. Each deadlocked thread attempts to acquire a lock held by another thread Can halt entire program execution Difficult to expose during testing

arausch
Download Presentation

Static Deadlock Detection for Java Libraries

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Static Deadlock Detection for Java Libraries Amy Williams, William Thies, and Michael D. Ernst Massachusetts Institute of Technology

  2. Deadlock • Each deadlocked thread attempts to acquire a lock held by another thread • Can halt entire program execution • Difficult to expose during testing • Once exposed, difficult to replicate • Example: a StringBuffer a, b; Thread 1: a.append(b); locks a, b Thread 2: b.append(a); locks b, a b

  3. Deadlock in Libraries • Library writers may wish to provide guarantees • JDK’s StringBuffer documentation says class is thread-safe • Goal: find client calls that deadlock library or verify that none exist

  4. Analyzing Programs / Libraries

  5. Deadlock from Sun’s JDK import java.beans.beancontext.*; BeanContextSupport support = new BeanContextSupport(); Object source = new Object(); PropertyChangeEvent event = new PropertyChangeEvent(source, "beanContext", ...); support.add(source); support.vetoableChange(event); Thread 1: support.propertyChange(event); locks global, field Thread 2: support.remove(source); locks field, global Also found 13 other deadlocks

  6. Analysis Overview • Build lock-order graph representing locking behavior of each method in library • Combine graphs for all public methods into single graph • Detect cycles in this graph, which indicate deadlock possibilities • Analysis properties: reports all deadlocks, context-sensitive, flow-sensitive

  7. JDK Source (simplified) interface BeanContext { public static final Object globalHierarchyLock; } class BeanContextSupport { protected HashMap children; public boolean remove(Object targetChild) { synchronized(BeanContext.globalHierarchyLock) { ... synchronized(children) { children.remove(targetChild); } ... } ... } Object HashMap Continued...

  8. JDK Source (simplified), cont. class BeanContextSupport { protected HashMap children; public void propertyChange(PropertyChangeEvent pce) { ... Object source = pce.getSource(); synchronized(children) { if (...) { ... remove(source); ... } } } } Object HashMap public boolean remove(Object targetChild) { synchronized (BeanContext.globalHierarchyLock) { ... } }

  9. Merged Graph • When merged, graphs indicate possible locking orders of all methods • Cycles indicate possible deadlock • Expose cases in which threads lock set of locks in different (conflicting) orders Object HashMap

  10. Outline • Introduction • Deadlock Detection Algorithm • Results • Related Work and Conclusions

  11. Synchronization in Java • Locking is hierarchical, performed usingsynchronized statement • Multiple locks acquiredvia nested synchronizedstatements • Synchronizing on previously acquired lock always succeeds • Considered a no-op for our analysis • Synchronized methods sugar for synchronizing on this synchronized (lock1) { synchronized (lock2) { ... } }

  12. Synchronization in Java • wait() and notify() methods • Java 1.5’s non-hierarchical primitives (in java.concurrent package) not covered by analysis • Usage rare; recommended only for expert programmers

  13. Analysis Overview • Build lock-order graph representing locking behavior of each method in library • Callee graphs integrated into caller • Iterate to fixed point; termination guaranteed • Combine graphs for all public methods into single graph • Detect cycles in this graph, which indicate deadlock possibilities

  14. Lock-order Graph • Directed graph that represents the order in which locks are acquired • Nodes represent may-alias sets • Allows graphs from differentmethods to be combined • Edges mean the source lockheld while destination lockacquired • Cycles indicate possibility of deadlock set 1 set 2 set 3

  15. public void deposit(Bank b1, Client c1) { synchronized (b1) { synchronized (c1) { ... } } } public void openAccount(Bank b2, Client c2) { synchronized (b2) { ... } synchronized (c2) { deposit(b2, c2); } } Example Library

  16. Graph: Ordered list of locks held: [] public void deposit(Bank b1, Client c1) { synchronized (b1) { synchronized (c1) { ... } } } public void openAccount(Bank b2, Client c2) { synchronized (b2) { ... } synchronized (c2) { deposit(b2, c2); } } Example Analysis: deposit()

  17. Graph: Ordered list of locks held: [] public void deposit(Bank b1, Client c1) { synchronized (b1) { synchronized (c1) { ... } } } public void openAccount(Bank b2, Client c2) { synchronized (b2) { ... } synchronized (c2) { deposit(b2, c2); } } Example Analysis: deposit()

  18. Graph: Ordered list of locks held: [b1] public void deposit(Bank b1, Client c1) { synchronized (b1) { synchronized (c1) { ... } } } public void openAccount(Bank b2, Client c2) { synchronized (b2) { ... } synchronized (c2) { deposit(b2, c2); } } Example Analysis: deposit() No locks held, so node is root b1

  19. Graph: Ordered list of locks held: [b1] public void deposit(Bank b1, Client c1) { synchronized (b1) { synchronized (c1) { ... } } } public void openAccount(Bank b2, Client c2) { synchronized (b2) { ... } synchronized (c2) { deposit(b2, c2); } } Example Analysis: deposit() b1

  20. Graph: Ordered list of locks held: [b1, c1] public void deposit(Bank b1, Client c1) { synchronized (b1) { synchronized (c1) { ... } } } public void openAccount(Bank b2, Client c2) { synchronized (b2) { ... } synchronized (c2) { deposit(b2, c2); } } Example Analysis: deposit() b1 c1

  21. Graph: Ordered list of locks held: [b1, c1] public void deposit(Bank b1, Client c1) { synchronized (b1) { synchronized (c1) { ... } } } public void openAccount(Bank b2, Client c2) { synchronized (b2) { ... } synchronized (c2) { deposit(b2, c2); } } Example Analysis: deposit() b1 c1

  22. Graph: Ordered list of locks held: [b1] public void deposit(Bank b1, Client c1) { synchronized (b1) { synchronized (c1) { ... } } } public void openAccount(Bank b2, Client c2) { synchronized (b2) { ... } synchronized (c2) { deposit(b2, c2); } } Example Analysis: deposit() b1 c1

  23. Graph: public void deposit(Bank b1, Client c1) { synchronized (b1) { synchronized (c1) { ... } } } public void openAccount(Bank b2, Client c2) { synchronized (b2) { ... } synchronized (c2) { deposit(b2, c2); } } Lock-order graph for deposit() b1 c1

  24. Graph: Ordered list of locks held: [] public void deposit(Bank b1, Client c1) { synchronized (b1) { synchronized (c1) { ... } } } public void openAccount(Bank b2, Client c2) { synchronized (b2) { ... } synchronized (c2) { deposit(b2, c2); } } Example Analysis: openAccount() deposit’s graph: b1 c1

  25. Graph: Ordered list of locks held: [b2] public void deposit(Bank b1, Client c1) { synchronized (b1) { synchronized (c1) { ... } } } public void openAccount(Bank b2, Client c2) { synchronized (b2) { ... } synchronized (c2) { deposit(b2, c2); } } Example Analysis: openAccount() deposit’s graph: b1 c1 b2

  26. Graph: Ordered list of locks held: [c2] public void deposit(Bank b1, Client c1) { synchronized (b1) { synchronized (c1) { ... } } } public void openAccount(Bank b2, Client c2) { synchronized (b2) { ... } synchronized (c2) { deposit(b2, c2); } } Example Analysis: openAccount() deposit’s graph: b1 c1 c2 b2

  27. Graph: Ordered list of locks held: [c2] public void deposit(Bank b1, Client c1) { synchronized (b1) { synchronized (c1) { ... } } } public void openAccount(Bank b2, Client c2) { synchronized (b2) { ... } synchronized (c2) { deposit(b2, c2); } } Example Analysis: openAccount() deposit’s graph: b1 c1 c2 b2

  28. Graph: Ordered list of locks held: [c2] public void deposit(Bank b1, Client c1) { synchronized (b1) { synchronized (c1) { ... } } } public void openAccount(Bank b2, Client c2) { synchronized (b2) { ... } synchronized (c2) { deposit(b2, c2); } } Example Analysis: openAccount() current graph: deposit’s graph: b1 c2 b2 c1

  29. Graph: Ordered list of locks held: [c2] public void deposit(Bank b1, Client c1) { synchronized (b1) { synchronized (c1) { ... } } } public void openAccount(Bank b2, Client c2) { synchronized (b2) { ... } synchronized (c2) { deposit(b2, c2); } } Call to deposit(): update copy of deposit’s graph ^ current graph: deposit’s graph: b1 c2 b2 c1 b1 b2 c1 b2

  30. Graph: Ordered list of locks held: [c2] public void deposit(Bank b1, Client c1) { synchronized (b1) { synchronized (c1) { ... } } } public void openAccount(Bank b2, Client c2) { synchronized (b2) { ... } synchronized (c2) { deposit(b2, c2); } } Call to deposit(): update copy of deposit’s graph ^ current graph: deposit’s graph: b1 c2 b2 c1 b2 c1 c2 c2

  31. Graph: Ordered list of locks held: [c2] public void deposit(Bank b1, Client c1) { synchronized (b1) { synchronized (c1) { ... } } } public void openAccount(Bank b2, Client c2) { synchronized (b2) { ... } synchronized (c2) { deposit(b2, c2); } } Call to deposit(): update copy of deposit’s graph ^ current graph: deposit’s graph: b1 c2 b2 c1 b2 c2

  32. Graph: Ordered list of locks held: [c2] public void deposit(Bank b1, Client c1) { synchronized (b1) { synchronized (c1) { ... } } } public void openAccount(Bank b2, Client c2) { synchronized (b2) { ... } synchronized (c2) { deposit(b2, c2); } } Call to deposit(): update copy of deposit’s graph ^ current graph: deposit’s graph: b1 c2 b2 c1 b2 b2

  33. Graph: Ordered list of locks held: [c2] public void deposit(Bank b1, Client c1) { synchronized (b1) { synchronized (c1) { ... } } } public void openAccount(Bank b2, Client c2) { synchronized (b2) { ... } synchronized (c2) { deposit(b2, c2); } } Call to deposit(): insert deposit’s graph deposit’s graph: b1 c1 c2 b2 b2

  34. Graph: Ordered list of locks held: [c2] public void deposit(Bank b1, Client c1) { synchronized (b1) { synchronized (c1) { ... } } } public void openAccount(Bank b2, Client c2) { synchronized (b2) { ... } synchronized (c2) { deposit(b2, c2); } } Call to deposit(): insert deposit’s graph deposit’s graph: b1 c1 c2 b2 b2

  35. Graph: public void deposit(Bank b1, Client c1) { synchronized (b1) { synchronized (c1) { ... } } } public void openAccount(Bank b2, Client c2) { synchronized (b2) { ... } synchronized (c2) { deposit(b2, c2); } } Lock-order graph for openAccount() deposit’s graph: b1 c1 c2 b2

  36. Analysis Overview • Build lock-order graph representing locking behavior of each method in library • Callee graphs integrated into caller • Iterate to fixed point; termination guaranteed • Combine graphs for all public methods into single graph • Detect cycles in this graph, which indicate deadlock possibilities

  37. Graph for deposit(): Graph for openAccount(): Combine Graphs b1 c2 b2 c1

  38. Graph for deposit(): Graph for openAccount(): Combine Graphs Bank Client Bank Client

  39. Graph for deposit(): Graph for openAccount(): Combine Graphs Final graph: Bank Client Bank Client

  40. Analysis Overview • Build lock-order graph representing locking behavior of each method in library • Callee graphs integrated into caller • Iterate to fixed point; termination guaranteed • Combine graphs for all public methods into single graph • Detect cycles in this graph, which indicate deadlock possibilities

  41. Cycle in Combined Graph Cycles indicate possibility of deadlock, and deadlock is possible Final graph: Client Bank

  42. public void deposit(Bank b1, Client c1) { synchronized (b1) { synchronized (c1) { ... } } } public void openAccount(Bank b2, Client c2) { synchronized (b2) { ... } synchronized (c2) { deposit(b2, c2); } } Code that Deadlocks Library Bank b; Client c; Thread 1: openAccount(b, c); locks c,b Thread 2: deposit(b, c); locks b, c

  43. Beyond Public Methods • Graph for library must also include static initializers and finalizers • Method calls to Thread.start() handled specially • Lock-order graphs for receiver’s run() method integrated into graph for whole library • ECOOP paper omitted these cases; addressed in forthcoming Technical Report

  44. Handling wait() and notify() • Calling wait() releases and later reacquires receiver’s lock • Considered no-op if receiver’s lock most recently acquired • Can change lock order even if receiver’s lock is held

  45. Handling wait() and notify() • Calling wait() releases and later reacquires receiver’s lock • Considered no-op if receiver’s lock most recently acquired • Can change lock order even if receiver’s lock is held synchronized (a) { synchronized (b) { … b.wait() … } } a b

  46. Handling wait() and notify() • Calling wait() releases and later reacquires receiver’s lock • Considered no-op if receiver’s lock most recently acquired • Can change lock order even if receiver’s lock is held synchronized (a) { synchronized (b) { … b.wait() … } } a b

  47. Handling wait() and notify() • Calling wait() releases and later reacquires receiver’s lock • Considered no-op if receiver’s lock most recently acquired • Can change lock order even if receiver’s lock is held synchronized (a) { synchronized (b) { … a.wait() … } } a b

  48. Handling wait() and notify() • Calling wait() releases and later reacquires receiver’s lock • Considered no-op if receiver’s lock most recently acquired • Can change lock order even if receiver’s lock is held synchronized (a) { synchronized (b) { … a.wait() … } } a b

  49. Deadlock using wait() Object a, b; Thread 1: synchronized (a) { synchronized (b) { … a.wait(); } } locks a, b and b, a

  50. Deadlock using wait() Object a, b; Thread 1: synchronized (a) { synchronized (b) { … a.wait(); } } locks a, b and b, a a b

More Related