Commit 4c20e76b authored by hpayer@chromium.org's avatar hpayer@chromium.org

heap: allow allocation in gc prologue/epilogue

BUG=
R=hpayer@chromium.org, danno@chromium.org

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

Patch from Fedor Indutny <fedor.indutny@gmail.com>.

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@19978 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 1174e923
......@@ -4252,13 +4252,12 @@ class V8_EXPORT Isolate {
/**
* Enables the host application to receive a notification before a
* garbage collection. Allocations are not allowed in the
* callback function, you therefore cannot manipulate objects (set
* or delete properties for example) since it is possible such
* operations will result in the allocation of objects. It is possible
* to specify the GCType filter for your callback. But it is not possible to
* register the same callback function two times with different
* GCType filters.
* garbage collection. Allocations are allowed in the callback function,
* but the callback is not re-entrant: if the allocation inside it will
* trigger the garbage collection, the callback won't be called again.
* It is possible to specify the GCType filter for your callback. But it is
* not possible to register the same callback function two times with
* different GCType filters.
*/
void AddGCPrologueCallback(
GCPrologueCallback callback, GCType gc_type_filter = kGCTypeAll);
......@@ -4271,13 +4270,12 @@ class V8_EXPORT Isolate {
/**
* Enables the host application to receive a notification after a
* garbage collection. Allocations are not allowed in the
* callback function, you therefore cannot manipulate objects (set
* or delete properties for example) since it is possible such
* operations will result in the allocation of objects. It is possible
* to specify the GCType filter for your callback. But it is not possible to
* register the same callback function two times with different
* GCType filters.
* garbage collection. Allocations are allowed in the callback function,
* but the callback is not re-entrant: if the allocation inside it will
* trigger the garbage collection, the callback won't be called again.
* It is possible to specify the GCType filter for your callback. But it is
* not possible to register the same callback function two times with
* different GCType filters.
*/
void AddGCEpilogueCallback(
GCEpilogueCallback callback, GCType gc_type_filter = kGCTypeAll);
......
......@@ -810,6 +810,21 @@ NoWeakObjectVerificationScope::~NoWeakObjectVerificationScope() {
#endif
GCCallbacksScope::GCCallbacksScope(Heap* heap) : heap_(heap) {
heap_->gc_callbacks_depth_++;
}
GCCallbacksScope::~GCCallbacksScope() {
heap_->gc_callbacks_depth_--;
}
bool GCCallbacksScope::CheckReenter() {
return heap_->gc_callbacks_depth_ == 1;
}
void VerifyPointersVisitor::VisitPointers(Object** start, Object** end) {
for (Object** current = start; current < end; current++) {
if ((*current)->IsHeapObject()) {
......
......@@ -155,7 +155,8 @@ Heap::Heap()
configured_(false),
external_string_table_(this),
chunks_queued_for_free_(NULL),
relocation_mutex_(NULL) {
relocation_mutex_(NULL),
gc_callbacks_depth_(0) {
// Allow build-time customization of the max semispace size. Building
// V8 with snapshots and a non-default max semispace size is much
// easier if you can define it as part of the build environment.
......@@ -1087,11 +1088,14 @@ bool Heap::PerformGarbageCollection(
GCType gc_type =
collector == MARK_COMPACTOR ? kGCTypeMarkSweepCompact : kGCTypeScavenge;
{
GCTracer::Scope scope(tracer, GCTracer::Scope::EXTERNAL);
VMState<EXTERNAL> state(isolate_);
HandleScope handle_scope(isolate_);
CallGCPrologueCallbacks(gc_type, kNoGCCallbackFlags);
{ GCCallbacksScope scope(this);
if (scope.CheckReenter()) {
AllowHeapAllocation allow_allocation;
GCTracer::Scope scope(tracer, GCTracer::Scope::EXTERNAL);
VMState<EXTERNAL> state(isolate_);
HandleScope handle_scope(isolate_);
CallGCPrologueCallbacks(gc_type, kNoGCCallbackFlags);
}
}
EnsureFromSpaceIsCommitted();
......@@ -1196,11 +1200,14 @@ bool Heap::PerformGarbageCollection(
amount_of_external_allocated_memory_;
}
{
GCTracer::Scope scope(tracer, GCTracer::Scope::EXTERNAL);
VMState<EXTERNAL> state(isolate_);
HandleScope handle_scope(isolate_);
CallGCEpilogueCallbacks(gc_type, gc_callback_flags);
{ GCCallbacksScope scope(this);
if (scope.CheckReenter()) {
AllowHeapAllocation allow_allocation;
GCTracer::Scope scope(tracer, GCTracer::Scope::EXTERNAL);
VMState<EXTERNAL> state(isolate_);
HandleScope handle_scope(isolate_);
CallGCEpilogueCallbacks(gc_type, gc_callback_flags);
}
}
#ifdef VERIFY_HEAP
......
......@@ -2516,6 +2516,8 @@ class Heap {
bool relocation_mutex_locked_by_optimizer_thread_;
#endif // DEBUG;
int gc_callbacks_depth_;
friend class Factory;
friend class GCTracer;
friend class DisallowAllocationFailure;
......@@ -2528,6 +2530,7 @@ class Heap {
#ifdef VERIFY_HEAP
friend class NoWeakObjectVerificationScope;
#endif
friend class GCCallbacksScope;
DISALLOW_COPY_AND_ASSIGN(Heap);
};
......@@ -2600,6 +2603,18 @@ class NoWeakObjectVerificationScope {
#endif
class GCCallbacksScope {
public:
explicit inline GCCallbacksScope(Heap* heap);
inline ~GCCallbacksScope();
inline bool CheckReenter();
private:
Heap* heap_;
};
// Visitor class to verify interior pointers in spaces that do not contain
// or care about intergenerational references. All heap object pointers have to
// point into the heap to a location that has a map pointer at its first word.
......
......@@ -18666,6 +18666,8 @@ int prologue_call_count = 0;
int epilogue_call_count = 0;
int prologue_call_count_second = 0;
int epilogue_call_count_second = 0;
int prologue_call_count_alloc = 0;
int epilogue_call_count_alloc = 0;
void PrologueCallback(v8::GCType, v8::GCCallbackFlags flags) {
CHECK_EQ(flags, v8::kNoGCCallbackFlags);
......@@ -18727,6 +18729,46 @@ void EpilogueCallbackSecond(v8::Isolate* isolate,
}
void PrologueCallbackAlloc(v8::Isolate* isolate,
v8::GCType,
v8::GCCallbackFlags flags) {
v8::HandleScope scope(isolate);
CHECK_EQ(flags, v8::kNoGCCallbackFlags);
CHECK_EQ(gc_callbacks_isolate, isolate);
++prologue_call_count_alloc;
// Simulate full heap to see if we will reenter this callback
SimulateFullSpace(CcTest::heap()->new_space());
Local<Object> obj = Object::New(isolate);
ASSERT(!obj.IsEmpty());
CcTest::heap()->CollectAllGarbage(
i::Heap::Heap::kAbortIncrementalMarkingMask);
}
void EpilogueCallbackAlloc(v8::Isolate* isolate,
v8::GCType,
v8::GCCallbackFlags flags) {
v8::HandleScope scope(isolate);
CHECK_EQ(flags, v8::kNoGCCallbackFlags);
CHECK_EQ(gc_callbacks_isolate, isolate);
++epilogue_call_count_alloc;
// Simulate full heap to see if we will reenter this callback
SimulateFullSpace(CcTest::heap()->new_space());
Local<Object> obj = Object::New(isolate);
ASSERT(!obj.IsEmpty());
CcTest::heap()->CollectAllGarbage(
i::Heap::Heap::kAbortIncrementalMarkingMask);
}
TEST(GCCallbacksOld) {
LocalContext context;
......@@ -18793,6 +18835,17 @@ TEST(GCCallbacks) {
CHECK_EQ(2, epilogue_call_count);
CHECK_EQ(2, prologue_call_count_second);
CHECK_EQ(2, epilogue_call_count_second);
CHECK_EQ(0, prologue_call_count_alloc);
CHECK_EQ(0, epilogue_call_count_alloc);
isolate->AddGCPrologueCallback(PrologueCallbackAlloc);
isolate->AddGCEpilogueCallback(EpilogueCallbackAlloc);
CcTest::heap()->CollectAllGarbage(
i::Heap::Heap::kAbortIncrementalMarkingMask);
CHECK_EQ(1, prologue_call_count_alloc);
CHECK_EQ(1, epilogue_call_count_alloc);
isolate->RemoveGCPrologueCallback(PrologueCallbackAlloc);
isolate->RemoveGCEpilogueCallback(EpilogueCallbackAlloc);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment