Fix missing dealloc hooks on object freed by cycle collector. (#1487)

This commit is contained in:
Nikolay Igotti
2018-04-12 18:40:07 +03:00
committed by GitHub
parent 1aabf3697d
commit c5c2a080d8
4 changed files with 42 additions and 34 deletions
+5
View File
@@ -2081,6 +2081,11 @@ task memory_weak0(type: RunKonanTest) {
source = "runtime/memory/weak0.kt" source = "runtime/memory/weak0.kt"
} }
task memory_weak1(type: RunKonanTest) {
goldValue = "OK\n"
source = "runtime/memory/weak1.kt"
}
task mpp1(type: RunStandaloneKonanTest) { task mpp1(type: RunStandaloneKonanTest) {
source = "codegen/mpp/mpp1.kt" source = "codegen/mpp/mpp1.kt"
flags = ['-tr', '-Xmulti-platform'] flags = ['-tr', '-Xmulti-platform']
+1 -1
View File
@@ -50,7 +50,7 @@ fun run() {
// hashCode (directly): // hashCode (directly):
if (foo.hashCode() == foo.hash().let { it.toInt() xor (it shr 32).toInt() }) { if (foo.hashCode() == foo.hash().let { it.toInt() xor (it shr 32).toInt() }) {
// toString (virtually): // toString (virtually):
println(listOf(foo, pair).map { it.toString() }.min() == foo.description()) println(map.keys.map { it.toString() }.min() == foo.description())
} }
println(globalString) println(globalString)
@@ -0,0 +1,15 @@
package runtime.memory.weak1
import kotlin.test.*
import konan.ref.*
class Node(var next: Node?)
@Test fun runTest() {
val node1 = Node(null)
val node2 = Node(node1)
node1.next = node2
konan.ref.WeakReference(node1)
println("OK")
}
+21 -33
View File
@@ -414,24 +414,20 @@ RUNTIME_NORETURN void ThrowInvalidMutabilityException();
} // extern "C" } // extern "C"
inline void runDeallocationHooks(ObjHeader* obj) {
if (obj->has_meta_object()) {
ObjHeader::destroyMetaObject(&obj->typeInfoOrMeta_);
}
}
inline void runDeallocationHooks(ContainerHeader* container) { inline void runDeallocationHooks(ContainerHeader* container) {
ObjHeader* obj = reinterpret_cast<ObjHeader*>(container + 1); ObjHeader* obj = reinterpret_cast<ObjHeader*>(container + 1);
for (int index = 0; index < container->objectCount(); index++) { for (int index = 0; index < container->objectCount(); index++) {
runDeallocationHooks(obj); if (obj->has_meta_object()) {
ObjHeader::destroyMetaObject(&obj->typeInfoOrMeta_);
}
obj = reinterpret_cast<ObjHeader*>( obj = reinterpret_cast<ObjHeader*>(
reinterpret_cast<uintptr_t>(obj) + objectSize(obj)); reinterpret_cast<uintptr_t>(obj) + objectSize(obj));
} }
} }
static inline void DeinitInstanceBodyImpl(const TypeInfo* typeInfo, void* body) { void DeinitInstanceBody(const TypeInfo* typeInfo, void* body) {
for (int index = 0; index < typeInfo->objOffsetsCount_; index++) { for (int index = 0; index < typeInfo->objOffsetsCount_; index++) {
ObjHeader** location = reinterpret_cast<ObjHeader**>( ObjHeader** location = reinterpret_cast<ObjHeader**>(
reinterpret_cast<uintptr_t>(body) + typeInfo->objOffsets_[index]); reinterpret_cast<uintptr_t>(body) + typeInfo->objOffsets_[index]);
@@ -439,10 +435,6 @@ static inline void DeinitInstanceBodyImpl(const TypeInfo* typeInfo, void* body)
} }
} }
void DeinitInstanceBody(const TypeInfo* typeInfo, void* body) {
DeinitInstanceBodyImpl(typeInfo, body);
}
namespace { namespace {
template<typename func> template<typename func>
@@ -500,15 +492,7 @@ inline void processFinalizerQueue(MemoryState* state) {
#endif #endif
inline void scheduleDestroyContainer( inline void scheduleDestroyContainer(
MemoryState* state, ContainerHeader* container, bool clearExternalRefs) { MemoryState* state, ContainerHeader* container) {
if (clearExternalRefs) {
traverseContainerObjectFields(container, [](ObjHeader** location) {
ObjHeader* ref = *location;
// Frozen object references do not participate in trial deletion, so shall be explicitly freed.
if (ref != nullptr && ref->container()->frozen())
UpdateRef(location, nullptr);
});
}
#if USE_GC #if USE_GC
state->finalizerQueue->push_front(container); state->finalizerQueue->push_front(container);
// We cannot clean finalizer queue while in GC. // We cannot clean finalizer queue while in GC.
@@ -517,8 +501,8 @@ inline void scheduleDestroyContainer(
} }
#else #else
atomicAdd(&allocCount, -1); atomicAdd(&allocCount, -1);
CONTAINER_DESTROY_EVENT(state, header) CONTAINER_DESTROY_EVENT(state, container)
konanFreeMemory(header); konanFreeMemory(container);
#endif #endif
} }
@@ -679,7 +663,7 @@ void MarkRoots(MemoryState* state) {
} else { } else {
container->resetBuffered(); container->resetBuffered();
if (color == CONTAINER_TAG_GC_BLACK && rcIsZero) { if (color == CONTAINER_TAG_GC_BLACK && rcIsZero) {
scheduleDestroyContainer(state, container, true); scheduleDestroyContainer(state, container);
} }
} }
} }
@@ -715,18 +699,22 @@ void Scan(ContainerHeader* container) {
} }
void CollectWhite(MemoryState* state, ContainerHeader* container) { void CollectWhite(MemoryState* state, ContainerHeader* container) {
if (container->color() != CONTAINER_TAG_GC_WHITE if (container->color() != CONTAINER_TAG_GC_WHITE || container->buffered())
|| container->buffered())
return; return;
container->setColor(CONTAINER_TAG_GC_BLACK); container->setColor(CONTAINER_TAG_GC_BLACK);
traverseContainerReferredObjects(container, [state](ObjHeader* ref) { traverseContainerObjectFields(container, [state](ObjHeader** location) {
auto ref = *location;
if (ref == nullptr) return;
auto childContainer = ref->container(); auto childContainer = ref->container();
RuntimeAssert(!isArena(childContainer), "A reference to local object is encountered"); RuntimeAssert(!isArena(childContainer), "A reference to local object is encountered");
if (!childContainer->permanentOrFrozen()) { if (childContainer->permanentOrFrozen()) {
UpdateRef(location, nullptr);
} else {
CollectWhite(state, childContainer); CollectWhite(state, childContainer);
} }
}); });
scheduleDestroyContainer(state, container, true); runDeallocationHooks(container);
scheduleDestroyContainer(state, container);
} }
inline void AddRef(ContainerHeader* header) { inline void AddRef(ContainerHeader* header) {
@@ -874,7 +862,7 @@ void FreeAggregatingFrozenContainer(ContainerHeader* container) {
FreeContainer(*subContainer++); FreeContainer(*subContainer++);
} }
--state->finalizerQueueSuspendCount; --state->finalizerQueueSuspendCount;
scheduleDestroyContainer(state, container, false); scheduleDestroyContainer(state, container);
MEMORY_LOG("Freeing subcontainers done\n"); MEMORY_LOG("Freeing subcontainers done\n");
} }
@@ -887,10 +875,10 @@ void FreeContainer(ContainerHeader* container) {
if (isAggregatingFrozenContainer(container)) { if (isAggregatingFrozenContainer(container)) {
FreeAggregatingFrozenContainer(container); FreeAggregatingFrozenContainer(container);
return; return;
} else {
runDeallocationHooks(container);
} }
runDeallocationHooks(container);
// Now let's clean all object's fields in this container. // Now let's clean all object's fields in this container.
traverseContainerObjectFields(container, [](ObjHeader** location) { traverseContainerObjectFields(container, [](ObjHeader** location) {
UpdateRef(location, nullptr); UpdateRef(location, nullptr);
@@ -900,7 +888,7 @@ void FreeContainer(ContainerHeader* container) {
if (isFreeable(container)) { if (isFreeable(container)) {
container->setColor(CONTAINER_TAG_GC_BLACK); container->setColor(CONTAINER_TAG_GC_BLACK);
if (!container->buffered()) if (!container->buffered())
scheduleDestroyContainer(state, container, false); scheduleDestroyContainer(state, container);
} }
} }