370 likes | 386 Views
Parallel Checking of Expressive Heap Assertions. Phalanx. Greta Yorsh. Eran Yahav. Martin Vechev. Bard Bloom. IBM T.J. Watson Research Center. Motivation. Unrestricted use of aliasing is evil Unrestricted use of aliasing in the presence of concurrency is ultimate evil.
E N D
Parallel Checking of Expressive Heap Assertions Phalanx Greta Yorsh EranYahav Martin Vechev Bard Bloom IBM T.J. Watson Research Center
Motivation • Unrestricted use of aliasing is evil • Unrestricted use of aliasing in the presence of concurrency is ultimate evil
Motivating Example: Azureus Over 360 million downloads
Runtime Error org.eclipse.swt.SWTException: Graphic is disposed at org.eclipse.swt.SWT.error(SWT.java:3744) at org.eclipse.swt.SWT.error(SWT.java:3662) at org.eclipse.swt.SWT.error(SWT.java:3633) at org.eclipse.swt.graphics.GC.getClipping(GC.java:2266) at com.aelitis.azureus.ui.swt.views.list.ListRow.doPaint(ListRow.java:260) at com.aelitis.azureus.ui.swt.views.list.ListRow.doPaint(ListRow.java:237) at com.aelitis.azureus.ui.swt.views.list.ListView.handleResize(ListView.java:867) at com.aelitis.azureus.ui.swt.views.list.ListView$5$2.runSupport(ListView.java:406) at org.gudy.azureus2.core3.util.AERunnable.run(AERunnable.java:38) at org.eclipse.swt.widgets.RunnableLock.run(RunnableLock.java:35) at org.eclipse.swt.widgets.Synchronizer.runAsyncMessages(Synchronizer.java:130) at org.eclipse.swt.widgets.Display.runAsyncMessages(Display.java:3323) at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:2985) at org.gudy.azureus2.ui.swt.mainwindow.SWTThread.<init>(SWTThread.java:183) at org.gudy.azureus2.ui.swt.mainwindow.SWTThread.createInstance(SWTThread.java:67)….
What Happened? protected void handleRefresh(booleanbForce) { // ... • gc.dispose(); } GC • gc • myGC Nativeresource protected void handleResize(booleanbForce) { // ... myGC.getClipping(…) }
If only I could check… protected void handleRefresh(booleanbForce) { // ... // @assert object pointed to by gc is not shared • gc.dispose(); } GC • gc • myGC Nativeresource protected void handleResize(booleanbForce) { // ... myGC.getClipping(…) }
Motivating Example II: jdbf public class Database { private ConnectionManager cm; public int insert(...) throws MappingEx { Connection c = cm.getConnection(...); ... } ... } public class ConnectionManager { private Map conns = Collections.synchronizedMap(new HashMap()); public Connection getConnection(String s) throws MappingException { try { ConnectionSource c = conns.get(s); if (c != null) return c.getConnection(); throw new MappingException(...); } catch (SQLEx e) { ... } } } } public class ConnectionSource { private Connection conn; private boolean used; public Connection getConnection() throws SQLEx { if (!used) { used = true; return conn; } throw new SQLEx(...); }
Motivating Example II: jdbf Root ct Running Running Static Thread Database Thread If only I could check… @invariant: every conncetion is only reachable from one thread (avoiding connection manager) Stack Connection Manager Stack HashMap Connection Source Connection Source Connection Source Connection Connection Connection
Phalanx Challenges • Expressing heap queries • Is object shared? • Is object reachable? • Is object reachable when avoiding paths through some other objects? • Is object owned? • … • Checking heap queries at runtime
Expressing Heap Queries • Use JML • Extended with additional primitives • reach(Object o, Object[] avoiding) • pred(Object o) • dom(Object o1,Object o2) • …
Examples • Object o is shared pred(o).size() > 1 • Set of threads that can reach o, while avoiding objects in avoid:{ Thread t | running().has(t) && (reach(t,avoid).has(o) || reach(stack(t),avoid).has(o)) }
Checking Heap Queries: Wish List • Support wide range of queries • We have a nice extended JML + primitives • Overhead low enough to permit running realistic applications • Debugging • Program understanding • Maybe even production
Checking Heap Queries: First Attempt • We need to traverse the heap to answer our queries • The garbage collector (GC) already traverses the heap periodically • GC can be parallel and leverage available system cores • Piggyback the GC !
Crash Course: Tracing GC r1 r2
Crash Course: Parallel Tracing GC r1 r2 Thread 1 Thread 2
Checking Heap Queries:How can we use the GC? • reach(o) • Know that objects are reachable, but not whether they are reachable from o • reach(o1,o2) • With GC I would only know o1,o2 are reachable from roots • Now what? • reach(o1) reach(o2) = • Requires two marked sets • Now what? • …
Supported Primitives? • reach(Object o) • pred(Object o) • reach(Object o, Object[] avoiding) • dom(Object o1,Object o2) • Some primitives cannot be computed by piggybacking a GC traversal
What can we do in parallel? • Is object o shared? • pred(o).size() > 1 • Disjoint reach set? • reach(o1) reach(o2) =
Checking Heap Queries:Second Attempt • We need new parallel algorithms • We can use components of a parallel GC as building blocks for our parallel algorithms
New Algorithms Based on GC components • New parallel algorithms for common queries • New operations performed on GC steps • New synchronization structures for computing answers to heap queries • Leverage available system cores • Modified JMLC maps common queries to parallel implementations
Back to our example: isShared trace-step(s; t) if (o = t) tm.sourcestm.sources { s } isShared(tm, o) tm.sources; mark-threads(tm, Ta) trace(tm) lock(allsources) allsourcesallsourcestm.sources unlock(allsources) if barrier-and-release-master() if |allsources| > 1 result true else result false release-blocked-evaluators()
Parallel Checking of isShared A r1 r2 B Is shared? Thread 1 t1.sources = { A } allsources = { A, B } Thread 2 t2.sources = { B } isShared = true
isObjectOwned(source,target) isObjectOwned(tm, source, target) { tag-object(tm, source) result false phase skip barrier() mark-roots(tm, Ta) barrier() phase none trace(tm) barrier() if (target Marked) barrier() push-object(tm; source) trace(tm) if barrier-and-release-master() if (target Marked) result true release-blocked-evaluators() } tag-step(t) if (phase = skip t = target) return false
isObjectOwned(source,target) target r1 source r2 r3 Phase 1: tag source
isObjectOwned(source,target) target r1 source r2 r3 Phase 2: mark roots (except target)
isObjectOwned(source,target) target r1 source r2 r3 Phase 3: trace from roots (except from source)
Experimental Evaluation • Implemented on top of QVM platform • IBM J9 production virtual machine • Can leverage QVM adaptive overhead manager (not in this talk) • Provide a portable reference implementation based on JVMTI • Less efficient, no parallel algorithms • Still useful in some cases • Modified JML Compiler
Probes in Real Applications • Disposal of Shared SWT Resources • replace code of the form: exp.dispose(); • with code of the form if (Phalanx.isShared(exp)) Phalanx.warning(”disposal of \ shared resource”+exp) ; exp.dispose();
Probes in Real Applications • Redundant Synchronization • replace code of the form: synchronized(exp) { ... } • with code of the form synchronized(exp) { if(Phalanx.dom(Thread.currentThread(),exp)) Phalanx.warning(”synchronziation on \ an owned object”+exp) ; ... }