Move GC daemon locking logic into heap

Fixes deadlock caused by acquirng the mutator lock while
synchronizing on the daemon thread.

Bug: 18739541
Change-Id: Ib3ac3788081d3d471195a6e3a8ed163237616a4f
diff --git a/libart/src/main/java/dalvik/system/VMRuntime.java b/libart/src/main/java/dalvik/system/VMRuntime.java
index f778af0..58094d8 100644
--- a/libart/src/main/java/dalvik/system/VMRuntime.java
+++ b/libart/src/main/java/dalvik/system/VMRuntime.java
@@ -297,6 +297,8 @@
 
     public native void trimHeap();
     public native void concurrentGC();
+    public native void requestConcurrentGC();
+    public native void waitForConcurrentGCRequest();
 
     /**
      * Let the heap know of the new process state. This can change allocation and garbage collection
diff --git a/libart/src/main/java/java/lang/Daemons.java b/libart/src/main/java/java/lang/Daemons.java
index 726f782..d3f62c2 100644
--- a/libart/src/main/java/java/lang/Daemons.java
+++ b/libart/src/main/java/java/lang/Daemons.java
@@ -80,6 +80,10 @@
         }
 
         public synchronized void interrupt() {
+            interrupt(thread);
+        }
+
+        public synchronized void interrupt(Thread thread) {
             if (thread == null) {
                 throw new IllegalStateException("not running");
             }
@@ -99,7 +103,7 @@
             if (threadToStop == null) {
                 throw new IllegalStateException("not running");
             }
-            threadToStop.interrupt();
+            interrupt(threadToStop);
             while (true) {
                 try {
                     threadToStop.join();
@@ -311,34 +315,20 @@
         }
     }
 
-    // Invoked by the GC to request that the HeapTrimmerDaemon thread attempt to trim the heap.
-    public static void requestGC() {
-        GCDaemon.INSTANCE.requestGC();
-    }
-
     private static class GCDaemon extends Daemon {
         private static final GCDaemon INSTANCE = new GCDaemon();
-        private static final AtomicBoolean atomicBoolean = new AtomicBoolean();
 
-        public void requestGC() {
-            if (atomicBoolean.getAndSet(true)) {
-              return;
-            }
-            synchronized (this) {
-                notify();
-            }
-            atomicBoolean.set(false);
+        // Overrides the Daemon.interupt method which is called from Daemons.stop.
+        public void interrupt(Thread thread) {
+            // Notifies the daemon thread.
+            VMRuntime.getRuntime().requestConcurrentGC();
         }
 
         @Override public void run() {
             while (isRunning()) {
-                try {
-                    synchronized (this) {
-                        // Wait until a request comes in.
-                        wait();
-                    }
+                VMRuntime.getRuntime().waitForConcurrentGCRequest();
+                if (isRunning()) {
                     VMRuntime.getRuntime().concurrentGC();
-                } catch (InterruptedException ignored) {
                 }
             }
         }