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"
|
#include "CyclicCollector.h"
|
||||||
#endif // USE_CYCLIC_GC
|
#endif // USE_CYCLIC_GC
|
||||||
#include "Exceptions.h"
|
#include "Exceptions.h"
|
||||||
|
#include "FinalizerHooks.hpp"
|
||||||
#include "FreezeHooks.hpp"
|
#include "FreezeHooks.hpp"
|
||||||
#include "KString.h"
|
#include "KString.h"
|
||||||
#include "Memory.h"
|
#include "Memory.h"
|
||||||
@@ -1198,27 +1199,11 @@ void freeAggregatingFrozenContainer(ContainerHeader* container) {
|
|||||||
MEMORY_LOG("Freeing subcontainers done\n");
|
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,
|
// This is called from 2 places where it's unconditionally called,
|
||||||
// so better be inlined.
|
// so better be inlined.
|
||||||
ALWAYS_INLINE void runDeallocationHooks(ContainerHeader* container) {
|
ALWAYS_INLINE void runDeallocationHooks(ContainerHeader* container) {
|
||||||
ObjHeader* obj = reinterpret_cast<ObjHeader*>(container + 1);
|
ObjHeader* obj = reinterpret_cast<ObjHeader*>(container + 1);
|
||||||
for (uint32_t index = 0; index < container->objectCount(); index++) {
|
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 USE_CYCLIC_GC
|
||||||
if ((type_info->flags_ & TF_LEAK_DETECTOR_CANDIDATE) != 0) {
|
if ((type_info->flags_ & TF_LEAK_DETECTOR_CANDIDATE) != 0) {
|
||||||
cyclicRemoveAtomicRoot(obj);
|
cyclicRemoveAtomicRoot(obj);
|
||||||
@@ -1227,9 +1212,7 @@ ALWAYS_INLINE void runDeallocationHooks(ContainerHeader* container) {
|
|||||||
#if USE_CYCLE_DETECTOR
|
#if USE_CYCLE_DETECTOR
|
||||||
CycleDetector::removeCandidateIfNeeded(obj);
|
CycleDetector::removeCandidateIfNeeded(obj);
|
||||||
#endif // USE_CYCLE_DETECTOR
|
#endif // USE_CYCLE_DETECTOR
|
||||||
if (obj->has_meta_object()) {
|
kotlin::RunFinalizers(obj);
|
||||||
ObjHeader::destroyMetaObject(obj);
|
|
||||||
}
|
|
||||||
obj = reinterpret_cast<ObjHeader*>(reinterpret_cast<uintptr_t>(obj) + objectSize(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_THAT(a->value(), 42);
|
||||||
EXPECT_CALL(*destructorHook, Call(42));
|
EXPECT_CALL(*destructorHook, Call(42));
|
||||||
a->~A();
|
a->~A();
|
||||||
testing::Mock::VerifyAndClearExpectations(destructorHook.get());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(KonanAllocatorAwareTest, PlacementConstructedArray) {
|
TEST_F(KonanAllocatorAwareTest, PlacementConstructedArray) {
|
||||||
@@ -131,5 +130,4 @@ TEST_F(KonanAllocatorAwareTest, PlacementConstructedArray) {
|
|||||||
for (A* a = as; a != as + kCount; ++a) {
|
for (A* a = as; a != as + kCount; ++a) {
|
||||||
a->~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};
|
ObjHeader obj = {&type};
|
||||||
EXPECT_CALL(freezeHook(), Call(&obj));
|
EXPECT_CALL(freezeHook(), Call(&obj));
|
||||||
RunFreezeHooks(&obj);
|
RunFreezeHooks(&obj);
|
||||||
testing::Mock::VerifyAndClearExpectations(&freezeHook());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(FreezeHooksTest, TypeWithoutFreezeHook) {
|
TEST_F(FreezeHooksTest, TypeWithoutFreezeHook) {
|
||||||
@@ -44,5 +43,4 @@ TEST_F(FreezeHooksTest, TypeWithoutFreezeHook) {
|
|||||||
ObjHeader obj = {&type};
|
ObjHeader obj = {&type};
|
||||||
EXPECT_CALL(freezeHook(), Call(_)).Times(0);
|
EXPECT_CALL(freezeHook(), Call(_)).Times(0);
|
||||||
RunFreezeHooks(&obj);
|
RunFreezeHooks(&obj);
|
||||||
testing::Mock::VerifyAndClearExpectations(&freezeHook());
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -342,5 +342,4 @@ TEST(SingleLockListTest, Destructor) {
|
|||||||
EXPECT_CALL(hook, Call(first));
|
EXPECT_CALL(hook, Call(first));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
testing::Mock::VerifyAndClear(&hook);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ public:
|
|||||||
|
|
||||||
RuntimeCheck(*globalMockLocation_ == mock_.get(), "unexpected global mock location");
|
RuntimeCheck(*globalMockLocation_ == mock_.get(), "unexpected global mock location");
|
||||||
|
|
||||||
testing::Mock::VerifyAndClear(mock_.get());
|
|
||||||
mock_.reset();
|
mock_.reset();
|
||||||
|
|
||||||
*globalMockLocation_ = nullptr;
|
*globalMockLocation_ = nullptr;
|
||||||
|
|||||||
Reference in New Issue
Block a user