Fix disabling of code flusher while marking.
authormstarzinger@chromium.org <mstarzinger@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 8 Nov 2012 10:26:50 +0000 (10:26 +0000)
committermstarzinger@chromium.org <mstarzinger@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 8 Nov 2012 10:26:50 +0000 (10:26 +0000)
This fixes a corner case when the code flusher is disabled while
incremental marking is running. The list of candidates needs to be
evicted to prevent list fragments without a head floating around.

R=ulan@chromium.org
BUG=chromium:159140

Review URL: https://codereview.chromium.org/11366136

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@12894 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/mark-compact.cc
src/mark-compact.h
test/cctest/test-heap.cc

index ebba22b44a875b01006d01f0fe790636775ed8bf..80988db5853b14e865cf756a3ce3b40092ae2c61 100644 (file)
@@ -952,6 +952,34 @@ void CodeFlusher::EvictCandidate(JSFunction* function) {
 }
 
 
+void CodeFlusher::EvictJSFunctionCandidates() {
+  Object* undefined = isolate_->heap()->undefined_value();
+
+  JSFunction* candidate = jsfunction_candidates_head_;
+  JSFunction* next_candidate;
+  while (candidate != NULL) {
+    next_candidate = GetNextCandidate(candidate);
+    ClearNextCandidate(candidate, undefined);
+    candidate = next_candidate;
+  }
+
+  jsfunction_candidates_head_ = NULL;
+}
+
+
+void CodeFlusher::EvictSharedFunctionInfoCandidates() {
+  SharedFunctionInfo* candidate = shared_function_info_candidates_head_;
+  SharedFunctionInfo* next_candidate;
+  while (candidate != NULL) {
+    next_candidate = GetNextCandidate(candidate);
+    ClearNextCandidate(candidate);
+    candidate = next_candidate;
+  }
+
+  shared_function_info_candidates_head_ = NULL;
+}
+
+
 void CodeFlusher::IteratePointersToFromSpace(ObjectVisitor* v) {
   Heap* heap = isolate_->heap();
 
@@ -3629,6 +3657,7 @@ void MarkCompactCollector::EnableCodeFlushing(bool enable) {
     code_flusher_ = new CodeFlusher(heap()->isolate());
   } else {
     if (code_flusher_ == NULL) return;
+    code_flusher_->EvictAllCandidates();
     delete code_flusher_;
     code_flusher_ = NULL;
   }
index 8b1620eb21baf51c5a0f78756dc807ad5b84a21d..642839d254afcd1f90fff74d482dfaa3216d459c 100644 (file)
@@ -441,11 +441,18 @@ class CodeFlusher {
     ProcessJSFunctionCandidates();
   }
 
+  void EvictAllCandidates() {
+    EvictJSFunctionCandidates();
+    EvictSharedFunctionInfoCandidates();
+  }
+
   void IteratePointersToFromSpace(ObjectVisitor* v);
 
  private:
   void ProcessJSFunctionCandidates();
   void ProcessSharedFunctionInfoCandidates();
+  void EvictJSFunctionCandidates();
+  void EvictSharedFunctionInfoCandidates();
 
   static JSFunction** GetNextCandidateSlot(JSFunction* candidate) {
     return reinterpret_cast<JSFunction**>(
index 811973b46ad3f87e636b79e39a00180f7c5f8ef2..2bb3af624dbedf8909c38ca247d0150683fe7174 100644 (file)
@@ -1133,6 +1133,65 @@ TEST(TestCodeFlushingIncrementalScavenge) {
 }
 
 
+TEST(TestCodeFlushingIncrementalAbort) {
+  // If we do not flush code this test is invalid.
+  if (!FLAG_flush_code || !FLAG_flush_code_incrementally) return;
+  i::FLAG_allow_natives_syntax = true;
+  InitializeVM();
+  v8::HandleScope scope;
+  const char* source = "function foo() {"
+                       "  var x = 42;"
+                       "  var y = 42;"
+                       "  var z = x + y;"
+                       "};"
+                       "foo()";
+  Handle<String> foo_name = FACTORY->LookupAsciiSymbol("foo");
+
+  // This compile will add the code to the compilation cache.
+  { v8::HandleScope scope;
+    CompileRun(source);
+  }
+
+  // Check function is compiled.
+  Object* func_value = Isolate::Current()->context()->global_object()->
+      GetProperty(*foo_name)->ToObjectChecked();
+  CHECK(func_value->IsJSFunction());
+  Handle<JSFunction> function(JSFunction::cast(func_value));
+  CHECK(function->shared()->is_compiled());
+
+  // The code will survive at least two GCs.
+  HEAP->CollectAllGarbage(Heap::kNoGCFlags);
+  HEAP->CollectAllGarbage(Heap::kNoGCFlags);
+  CHECK(function->shared()->is_compiled());
+
+  // Bump the code age so that flushing is triggered.
+  const int kAgingThreshold = 6;
+  function->shared()->set_code_age(kAgingThreshold);
+
+  // Simulate incremental marking so that the function is enqueued as
+  // code flushing candidate.
+  SimulateIncrementalMarking();
+
+  // Enable the debugger and add a breakpoint while incremental marking
+  // is running so that incremental marking aborts and code flushing is
+  // disabled.
+  int position = 0;
+  Handle<Object> breakpoint_object(Smi::FromInt(0));
+  ISOLATE->debug()->SetBreakPoint(function, breakpoint_object, &position);
+  ISOLATE->debug()->ClearAllBreakPoints();
+
+  // Force optimization now that code flushing is disabled.
+  { v8::HandleScope scope;
+    CompileRun("%OptimizeFunctionOnNextCall(foo); foo();");
+  }
+
+  // Simulate one final GC to make sure the candidate queue is sane.
+  HEAP->CollectAllGarbage(Heap::kNoGCFlags);
+  CHECK(function->shared()->is_compiled() || !function->IsOptimized());
+  CHECK(function->is_compiled() || !function->IsOptimized());
+}
+
+
 // Count the number of native contexts in the weak list of native contexts.
 int CountNativeContexts() {
   int count = 0;