[K/N] Immediately destroy objects that finalize only via ExtraObjectData ^KT-63423

This commit is contained in:
Alexander Shabalin
2024-01-11 16:47:44 +01:00
committed by Space Cloud
parent 3c897ab20c
commit 30550a6da1
12 changed files with 65 additions and 26 deletions
@@ -15,5 +15,6 @@ namespace kotlin::alloc::test_support {
void assertClear(Allocator& allocator) noexcept; void assertClear(Allocator& allocator) noexcept;
std::vector<ObjHeader*> allocatedObjects(mm::ThreadData& threadData) noexcept; std::vector<ObjHeader*> allocatedObjects(mm::ThreadData& threadData) noexcept;
void detachAndDestroyExtraObjectData(mm::ExtraObjectData& extraObject) noexcept;
} // namespace kotlin::alloc::test_support } // namespace kotlin::alloc::test_support
@@ -33,3 +33,10 @@ void alloc::test_support::assertClear(Allocator& allocator) noexcept {
std::vector<ObjHeader*> alloc::test_support::allocatedObjects(mm::ThreadData& threadData) noexcept { std::vector<ObjHeader*> alloc::test_support::allocatedObjects(mm::ThreadData& threadData) noexcept {
return threadData.allocator().impl().alloc().heap().GetAllocatedObjects(); return threadData.allocator().impl().alloc().heap().GetAllocatedObjects();
} }
void alloc::test_support::detachAndDestroyExtraObjectData(mm::ExtraObjectData& extraObject) noexcept {
extraObject.ClearRegularWeakReferenceImpl();
extraObject.UnlinkFromBaseObject();
destroyExtraObjectData(extraObject);
extraObject.setFlag(mm::ExtraObjectData::FLAGS_SWEEPABLE);
}
@@ -23,8 +23,13 @@ struct FinalizerQueueTraits {
static void process(FinalizerQueue queue) noexcept { static void process(FinalizerQueue queue) noexcept {
while (auto* cell = queue.Pop()) { while (auto* cell = queue.Pop()) {
auto* extraObject = cell->Data(); auto* extraObject = cell->Data();
auto* baseObject = extraObject->GetBaseObject(); if (auto* baseObject = extraObject->GetBaseObject()) {
RunFinalizers(baseObject); RunFinalizers(baseObject);
} else {
// This `ExtraObjectData` does not have an object attached. This means
// that the only finalization step is destroying it.
destroyExtraObjectData(*extraObject);
}
} }
} }
}; };
@@ -20,6 +20,7 @@
#include "CustomLogging.hpp" #include "CustomLogging.hpp"
#include "ExtraObjectData.hpp" #include "ExtraObjectData.hpp"
#include "ExtraObjectPage.hpp" #include "ExtraObjectPage.hpp"
#include "FinalizerHooks.hpp"
#include "GC.hpp" #include "GC.hpp"
#include "GCStatistics.hpp" #include "GCStatistics.hpp"
#include "KAssert.h" #include "KAssert.h"
@@ -49,9 +50,17 @@ bool SweepObject(uint8_t* object, FinalizerQueue& finalizerQueue, gc::GCHandle::
extraObject->ClearRegularWeakReferenceImpl(); extraObject->ClearRegularWeakReferenceImpl();
CustomAllocDebug("SweepObject: fromExtraObject(%p) = %p", extraObject, ExtraObjectCell::fromExtraObject(extraObject)); CustomAllocDebug("SweepObject: fromExtraObject(%p) = %p", extraObject, ExtraObjectCell::fromExtraObject(extraObject));
finalizerQueue.Push(ExtraObjectCell::fromExtraObject(extraObject)); finalizerQueue.Push(ExtraObjectCell::fromExtraObject(extraObject));
gcHandle.addMarkedObject(); if (HasFinalizersDataInObject(heapObjHeader->object())) {
gcHandle.addKeptObject(size); // The object must survive until the finalizers for it are finished.
return true; gcHandle.addMarkedObject();
gcHandle.addKeptObject(size);
return true;
}
// The object has a finalizer, but all the data for it resides in `ExtraObjectData`. So, detach the object from it, and free it.
extraObject->UnlinkFromBaseObject();
CustomAllocDebug("SweepObject(%p): can be reclaimed", heapObjHeader);
gcHandle.addSweptObject();
return false;
} }
if (!extraObject->getFlag(mm::ExtraObjectData::FLAGS_FINALIZED)) { if (!extraObject->getFlag(mm::ExtraObjectData::FLAGS_FINALIZED)) {
CustomAllocDebug("SweepObject(%p): already waiting to be finalized", heapObjHeader); CustomAllocDebug("SweepObject(%p): already waiting to be finalized", heapObjHeader);
@@ -51,3 +51,8 @@ std::vector<ObjHeader*> alloc::test_support::allocatedObjects(mm::ThreadData& th
} }
return objects; return objects;
} }
void alloc::test_support::detachAndDestroyExtraObjectData(mm::ExtraObjectData& extraObject) noexcept {
extraObject.ClearRegularWeakReferenceImpl();
destroyExtraObjectData(extraObject);
}
@@ -30,6 +30,14 @@ public:
// when it's asked by GC to stop. // when it's asked by GC to stop.
using Producer::Publish; using Producer::Publish;
void ClearForTests() noexcept {
forEachNode([](auto& extraObject) noexcept {
extraObject.ClearRegularWeakReferenceImpl();
extraObject.Uninstall();
});
Producer::ClearForTests();
}
// Do not add fields as this is just a wrapper and Producer does not have virtual destructor. // Do not add fields as this is just a wrapper and Producer does not have virtual destructor.
}; };
@@ -43,7 +51,13 @@ public:
// Lock registry for safe iteration. // Lock registry for safe iteration.
Iterable LockForIter() noexcept { return extraObjects_.LockForIter(); } Iterable LockForIter() noexcept { return extraObjects_.LockForIter(); }
void ClearForTests() noexcept { extraObjects_.ClearForTests(); } void ClearForTests() noexcept {
for (auto& extraObject : extraObjects_.LockForIter()) {
extraObject.ClearRegularWeakReferenceImpl();
extraObject.Uninstall();
}
extraObjects_.ClearForTests();
}
size_t GetSizeUnsafe() noexcept { return extraObjects_.GetSizeUnsafe(); } size_t GetSizeUnsafe() noexcept { return extraObjects_.GetSizeUnsafe(); }
@@ -1234,17 +1234,6 @@ TYPED_TEST_P(TracingGCTest, WeakResuractionInMark) {
for (auto& future : mutatorFutures) { for (auto& future : mutatorFutures) {
future.wait(); future.wait();
} }
for (int i = 0; i < kDefaultThreadCount; ++i) {
mutators[i].Execute([&, i](mm::ThreadData&, Mutator&) noexcept {
if (auto weakReferee = weaks[i]->get()) {
auto& extraObj = *mm::ExtraObjectData::Get(weakReferee);
extraObj.ClearRegularWeakReferenceImpl();
extraObj.Uninstall();
alloc::destroyExtraObjectData(extraObj);
}
}).wait();
}
} }
#define TRACING_GC_TEST_LIST \ #define TRACING_GC_TEST_LIST \
@@ -1356,11 +1345,6 @@ TYPED_TEST_P(STWMarkGCTest, MultipleMutatorsWeakNewObj) {
})(); })();
EXPECT_NE(objectWeak.get(), nullptr); EXPECT_NE(objectWeak.get(), nullptr);
auto& extraObj = *mm::ExtraObjectData::Get(object.header());
extraObj.ClearRegularWeakReferenceImpl();
extraObj.Uninstall();
alloc::destroyExtraObjectData(extraObj);
while (!gcDone.load(std::memory_order_relaxed)) { while (!gcDone.load(std::memory_order_relaxed)) {
mm::safePoint(threadData); mm::safePoint(threadData);
} }
@@ -35,7 +35,11 @@ NO_INLINE void RunFinalizerHooksImpl(ObjHeader* object, const TypeInfo* type) no
} // namespace } // namespace
ALWAYS_INLINE bool kotlin::HasFinalizers(ObjHeader* object) noexcept { ALWAYS_INLINE bool kotlin::HasFinalizers(ObjHeader* object) noexcept {
return object->has_meta_object() || (object->type_info()->flags_ & TF_HAS_FINALIZER) != 0; return object->has_meta_object() || HasFinalizersDataInObject(object);
}
ALWAYS_INLINE bool kotlin::HasFinalizersDataInObject(ObjHeader* object) noexcept {
return (object->type_info()->flags_ & TF_HAS_FINALIZER) != 0;
} }
ALWAYS_INLINE void kotlin::RunFinalizers(ObjHeader* object) noexcept { ALWAYS_INLINE void kotlin::RunFinalizers(ObjHeader* object) noexcept {
@@ -14,6 +14,7 @@ namespace kotlin {
// finalizer must never try to reference them. // finalizer must never try to reference them.
bool HasFinalizers(ObjHeader* object) noexcept; bool HasFinalizers(ObjHeader* object) noexcept;
bool HasFinalizersDataInObject(ObjHeader* object) noexcept;
void RunFinalizers(ObjHeader* object) noexcept; void RunFinalizers(ObjHeader* object) noexcept;
void SetFinalizerHookForTesting(void (*hook)(ObjHeader*)) noexcept; void SetFinalizerHookForTesting(void (*hook)(ObjHeader*)) noexcept;
@@ -102,6 +102,12 @@ public:
deletionQueue_.clear(); deletionQueue_.clear();
} }
protected:
template <typename F>
void forEachNode(F&& f) noexcept(noexcept(f(std::declval<T&>()))) {
for (auto& node : queue_) f(*node);
}
private: private:
MultiSourceQueue& owner_; // weak MultiSourceQueue& owner_; // weak
List<Node> queue_; List<Node> queue_;
@@ -40,7 +40,10 @@ mm::ExtraObjectData& mm::ExtraObjectData::Install(ObjHeader* object) noexcept {
} }
void mm::ExtraObjectData::UnlinkFromBaseObject() noexcept { void mm::ExtraObjectData::UnlinkFromBaseObject() noexcept {
auto *object = GetBaseObject(); auto* object = weakReferenceOrBaseObject_.exchange(nullptr);
RuntimeAssert(
!hasPointerBits(object, WEAK_REF_TAG), "ExtraObjectData %p has uncleared weak reference %p during unlink", this,
clearPointerBits(object, WEAK_REF_TAG));
atomicSetRelease(const_cast<const TypeInfo**>(&object->typeInfoOrMeta_), typeInfo_); atomicSetRelease(const_cast<const TypeInfo**>(&object->typeInfoOrMeta_), typeInfo_);
RuntimeAssert( RuntimeAssert(
!object->has_meta_object(), "Object %p has metaobject %p after removing metaobject %p", object, object->meta_object_or_null(), !object->has_meta_object(), "Object %p has metaobject %p after removing metaobject %p", object, object->meta_object_or_null(),
@@ -10,6 +10,7 @@
#include "gmock/gmock.h" #include "gmock/gmock.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "AllocatorTestSupport.hpp"
#include "ObjectTestSupport.hpp" #include "ObjectTestSupport.hpp"
#include "ScopedThread.hpp" #include "ScopedThread.hpp"
#include "TestSupport.hpp" #include "TestSupport.hpp"
@@ -51,8 +52,7 @@ TEST_F(ExtraObjectDataTest, Install) {
EXPECT_FALSE(extraData.HasRegularWeakReferenceImpl()); EXPECT_FALSE(extraData.HasRegularWeakReferenceImpl());
EXPECT_THAT(extraData.GetBaseObject(), object.header()); EXPECT_THAT(extraData.GetBaseObject(), object.header());
extraData.Uninstall(); alloc::test_support::detachAndDestroyExtraObjectData(extraData);
mm::GlobalData::Instance().threadRegistry().CurrentThreadData()->ClearForTests();
EXPECT_FALSE(object.header()->has_meta_object()); EXPECT_FALSE(object.header()->has_meta_object());
EXPECT_THAT(object.header()->type_info(), typeInfo); EXPECT_THAT(object.header()->type_info(), typeInfo);