From 7db1759b656ecdc017236165c756ceb4488f5711 Mon Sep 17 00:00:00 2001 From: erikcorry Date: Mon, 12 Jan 2015 04:11:49 -0800 Subject: [PATCH] Unify phantom and internal fields weak handle callbacks R=dcarney@chromium.org BUG= Review URL: https://codereview.chromium.org/842153004 Cr-Commit-Position: refs/heads/master@{#26022} --- include/v8.h | 106 +++++++++++++++------------- src/api.cc | 26 ++++--- src/debug.cc | 2 +- src/global-handles.cc | 184 +++++++++++++++++------------------------------- src/global-handles.h | 39 ++++------ test/cctest/test-api.cc | 9 ++- 6 files changed, 154 insertions(+), 212 deletions(-) diff --git a/include/v8.h b/include/v8.h index 913866b..a87ad01 100644 --- a/include/v8.h +++ b/include/v8.h @@ -137,15 +137,18 @@ class PropertyCallbackArguments; class FunctionCallbackArguments; class GlobalHandles; +template class CallbackData { public: V8_INLINE v8::Isolate* GetIsolate() const { return isolate_; } - protected: - explicit CallbackData(v8::Isolate* isolate) : isolate_(isolate) {} + explicit CallbackData(v8::Isolate* isolate, T* parameter) + : isolate_(isolate), parameter_(parameter) {} + V8_INLINE T* GetParameter() const { return parameter_; } private: v8::Isolate* isolate_; + T* parameter_; }; } @@ -425,23 +428,28 @@ template class Eternal { }; -template -class PhantomCallbackData : public internal::CallbackData { +template +class PhantomCallbackData : public internal::CallbackData { public: - typedef void (*Callback)(const PhantomCallbackData& data); + typedef void (*Callback)(const PhantomCallbackData& data); - V8_INLINE T* GetParameter() const { return parameter_; } + V8_INLINE U* GetInternalField1() const { return internal_field1_; } + V8_INLINE V* GetInternalField2() const { return internal_field2_; } - PhantomCallbackData(Isolate* isolate, T* parameter) - : internal::CallbackData(isolate), parameter_(parameter) {} + PhantomCallbackData(Isolate* isolate, T* parameter, U* internal_field1, + V* internal_field2) + : internal::CallbackData(isolate, parameter), + internal_field1_(internal_field1), + internal_field2_(internal_field2) {} private: - T* parameter_; + U* internal_field1_; + V* internal_field2_; }; template -class WeakCallbackData : public PhantomCallbackData

{ +class WeakCallbackData : public internal::CallbackData

{ public: typedef void (*Callback)(const WeakCallbackData& data); @@ -450,31 +458,11 @@ class WeakCallbackData : public PhantomCallbackData

{ private: friend class internal::GlobalHandles; WeakCallbackData(Isolate* isolate, P* parameter, Local handle) - : PhantomCallbackData

(isolate, parameter), handle_(handle) {} + : internal::CallbackData

(isolate, parameter), handle_(handle) {} Local handle_; }; -template -class InternalFieldsCallbackData : public internal::CallbackData { - public: - typedef void (*Callback)(const InternalFieldsCallbackData& data); - - InternalFieldsCallbackData(Isolate* isolate, T* internalField1, - U* internalField2) - : internal::CallbackData(isolate), - internal_field1_(internalField1), - internal_field2_(internalField2) {} - - V8_INLINE T* GetInternalField1() const { return internal_field1_; } - V8_INLINE U* GetInternalField2() const { return internal_field2_; } - - private: - T* internal_field1_; - U* internal_field2_; -}; - - /** * An object reference that is independent of any handle scope. Where * a Local handle only lives as long as the HandleScope in which it was @@ -562,13 +550,17 @@ template class PersistentBase { // specify a parameter for the callback or the location of two internal // fields in the dying object. template - V8_INLINE void SetPhantom(P* parameter, - typename PhantomCallbackData

::Callback callback); - + V8_INLINE void SetPhantom( + P* parameter, + typename PhantomCallbackData::Callback callback); template V8_INLINE void SetPhantom( - void (*callback)(const InternalFieldsCallbackData&), - int internal_field_index1, int internal_field_index2); + P* parameter, int internal_field_index1, + typename PhantomCallbackData::Callback callback); + template + V8_INLINE void SetPhantom( + P* parameter, int internal_field_index1, int internal_field_index2, + typename PhantomCallbackData::Callback callback); template V8_INLINE P* ClearWeak(); @@ -5595,13 +5587,13 @@ class V8_EXPORT V8 { typedef WeakCallbackData::Callback WeakCallback; static void MakeWeak(internal::Object** global_handle, void* data, WeakCallback weak_callback); - static void MakePhantom(internal::Object** global_handle, void* data, - PhantomCallbackData::Callback weak_callback); static void MakePhantom( - internal::Object** global_handle, - InternalFieldsCallbackData::Callback weak_callback, + internal::Object** global_handle, void* data, + // Must be 0 or kNoInternalFieldIndex. int internal_field_index1, - int internal_field_index2 = Object::kNoInternalFieldIndex); + // Must be 1 or kNoInternalFieldIndex. + int internal_field_index2, + PhantomCallbackData::Callback weak_callback); static void* ClearWeak(internal::Object** global_handle); static void Eternalize(Isolate* isolate, Value* handle, @@ -6489,22 +6481,36 @@ void PersistentBase::SetWeak( template template void PersistentBase::SetPhantom( - P* parameter, typename PhantomCallbackData

::Callback callback) { - typedef typename PhantomCallbackData::Callback Callback; + P* parameter, + typename PhantomCallbackData::Callback callback) { + typedef typename PhantomCallbackData::Callback Callback; + V8::MakePhantom(reinterpret_cast(this->val_), parameter, + Object::kNoInternalFieldIndex, Object::kNoInternalFieldIndex, + reinterpret_cast(callback)); +} + + +template +template +void PersistentBase::SetPhantom( + P* parameter, int internal_field_index1, + typename PhantomCallbackData::Callback callback) { + typedef typename PhantomCallbackData::Callback Callback; V8::MakePhantom(reinterpret_cast(this->val_), parameter, + internal_field_index1, Object::kNoInternalFieldIndex, reinterpret_cast(callback)); } template -template +template void PersistentBase::SetPhantom( - void (*callback)(const InternalFieldsCallbackData&), - int internal_field_index1, int internal_field_index2) { - typedef typename InternalFieldsCallbackData::Callback Callback; - V8::MakePhantom(reinterpret_cast(this->val_), - reinterpret_cast(callback), internal_field_index1, - internal_field_index2); + P* parameter, int internal_field_index1, int internal_field_index2, + typename PhantomCallbackData::Callback callback) { + typedef typename PhantomCallbackData::Callback Callback; + V8::MakePhantom(reinterpret_cast(this->val_), parameter, + internal_field_index1, internal_field_index2, + reinterpret_cast(callback)); } diff --git a/src/api.cc b/src/api.cc index fc528b6..51cd6c8 100644 --- a/src/api.cc +++ b/src/api.cc @@ -408,18 +408,22 @@ void V8::MakeWeak(i::Object** object, void* parameter, } -void V8::MakePhantom(i::Object** object, void* parameter, - PhantomCallbackData::Callback weak_callback) { - i::GlobalHandles::MakePhantom(object, parameter, weak_callback); -} - - void V8::MakePhantom( - i::Object** object, - InternalFieldsCallbackData::Callback weak_callback, - int internal_field_index1, int internal_field_index2) { - i::GlobalHandles::MakePhantom(object, weak_callback, internal_field_index1, - internal_field_index2); + i::Object** object, void* parameter, int internal_field_index1, + int internal_field_index2, + PhantomCallbackData::Callback weak_callback) { + if (internal_field_index1 == 0) { + if (internal_field_index2 == 1) { + i::GlobalHandles::MakePhantom(object, parameter, 2, weak_callback); + } else { + DCHECK_EQ(internal_field_index2, Object::kNoInternalFieldIndex); + i::GlobalHandles::MakePhantom(object, parameter, 1, weak_callback); + } + } else { + DCHECK_EQ(internal_field_index1, Object::kNoInternalFieldIndex); + DCHECK_EQ(internal_field_index2, Object::kNoInternalFieldIndex); + i::GlobalHandles::MakePhantom(object, parameter, 0, weak_callback); + } } diff --git a/src/debug.cc b/src/debug.cc index 93ef1cf..e3cadda 100644 --- a/src/debug.cc +++ b/src/debug.cc @@ -716,7 +716,7 @@ DebugInfoListNode::DebugInfoListNode(DebugInfo* debug_info): next_(NULL) { debug_info_ = Handle::cast(global_handles->Create(debug_info)); typedef PhantomCallbackData::Callback Callback; GlobalHandles::MakePhantom( - reinterpret_cast(debug_info_.location()), this, + reinterpret_cast(debug_info_.location()), this, 0, reinterpret_cast(Debug::HandlePhantomDebugInfo)); } diff --git a/src/global-handles.cc b/src/global-handles.cc index 4f744d6..d717c48 100644 --- a/src/global-handles.cc +++ b/src/global-handles.cc @@ -203,7 +203,6 @@ class GlobalHandles::Node { // Callback parameter accessors. void set_parameter(void* parameter) { DCHECK(IsInUse()); - DCHECK(weakness_type() == NORMAL_WEAK || weakness_type() == PHANTOM_WEAK); parameter_or_next_free_.parameter = parameter; } void* parameter() const { @@ -211,30 +210,6 @@ class GlobalHandles::Node { return parameter_or_next_free_.parameter; } - void set_internal_fields(int internal_field_index1, - int internal_field_index2) { - DCHECK(weakness_type() == INTERNAL_FIELDS_WEAK); - // These are stored in an int16_t. - DCHECK(internal_field_index1 < 1 << 16); - DCHECK(internal_field_index1 >= -(1 << 16)); - DCHECK(internal_field_index2 < 1 << 16); - DCHECK(internal_field_index2 >= -(1 << 16)); - parameter_or_next_free_.internal_field_indeces.internal_field1 = - static_cast(internal_field_index1); - parameter_or_next_free_.internal_field_indeces.internal_field2 = - static_cast(internal_field_index2); - } - - int internal_field1() const { - DCHECK(weakness_type() == INTERNAL_FIELDS_WEAK); - return parameter_or_next_free_.internal_field_indeces.internal_field1; - } - - int internal_field2() const { - DCHECK(weakness_type() == INTERNAL_FIELDS_WEAK); - return parameter_or_next_free_.internal_field_indeces.internal_field2; - } - // Accessors for next free node in the free list. Node* next_free() { DCHECK(state() == FREE); @@ -255,23 +230,22 @@ class GlobalHandles::Node { weak_callback_ = weak_callback; } - void MakePhantom(void* parameter, - PhantomCallbackData::Callback phantom_callback, - int16_t internal_field_index1, - int16_t internal_field_index2) { + void MakePhantom(void* parameter, int number_of_internal_fields, + PhantomCallbackData::Callback phantom_callback) { + DCHECK(number_of_internal_fields >= 0); + DCHECK(number_of_internal_fields <= 2); DCHECK(phantom_callback != NULL); DCHECK(IsInUse()); CHECK(object_ != NULL); set_state(WEAK); - if (parameter == NULL) { - set_weakness_type(INTERNAL_FIELDS_WEAK); - set_internal_fields(internal_field_index1, internal_field_index2); + if (number_of_internal_fields == 0) { + set_weakness_type(PHANTOM_WEAK_0_INTERNAL_FIELDS); + } else if (number_of_internal_fields == 1) { + set_weakness_type(PHANTOM_WEAK_1_INTERNAL_FIELDS); } else { - DCHECK(internal_field_index1 == v8::Object::kNoInternalFieldIndex); - DCHECK(internal_field_index2 == v8::Object::kNoInternalFieldIndex); - set_weakness_type(PHANTOM_WEAK); - set_parameter(parameter); + set_weakness_type(PHANTOM_WEAK_2_INTERNAL_FIELDS); } + set_parameter(parameter); weak_callback_ = reinterpret_cast(phantom_callback); } @@ -284,63 +258,51 @@ class GlobalHandles::Node { } void CollectPhantomCallbackData( - Isolate* isolate, List* pending_phantom_callbacks, - List* pending_internal_fields_callbacks) { - if (state() != Node::PENDING) return; - bool do_release = true; + Isolate* isolate, + List* pending_phantom_callbacks) { + if (state() != PENDING) return; if (weak_callback_ != NULL) { if (weakness_type() == NORMAL_WEAK) return; v8::Isolate* api_isolate = reinterpret_cast(isolate); - if (weakness_type() == PHANTOM_WEAK) { - // Phantom weak pointer case. Zap with harmless value. - DCHECK(*location() == Smi::FromInt(0)); - typedef PhantomCallbackData Data; + DCHECK(weakness_type() == PHANTOM_WEAK_0_INTERNAL_FIELDS || + weakness_type() == PHANTOM_WEAK_1_INTERNAL_FIELDS || + weakness_type() == PHANTOM_WEAK_2_INTERNAL_FIELDS); - Data data(api_isolate, parameter()); - Data::Callback callback = - reinterpret_cast(weak_callback_); - - pending_phantom_callbacks->Add( - PendingPhantomCallback(this, data, callback)); - - // Postpone the release of the handle. The embedder can't use the - // handle (it's zapped), but it may be using the location, and we - // don't want to confuse things by reusing that. - do_release = false; - } else { - DCHECK(weakness_type() == INTERNAL_FIELDS_WEAK); - typedef InternalFieldsCallbackData Data; - - // Phantom weak pointer case, passing internal fields instead of - // parameter. Don't use a handle here during GC, because it will - // create a handle pointing to a dying object, which can confuse - // the next GC. + Object* internal_field0 = nullptr; + Object* internal_field1 = nullptr; + if (weakness_type() != PHANTOM_WEAK_0_INTERNAL_FIELDS) { JSObject* jsobject = reinterpret_cast(object()); DCHECK(jsobject->IsJSObject()); - Data data(api_isolate, jsobject->GetInternalField(internal_field1()), - jsobject->GetInternalField(internal_field2())); - Data::Callback callback = - reinterpret_cast(weak_callback_); - - // In the future, we want to delay the callback. In that case we will - // zap when we queue up, to stop the C++ side accessing the dead V8 - // object, but we will call Release only after the callback (allowing - // the node to be reused). - pending_internal_fields_callbacks->Add( - PendingInternalFieldsCallback(data, callback)); + DCHECK(jsobject->GetInternalFieldCount() >= 1); + internal_field0 = jsobject->GetInternalField(0); + if (weakness_type() == PHANTOM_WEAK_2_INTERNAL_FIELDS) { + DCHECK(jsobject->GetInternalFieldCount() >= 2); + internal_field1 = jsobject->GetInternalField(1); + } } + + // Zap with harmless value. + *location() = Smi::FromInt(0); + typedef PhantomCallbackData Data; + + if (!internal_field0->IsSmi()) internal_field0 = nullptr; + if (!internal_field1->IsSmi()) internal_field1 = nullptr; + + Data data(api_isolate, parameter(), internal_field0, internal_field1); + Data::Callback callback = + reinterpret_cast(weak_callback_); + + pending_phantom_callbacks->Add( + PendingPhantomCallback(this, data, callback)); + DCHECK(IsInUse()); + set_state(NEAR_DEATH); } - // TODO(erikcorry): At the moment the callbacks are not postponed much, - // but if we really postpone them until after the mutator has run, we - // need to divide things up, so that an early callback clears the handle, - // while a later one destroys the objects involved, possibley triggering - // some work when decremented ref counts hit zero. - if (do_release) Release(); } bool PostGarbageCollectionProcessing(Isolate* isolate) { + // Handles only weak handles (not phantom) that are dying. if (state() != Node::PENDING) return false; if (weak_callback_ == NULL) { Release(); @@ -354,11 +316,11 @@ class GlobalHandles::Node { ExternalOneByteString::cast(object_)->resource() != NULL); DCHECK(!object_->IsExternalTwoByteString() || ExternalTwoByteString::cast(object_)->resource() != NULL); + if (weakness_type() != NORMAL_WEAK) return false; + // Leaving V8. VMState vmstate(isolate); HandleScope handle_scope(isolate); - if (weakness_type() == PHANTOM_WEAK) return false; - DCHECK(weakness_type() == NORMAL_WEAK); Object** object = location(); Handle handle(*object, isolate); v8::WeakCallbackData data( @@ -410,10 +372,6 @@ class GlobalHandles::Node { // the free list link. union { void* parameter; - struct { - int16_t internal_field1; - int16_t internal_field2; - } internal_field_indeces; Node* next_free; } parameter_or_next_free_; @@ -604,32 +562,21 @@ void GlobalHandles::MakeWeak(Object** location, void* parameter, } -typedef PhantomCallbackData::Callback GenericCallback; - - -void GlobalHandles::MakePhantom( - Object** location, - v8::InternalFieldsCallbackData::Callback phantom_callback, - int16_t internal_field_index1, int16_t internal_field_index2) { - Node::FromLocation(location) - ->MakePhantom(NULL, reinterpret_cast(phantom_callback), - internal_field_index1, internal_field_index2); -} +typedef PhantomCallbackData::Callback GenericCallback; void GlobalHandles::MakePhantom(Object** location, void* parameter, + int number_of_internal_fields, GenericCallback phantom_callback) { - Node::FromLocation(location)->MakePhantom(parameter, phantom_callback, - v8::Object::kNoInternalFieldIndex, - v8::Object::kNoInternalFieldIndex); + Node::FromLocation(location) + ->MakePhantom(parameter, number_of_internal_fields, phantom_callback); } void GlobalHandles::CollectPhantomCallbackData() { for (NodeIterator it(this); !it.done(); it.Advance()) { Node* node = it.node(); - node->CollectPhantomCallbackData(isolate(), &pending_phantom_callbacks_, - &pending_internal_fields_callbacks_); + node->CollectPhantomCallbackData(isolate(), &pending_phantom_callbacks_); } } @@ -668,22 +615,22 @@ void GlobalHandles::IterateWeakRoots(ObjectVisitor* v) { for (NodeIterator it(this); !it.done(); it.Advance()) { Node* node = it.node(); if (node->IsWeakRetainer()) { - // Weakness type can be normal, phantom or internal fields. - // For normal weakness we mark through the handle so that - // the object and things reachable from it are available - // to the callback. - // In the case of phantom we can zap the object handle now - // and we won't need it, so we don't need to mark through it. + // Weakness type can be normal or phantom, with or without internal + // fields). For normal weakness we mark through the handle so that the + // object and things reachable from it are available to the callback. + // + // In the case of phantom with no internal fields, we can zap the object + // handle now and we won't need it, so we don't need to mark through it. // In the internal fields case we will need the internal - // fields, so we can't zap the handle, but we don't need to - // mark through it, because it will die in this GC round. + // fields, so we can't zap the handle. if (node->state() == Node::PENDING) { - if (node->weakness_type() == PHANTOM_WEAK) { + if (node->weakness_type() == PHANTOM_WEAK_0_INTERNAL_FIELDS) { *(node->location()) = Smi::FromInt(0); } else if (node->weakness_type() == NORMAL_WEAK) { v->VisitPointer(node->location()); } else { - DCHECK(node->weakness_type() == INTERNAL_FIELDS_WEAK); + DCHECK(node->weakness_type() == PHANTOM_WEAK_1_INTERNAL_FIELDS || + node->weakness_type() == PHANTOM_WEAK_2_INTERNAL_FIELDS); } } else { // Node is not pending, so that means the object survived. We still @@ -736,12 +683,13 @@ void GlobalHandles::IterateNewSpaceWeakIndependentRoots(ObjectVisitor* v) { DCHECK(node->is_in_new_space_list()); if ((node->is_independent() || node->is_partially_dependent()) && node->IsWeakRetainer()) { - if (node->weakness_type() == PHANTOM_WEAK) { + if (node->weakness_type() == PHANTOM_WEAK_0_INTERNAL_FIELDS) { *(node->location()) = Smi::FromInt(0); } else if (node->weakness_type() == NORMAL_WEAK) { v->VisitPointer(node->location()); } else { - DCHECK(node->weakness_type() == INTERNAL_FIELDS_WEAK); + DCHECK(node->weakness_type() == PHANTOM_WEAK_1_INTERNAL_FIELDS || + node->weakness_type() == PHANTOM_WEAK_2_INTERNAL_FIELDS); // For this case we only need to trace if it's alive: The tracing of // something that is already alive is just to get the pointer updated // to the new location of the object). @@ -886,13 +834,9 @@ int GlobalHandles::DispatchPendingPhantomCallbacks() { int freed_nodes = 0; while (pending_phantom_callbacks_.length() != 0) { PendingPhantomCallback callback = pending_phantom_callbacks_.RemoveLast(); + DCHECK(callback.node()->IsInUse()); callback.invoke(); - freed_nodes++; - } - while (pending_internal_fields_callbacks_.length() != 0) { - PendingInternalFieldsCallback callback = - pending_internal_fields_callbacks_.RemoveLast(); - callback.invoke(); + DCHECK(!callback.node()->IsInUse()); freed_nodes++; } return freed_nodes; diff --git a/src/global-handles.h b/src/global-handles.h index b3756d0..fe9ed5a 100644 --- a/src/global-handles.h +++ b/src/global-handles.h @@ -98,9 +98,15 @@ struct ObjectGroupRetainerInfo { enum WeaknessType { - NORMAL_WEAK, // Embedder gets a handle to the dying object. - PHANTOM_WEAK, // Embedder gets the parameter they passed in earlier. - INTERNAL_FIELDS_WEAK // Embedder gets 2 internal fields from dying object. + NORMAL_WEAK, // Embedder gets a handle to the dying object. + // In the following cases, the embedder gets the parameter they passed in + // earlier, and the 0, 1 or 2 first internal fields. Note that the internal + // fields must contain aligned non-V8 pointers. Getting pointers to V8 + // objects through this interface would be GC unsafe so in that case the + // embedder gets a null pointer instead. + PHANTOM_WEAK_0_INTERNAL_FIELDS, + PHANTOM_WEAK_1_INTERNAL_FIELDS, + PHANTOM_WEAK_2_INTERNAL_FIELDS }; @@ -139,14 +145,9 @@ class GlobalHandles { // It would be nice to template this one, but it's really hard to get // the template instantiator to work right if you do. - static void MakePhantom(Object** location, void* parameter, - PhantomCallbackData::Callback weak_callback); - static void MakePhantom( - Object** location, - v8::InternalFieldsCallbackData::Callback weak_callback, - int16_t internal_field_index1, - int16_t internal_field_index2 = v8::Object::kNoInternalFieldIndex); + Object** location, void* parameter, int number_of_internal_fields, + PhantomCallbackData::Callback weak_callback); void RecordStats(HeapStats* stats); @@ -302,7 +303,6 @@ class GlobalHandles { class NodeBlock; class NodeIterator; class PendingPhantomCallback; - class PendingInternalFieldsCallback; Isolate* isolate_; @@ -336,7 +336,6 @@ class GlobalHandles { List implicit_ref_connections_; List pending_phantom_callbacks_; - List pending_internal_fields_callbacks_; friend class Isolate; @@ -346,7 +345,7 @@ class GlobalHandles { class GlobalHandles::PendingPhantomCallback { public: - typedef PhantomCallbackData Data; + typedef PhantomCallbackData Data; PendingPhantomCallback(Node* node, Data data, Data::Callback callback) : node_(node), data_(data), callback_(callback) {} @@ -361,20 +360,6 @@ class GlobalHandles::PendingPhantomCallback { }; -class GlobalHandles::PendingInternalFieldsCallback { - public: - typedef InternalFieldsCallbackData Data; - PendingInternalFieldsCallback(Data data, Data::Callback callback) - : data_(data), callback_(callback) {} - - void invoke() { callback_(data_); } - - private: - Data data_; - Data::Callback callback_; -}; - - class EternalHandles { public: enum SingletonHandle { diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc index d18059c..e62b0ed 100644 --- a/test/cctest/test-api.cc +++ b/test/cctest/test-api.cc @@ -7796,8 +7796,10 @@ class Trivial2 { }; -void CheckInternalFields( - const v8::InternalFieldsCallbackData& data) { +void CheckInternalFields(const v8::PhantomCallbackData< + v8::Persistent, Trivial, Trivial2>& data) { + v8::Persistent* handle = data.GetParameter(); + handle->Reset(); Trivial* t1 = data.GetInternalField1(); Trivial2* t2 = data.GetInternalField2(); CHECK_EQ(42, t1->x()); @@ -7835,7 +7837,8 @@ void InternalFieldCallback(bool global_gc) { reinterpret_cast(obj->GetAlignedPointerFromInternalField(1)); CHECK_EQ(103, t2->x()); - handle.SetPhantom(CheckInternalFields, 0, 1); + handle.SetPhantom, Trivial, Trivial2>( + &handle, 0, 1, CheckInternalFields); if (!global_gc) { handle.MarkIndependent(); } -- 2.7.4