Do not initiate a concurrent collection if one is already running.

It is possible to cause a recursive garbage collection by writing a
program that triggers a concurrent garbage collection and initiates a
concurrent garbage collection before the garbage collection thread is
scheduled.  For example

  for (;;) { new byte[16 << 20]; System.gc(); }

When this condition occurs a warning is logged although such warnings
ought to be upgraded to fatal errors.  With this change, when the
garbage collection thread is scheduled it first checks to see if there
is a running collection before calling down to start the collection.

Change-Id: Ia7baf5eba245bbf3fe053d3bad3f90876cad2459
diff --git a/vm/alloc/HeapSource.cpp b/vm/alloc/HeapSource.cpp
index aa084ab..fd2d46c 100644
--- a/vm/alloc/HeapSource.cpp
+++ b/vm/alloc/HeapSource.cpp
@@ -401,9 +401,16 @@
     while (gHs->gcThreadShutdown != true) {
         dvmWaitCond(&gHs->gcThreadCond, &gHs->gcThreadMutex);
         dvmLockHeap();
-        dvmChangeStatus(NULL, THREAD_RUNNING);
-        dvmCollectGarbageInternal(GC_CONCURRENT);
-        dvmChangeStatus(NULL, THREAD_VMWAIT);
+        /*
+         * Another thread may have started a concurrent garbage
+         * collection before we were scheduled.  Check for this
+         * condition before proceeding.
+         */
+        if (!gDvm.gcHeap->gcRunning) {
+            dvmChangeStatus(NULL, THREAD_RUNNING);
+            dvmCollectGarbageInternal(GC_CONCURRENT);
+            dvmChangeStatus(NULL, THREAD_VMWAIT);
+        }
         dvmUnlockHeap();
     }
     dvmChangeStatus(NULL, THREAD_RUNNING);