Parallel Checking of Expressive Heap Assertions Greta YorshMartin VechevEran YahavBard 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. (SWTThread.java:183) at org.gudy.azureus2.ui.swt.mainwindow.SWTThread.createInstance(SWTThread.java: 67) ….
What Happened? protected void handleRefresh(boolean bForce) { //... gc.dispose(); } protected void handleResize(boolean bForce) { //... myGC.getClipping(…) } GC gc myGC Native resource
If only I could check… protected void handleRefresh(boolean bForce) { //... object pointed to by gc is not shared gc.dispose(); } protected void handleResize(boolean bForce) { //... myGC.getClipping(…) } GC gc myGC Native resource
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(...); }
Running Thread Stack Database Root Running Thread Stack HashMap Connection Source Connection Source Connection Static Connection Source Connection Manager ct Motivating Example II: jdbf If only I could every conncetion is only reachable from one thread (avoiding connection manager)
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 isShared(t m, o) t m.sources ; mark-threads(tm, Ta) trace(tm) lock(allsources) allsources allsources tm.sources unlock(allsources) if barrier-and-release-master() if |allsources| > 1 result true else result false release-blocked-evaluators() trace-step(s; t) if (o = t) t m.sources t m.sources { s }
Parallel Checking of isShared r1 r2 Thread 1 Thread 2 Is shared? t 1.sources = { A } A B t 2.sources = { B } allsources = { A, 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) source r2 r1 r3 target Phase 1: tag source
isObjectOwned(source,target) source r2 r1 r3 target Phase 2: mark roots (except target)
isObjectOwned(source,target) source r2 r1 r3 target Phase 3: trace from roots (except from source)
Parallel Algorithms QueryDescriptionProbe pred(o).size() > 0Is o pointed to by a heap object? isHeap(Object o) pred(o).size() > 1Is o pointed to by two or more heap objects? isShared(Object o) reach(src).has(dst)Is dst reachable from src?isReachable(Object src, Object dst) !(exists Object v; reach(o1).has(v) ; reach(o2).has(v)) Is there an object reachable from both o1 and o2? isDisjoint(Object o1, Object o2) !(exists Object v ; reach(o).has(v) ; !dom(o,v)) Does o dominate all objects reachable from it? isUniqueOwner(Object o) !reach(o1,cut).has(o2)Does every path from o1 to o2 go through an object in cut reachThrough(Object o1, o2, Object[] cut) dom(Thread.currentThread(), o) Does the current thread dominate o? isObjectOwned(Object o1, Object o2) …
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
Speedup / #objects
Time / #cores
Speedup / #cores
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) ;... }
Probes in Real Applications ApplicationLOCProbesViolations AOI111, Azureus425, Freemind70, Frostwire245, JEdit93, jrisk20, rssowl74, tvbrowser105, TVLA57,594100
Summary
GC Details
The End