Extract finalizer hooks (#4723)
(cherry picked from commit 31aa3521925a22f077acb723315aabb0a7274121)
This commit is contained in:
committed by
Space
parent
7bfcf815ba
commit
248e340cd9
@@ -39,6 +39,7 @@
|
||||
#include "CyclicCollector.h"
|
||||
#endif // USE_CYCLIC_GC
|
||||
#include "Exceptions.h"
|
||||
#include "FinalizerHooks.hpp"
|
||||
#include "FreezeHooks.hpp"
|
||||
#include "KString.h"
|
||||
#include "Memory.h"
|
||||
@@ -1198,27 +1199,11 @@ void freeAggregatingFrozenContainer(ContainerHeader* container) {
|
||||
MEMORY_LOG("Freeing subcontainers done\n");
|
||||
}
|
||||
|
||||
// Not inlining this call as it affects deallocation performance for
|
||||
// all types.
|
||||
NO_INLINE RUNTIME_NOTHROW void runFinalizers(ObjHeader* obj) {
|
||||
auto* type_info = obj->type_info();
|
||||
if (type_info == theCleanerImplTypeInfo) {
|
||||
DisposeCleaner(obj);
|
||||
}
|
||||
if (type_info == theWorkerBoundReferenceTypeInfo) {
|
||||
DisposeWorkerBoundReference(obj);
|
||||
}
|
||||
}
|
||||
|
||||
// This is called from 2 places where it's unconditionally called,
|
||||
// so better be inlined.
|
||||
ALWAYS_INLINE void runDeallocationHooks(ContainerHeader* container) {
|
||||
ObjHeader* obj = reinterpret_cast<ObjHeader*>(container + 1);
|
||||
for (uint32_t index = 0; index < container->objectCount(); index++) {
|
||||
auto* type_info = obj->type_info();
|
||||
if ((type_info->flags_ & TF_HAS_FINALIZER) != 0) {
|
||||
runFinalizers(obj);
|
||||
}
|
||||
#if USE_CYCLIC_GC
|
||||
if ((type_info->flags_ & TF_LEAK_DETECTOR_CANDIDATE) != 0) {
|
||||
cyclicRemoveAtomicRoot(obj);
|
||||
@@ -1227,9 +1212,7 @@ ALWAYS_INLINE void runDeallocationHooks(ContainerHeader* container) {
|
||||
#if USE_CYCLE_DETECTOR
|
||||
CycleDetector::removeCandidateIfNeeded(obj);
|
||||
#endif // USE_CYCLE_DETECTOR
|
||||
if (obj->has_meta_object()) {
|
||||
ObjHeader::destroyMetaObject(obj);
|
||||
}
|
||||
kotlin::RunFinalizers(obj);
|
||||
obj = reinterpret_cast<ObjHeader*>(reinterpret_cast<uintptr_t>(obj) + objectSize(obj));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +107,6 @@ TEST_F(KonanAllocatorAwareTest, PlacementAllocated) {
|
||||
EXPECT_THAT(a->value(), 42);
|
||||
EXPECT_CALL(*destructorHook, Call(42));
|
||||
a->~A();
|
||||
testing::Mock::VerifyAndClearExpectations(destructorHook.get());
|
||||
}
|
||||
|
||||
TEST_F(KonanAllocatorAwareTest, PlacementConstructedArray) {
|
||||
@@ -131,5 +130,4 @@ TEST_F(KonanAllocatorAwareTest, PlacementConstructedArray) {
|
||||
for (A* a = as; a != as + kCount; ++a) {
|
||||
a->~A();
|
||||
}
|
||||
testing::Mock::VerifyAndClearExpectations(destructorHook.get());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "FinalizerHooks.hpp"
|
||||
|
||||
#include "Cleaner.h"
|
||||
#include "Memory.h"
|
||||
#include "Types.h"
|
||||
#include "WorkerBoundReference.h"
|
||||
|
||||
using namespace kotlin;
|
||||
|
||||
namespace {
|
||||
|
||||
void (*g_hookOverrideForTesting)(ObjHeader*) = nullptr;
|
||||
|
||||
// Not inlining this call as it affects deallocation performance for
|
||||
// all types.
|
||||
NO_INLINE void RunFinalizerHooksImpl(ObjHeader* object, const TypeInfo* type) noexcept {
|
||||
if (g_hookOverrideForTesting != nullptr) {
|
||||
g_hookOverrideForTesting(object);
|
||||
return;
|
||||
}
|
||||
// TODO: Consider some global registration.
|
||||
if (type == theCleanerImplTypeInfo) {
|
||||
DisposeCleaner(object);
|
||||
} else if (type == theWorkerBoundReferenceTypeInfo) {
|
||||
DisposeWorkerBoundReference(object);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ALWAYS_INLINE bool kotlin::HasFinalizers(ObjHeader* object) noexcept {
|
||||
return object->has_meta_object() || (object->type_info()->flags_ & TF_HAS_FINALIZER) != 0;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void kotlin::RunFinalizers(ObjHeader* object) noexcept {
|
||||
auto* type = object->type_info();
|
||||
if ((type->flags_ & TF_HAS_FINALIZER) != 0) {
|
||||
// This is a cold path.
|
||||
RunFinalizerHooksImpl(object, type);
|
||||
}
|
||||
if (object->has_meta_object()) {
|
||||
ObjHeader::destroyMetaObject(object);
|
||||
}
|
||||
}
|
||||
|
||||
void kotlin::SetFinalizerHookForTesting(void (*hook)(ObjHeader*)) noexcept {
|
||||
g_hookOverrideForTesting = hook;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef RUNTIME_MM_FINALIZER_HOOKS_H
|
||||
#define RUNTIME_MM_FINALIZER_HOOKS_H
|
||||
|
||||
struct ObjHeader;
|
||||
|
||||
namespace kotlin {
|
||||
|
||||
bool HasFinalizers(ObjHeader* object) noexcept;
|
||||
void RunFinalizers(ObjHeader* object) noexcept;
|
||||
|
||||
void SetFinalizerHookForTesting(void (*hook)(ObjHeader*)) noexcept;
|
||||
|
||||
} // namespace kotlin
|
||||
|
||||
#endif // RUNTIME_MM_FINALIZER_HOOKS_H
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "FinalizerHooks.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "gmock/gmock.h"
|
||||
|
||||
#include "FinalizerHooksTestSupport.hpp"
|
||||
#include "Memory.h"
|
||||
|
||||
using namespace kotlin;
|
||||
|
||||
using ::testing::_;
|
||||
|
||||
namespace {
|
||||
|
||||
class FinalizerHooksTest : public testing::Test {
|
||||
public:
|
||||
testing::MockFunction<void(ObjHeader*)>& finalizerHook() { return finalizerHooks_.finalizerHook(); }
|
||||
|
||||
private:
|
||||
FinalizerHooksTestSupport finalizerHooks_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_F(FinalizerHooksTest, TypeWithFinalizerHookWithoutExtra) {
|
||||
TypeInfo type;
|
||||
type.typeInfo_ = &type;
|
||||
type.flags_ |= TF_HAS_FINALIZER;
|
||||
ObjHeader obj = {&type};
|
||||
ASSERT_FALSE(obj.has_meta_object());
|
||||
|
||||
EXPECT_TRUE(HasFinalizers(&obj));
|
||||
EXPECT_CALL(finalizerHook(), Call(&obj));
|
||||
RunFinalizers(&obj);
|
||||
EXPECT_FALSE(obj.has_meta_object());
|
||||
}
|
||||
|
||||
TEST_F(FinalizerHooksTest, TypeWithFinalizerHookWithExtra) {
|
||||
TypeInfo type;
|
||||
type.typeInfo_ = &type;
|
||||
type.flags_ |= TF_HAS_FINALIZER;
|
||||
ObjHeader obj = {&type};
|
||||
ObjHeader::createMetaObject(&obj);
|
||||
ASSERT_TRUE(obj.has_meta_object());
|
||||
|
||||
EXPECT_TRUE(HasFinalizers(&obj));
|
||||
EXPECT_CALL(finalizerHook(), Call(&obj));
|
||||
RunFinalizers(&obj);
|
||||
EXPECT_FALSE(obj.has_meta_object());
|
||||
}
|
||||
|
||||
TEST_F(FinalizerHooksTest, TypeWithoutFinalizerHookWithoutExtra) {
|
||||
TypeInfo type;
|
||||
type.typeInfo_ = &type;
|
||||
type.flags_ &= ~TF_HAS_FINALIZER;
|
||||
ObjHeader obj = {&type};
|
||||
ASSERT_FALSE(obj.has_meta_object());
|
||||
|
||||
EXPECT_FALSE(HasFinalizers(&obj));
|
||||
EXPECT_CALL(finalizerHook(), Call(_)).Times(0);
|
||||
RunFinalizers(&obj);
|
||||
EXPECT_FALSE(obj.has_meta_object());
|
||||
}
|
||||
|
||||
TEST_F(FinalizerHooksTest, TypeWithoutFinalizerHookWithExtra) {
|
||||
TypeInfo type;
|
||||
type.typeInfo_ = &type;
|
||||
type.flags_ &= ~TF_HAS_FINALIZER;
|
||||
ObjHeader obj = {&type};
|
||||
ObjHeader::createMetaObject(&obj);
|
||||
ASSERT_TRUE(obj.has_meta_object());
|
||||
|
||||
EXPECT_TRUE(HasFinalizers(&obj));
|
||||
EXPECT_CALL(finalizerHook(), Call(_)).Times(0);
|
||||
RunFinalizers(&obj);
|
||||
EXPECT_FALSE(obj.has_meta_object());
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "FinalizerHooksTestSupport.hpp"
|
||||
|
||||
#include "FinalizerHooks.hpp"
|
||||
|
||||
using namespace kotlin;
|
||||
|
||||
namespace {
|
||||
|
||||
testing::MockFunction<void(ObjHeader*)>* g_finalizerHook = nullptr;
|
||||
|
||||
void finalizerHook(ObjHeader* object) {
|
||||
g_finalizerHook->Call(object);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
kotlin::FinalizerHooksTestSupport::FinalizerHooksTestSupport() {
|
||||
g_finalizerHook = &finalizerHook_;
|
||||
SetFinalizerHookForTesting(&::finalizerHook);
|
||||
}
|
||||
|
||||
kotlin::FinalizerHooksTestSupport::~FinalizerHooksTestSupport() {
|
||||
SetFinalizerHookForTesting(nullptr);
|
||||
g_finalizerHook = nullptr;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef RUNTIME_MM_FINALIZER_HOOKS_TEST_SUPPORT_H
|
||||
#define RUNTIME_MM_FINALIZER_HOOKS_TEST_SUPPORT_H
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "gmock/gmock.h"
|
||||
|
||||
struct ObjHeader;
|
||||
|
||||
namespace kotlin {
|
||||
|
||||
class FinalizerHooksTestSupport {
|
||||
public:
|
||||
FinalizerHooksTestSupport();
|
||||
~FinalizerHooksTestSupport();
|
||||
|
||||
testing::MockFunction<void(ObjHeader*)>& finalizerHook() { return finalizerHook_; }
|
||||
|
||||
private:
|
||||
testing::StrictMock<testing::MockFunction<void(ObjHeader*)>> finalizerHook_;
|
||||
};
|
||||
|
||||
} // namespace kotlin
|
||||
|
||||
#endif // RUNTIME_MM_FINALIZER_HOOKS_TEST_SUPPORT_H
|
||||
@@ -34,7 +34,6 @@ TEST_F(FreezeHooksTest, TypeWithFreezeHook) {
|
||||
ObjHeader obj = {&type};
|
||||
EXPECT_CALL(freezeHook(), Call(&obj));
|
||||
RunFreezeHooks(&obj);
|
||||
testing::Mock::VerifyAndClearExpectations(&freezeHook());
|
||||
}
|
||||
|
||||
TEST_F(FreezeHooksTest, TypeWithoutFreezeHook) {
|
||||
@@ -44,5 +43,4 @@ TEST_F(FreezeHooksTest, TypeWithoutFreezeHook) {
|
||||
ObjHeader obj = {&type};
|
||||
EXPECT_CALL(freezeHook(), Call(_)).Times(0);
|
||||
RunFreezeHooks(&obj);
|
||||
testing::Mock::VerifyAndClearExpectations(&freezeHook());
|
||||
}
|
||||
|
||||
@@ -342,5 +342,4 @@ TEST(SingleLockListTest, Destructor) {
|
||||
EXPECT_CALL(hook, Call(first));
|
||||
}
|
||||
}
|
||||
testing::Mock::VerifyAndClear(&hook);
|
||||
}
|
||||
|
||||
@@ -39,7 +39,6 @@ public:
|
||||
|
||||
RuntimeCheck(*globalMockLocation_ == mock_.get(), "unexpected global mock location");
|
||||
|
||||
testing::Mock::VerifyAndClear(mock_.get());
|
||||
mock_.reset();
|
||||
|
||||
*globalMockLocation_ = nullptr;
|
||||
|
||||
Reference in New Issue
Block a user