Refactor reference queues.

Refactored the reference queue processing to reside in the heap code.
This removes significant code duplication in the semispace and
marksweep garbage collectors.

Changed the soft reference behaviour to preserve all soft references
unless the GC requires them to be cleared to avoid an out of memory
error. It may be worth investigating a better heuristic in the
future to preserve soft references by LRU order.

Change-Id: I1f3ff5bd4b3c5149271f4bb4fc94ba199e2f9bc2
diff --git a/runtime/gc/reference_queue.cc b/runtime/gc/reference_queue.cc
new file mode 100644
index 0000000..d006349
--- /dev/null
+++ b/runtime/gc/reference_queue.cc
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "reference_queue.h"
+
+#include "accounting/card_table-inl.h"
+#include "heap.h"
+#include "mirror/class-inl.h"
+#include "mirror/object-inl.h"
+
+namespace art {
+namespace gc {
+
+ReferenceQueue::ReferenceQueue(Heap* heap)
+    : lock_("reference queue lock"),
+      heap_(heap),
+      list_(nullptr) {
+}
+
+void ReferenceQueue::AtomicEnqueueIfNotEnqueued(Thread* self, mirror::Object* ref) {
+  DCHECK(ref != NULL);
+  MutexLock mu(self, lock_);
+  if (!heap_->IsEnqueued(ref)) {
+    EnqueuePendingReference(ref);
+  }
+}
+
+void ReferenceQueue::EnqueueReference(mirror::Object* ref) {
+  CHECK(heap_->IsEnqueuable(ref));
+  EnqueuePendingReference(ref);
+}
+
+void ReferenceQueue::EnqueuePendingReference(mirror::Object* ref) {
+  DCHECK(ref != NULL);
+  MemberOffset pending_next_offset = heap_->GetReferencePendingNextOffset();
+  DCHECK_NE(pending_next_offset.Uint32Value(), 0U);
+  if (IsEmpty()) {
+    // 1 element cyclic queue, ie: Reference ref = ..; ref.pendingNext = ref;
+    ref->SetFieldObject(pending_next_offset, ref, false);
+    list_ = ref;
+  } else {
+    mirror::Object* head =
+        list_->GetFieldObject<mirror::Object*>(pending_next_offset, false);
+    ref->SetFieldObject(pending_next_offset, head, false);
+    list_->SetFieldObject(pending_next_offset, ref, false);
+  }
+}
+
+mirror::Object* ReferenceQueue::DequeuePendingReference() {
+  DCHECK(!IsEmpty());
+  MemberOffset pending_next_offset = heap_->GetReferencePendingNextOffset();
+  mirror::Object* head = list_->GetFieldObject<mirror::Object*>(pending_next_offset, false);
+  DCHECK(head != nullptr);
+  mirror::Object* ref;
+  // Note: the following code is thread-safe because it is only called from ProcessReferences which
+  // is single threaded.
+  if (list_ == head) {
+    ref = list_;
+    list_ = nullptr;
+  } else {
+    mirror::Object* next = head->GetFieldObject<mirror::Object*>(pending_next_offset, false);
+    list_->SetFieldObject(pending_next_offset, next, false);
+    ref = head;
+  }
+  ref->SetFieldObject(pending_next_offset, nullptr, false);
+  return ref;
+}
+
+void ReferenceQueue::Dump(std::ostream& os) const {
+  mirror::Object* cur = list_;
+  os << "Reference starting at list_=" << list_ << "\n";
+  while (cur != nullptr) {
+    mirror::Object* pending_next =
+        cur->GetFieldObject<mirror::Object*>(heap_->GetReferencePendingNextOffset(), false);
+    os << "PendingNext=" << pending_next;
+    if (cur->GetClass()->IsFinalizerReferenceClass()) {
+      os << " Zombie=" <<
+          cur->GetFieldObject<mirror::Object*>(heap_->GetFinalizerReferenceZombieOffset(), false);
+    }
+    os << "\n";
+    cur = pending_next;
+  }
+}
+
+void ReferenceQueue::ClearWhiteReferences(ReferenceQueue& cleared_references, RootVisitor visitor,
+                                          void* arg) {
+  while (!IsEmpty()) {
+    mirror::Object* ref = DequeuePendingReference();
+    mirror::Object* referent = heap_->GetReferenceReferent(ref);
+    if (referent != nullptr) {
+      mirror::Object* forward_address = visitor(referent, arg);
+      if (forward_address == nullptr) {
+        // Referent is white, clear it.
+        heap_->ClearReferenceReferent(ref);
+        if (heap_->IsEnqueuable(ref)) {
+          cleared_references.EnqueuePendingReference(ref);
+        }
+      } else if (referent != forward_address) {
+        // Object moved, need to updated the referrent.
+        heap_->SetReferenceReferent(ref, forward_address);
+      }
+    }
+  }
+}
+
+void ReferenceQueue::EnqueueFinalizerReferences(ReferenceQueue& cleared_references,
+                                                RootVisitor is_marked_callback,
+                                                RootVisitor recursive_mark_callback, void* arg) {
+  while (!IsEmpty()) {
+    mirror::Object* ref = DequeuePendingReference();
+    mirror::Object* referent = heap_->GetReferenceReferent(ref);
+    if (referent != nullptr) {
+      mirror::Object* forward_address = is_marked_callback(referent, arg);
+      // If the referent isn't marked, mark it and update the
+      if (forward_address == nullptr) {
+        forward_address = recursive_mark_callback(referent, arg);
+        // If the referent is non-null the reference must queuable.
+        DCHECK(heap_->IsEnqueuable(ref));
+        // Move the updated referent to the zombie field.
+        ref->SetFieldObject(heap_->GetFinalizerReferenceZombieOffset(), forward_address, false);
+        heap_->ClearReferenceReferent(ref);
+        cleared_references.EnqueueReference(ref);
+      } else if (referent != forward_address) {
+        heap_->SetReferenceReferent(ref, forward_address);
+      }
+    }
+  }
+}
+
+void ReferenceQueue::PreserveSomeSoftReferences(RootVisitor preserve_callback, void* arg) {
+  ReferenceQueue cleared(heap_);
+  while (!IsEmpty()) {
+    mirror::Object* ref = DequeuePendingReference();
+    mirror::Object* referent = heap_->GetReferenceReferent(ref);
+    if (referent != nullptr) {
+      mirror::Object* forward_address = preserve_callback(referent, arg);
+      if (forward_address == nullptr) {
+        // Either the reference isn't marked or we don't wish to preserve it.
+        cleared.EnqueuePendingReference(ref);
+      } else {
+        heap_->SetReferenceReferent(ref, forward_address);
+      }
+    }
+  }
+  list_ = cleared.GetList();
+}
+
+}  // namespace gc
+}  // namespace art
+