Fix Objective-C weak refs to Kotlin cyclic garbage going to be collected
Also make the implementation more reliable.
This commit is contained in:
committed by
SvyatoslavScherbina
parent
bcaa99cbbe
commit
c3f11e62a5
@@ -805,7 +805,7 @@ func testWeakRefs0(frozen: Bool) throws {
|
||||
try test1()
|
||||
try test2()
|
||||
try test3()
|
||||
// try test4()
|
||||
try test4()
|
||||
}
|
||||
|
||||
var falseFlag = false
|
||||
|
||||
@@ -989,6 +989,11 @@ void traverseStronglyConnectedComponent(ContainerHeader* start,
|
||||
}
|
||||
}
|
||||
|
||||
template <bool Atomic>
|
||||
inline bool tryIncrementRC(ContainerHeader* container) {
|
||||
return container->tryIncRefCount<Atomic>();
|
||||
}
|
||||
|
||||
#if !USE_GC
|
||||
|
||||
template <bool Atomic>
|
||||
@@ -1377,6 +1382,29 @@ inline void addHeapRef(const ObjHeader* header) {
|
||||
addHeapRef(const_cast<ContainerHeader*>(container));
|
||||
}
|
||||
|
||||
inline bool tryAddHeapRef(ContainerHeader* container) {
|
||||
switch (container->tag()) {
|
||||
case CONTAINER_TAG_STACK:
|
||||
break;
|
||||
case CONTAINER_TAG_LOCAL:
|
||||
if (!tryIncrementRC</* Atomic = */ false>(container)) return false;
|
||||
break;
|
||||
/* case CONTAINER_TAG_FROZEN: case CONTAINER_TAG_SHARED: */
|
||||
default:
|
||||
if (!tryIncrementRC</* Atomic = */ true>(container)) return false;
|
||||
break;
|
||||
}
|
||||
|
||||
MEMORY_LOG("AddHeapRef %p: rc=%d\n", container, container->refCount() - 1)
|
||||
UPDATE_ADDREF_STAT(memoryState, container, needAtomicAccess(container), 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool tryAddHeapRef(const ObjHeader* header) {
|
||||
auto* container = header->container();
|
||||
return (container != nullptr) ? tryAddHeapRef(container) : true;
|
||||
}
|
||||
|
||||
template <bool Strict>
|
||||
inline void releaseHeapRef(ContainerHeader* container) {
|
||||
MEMORY_LOG("ReleaseHeapRef %p: rc=%d\n", container, container->refCount())
|
||||
@@ -2601,6 +2629,10 @@ ArrayHeader* ArenaContainer::PlaceArray(const TypeInfo* type_info, uint32_t coun
|
||||
extern "C" {
|
||||
|
||||
// Private memory interface.
|
||||
bool TryAddHeapRef(const ObjHeader* object) {
|
||||
return tryAddHeapRef(object);
|
||||
}
|
||||
|
||||
void ReleaseHeapRefStrict(const ObjHeader* object) {
|
||||
releaseHeapRef<true>(const_cast<ObjHeader*>(object));
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "KAssert.h"
|
||||
#include "Common.h"
|
||||
#include "TypeInfo.h"
|
||||
#include "Atomic.h"
|
||||
|
||||
typedef enum {
|
||||
// Those bit masks are applied to refCount_ field.
|
||||
@@ -136,6 +137,32 @@ struct ContainerHeader {
|
||||
#endif
|
||||
}
|
||||
|
||||
template <bool Atomic>
|
||||
inline bool tryIncRefCount() {
|
||||
if (Atomic) {
|
||||
while (true) {
|
||||
uint32_t currentRefCount_ = refCount_;
|
||||
if (((int)currentRefCount_ >> CONTAINER_TAG_SHIFT) > 0) {
|
||||
if (compareAndSet(&refCount_, currentRefCount_, currentRefCount_ + CONTAINER_TAG_INCREMENT)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Note: tricky case here is doing this during cycle collection.
|
||||
// This can actually happen due to deallocation hooks.
|
||||
// Fortunately by this point reference counts have been made precise again.
|
||||
if (refCount() > 0) {
|
||||
incRefCount</* Atomic = */ false>();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <bool Atomic>
|
||||
inline int decRefCount() {
|
||||
#ifdef KONAN_NO_THREADS
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
|
||||
extern "C" {
|
||||
|
||||
bool TryAddHeapRef(const ObjHeader* object);
|
||||
|
||||
MODEL_VARIANTS(void, ReleaseHeapRef, const ObjHeader* object);
|
||||
|
||||
void DeinitInstanceBody(const TypeInfo* typeInfo, void* body);
|
||||
|
||||
@@ -75,14 +75,15 @@ void BackRefFromAssociatedObject::addRef() {
|
||||
}
|
||||
|
||||
bool BackRefFromAssociatedObject::tryAddRef() {
|
||||
// Suboptimal but simple:
|
||||
ObjHeader* obj = this->ref();
|
||||
if (!obj->has_meta_object()) {
|
||||
// Then object is being deallocated.
|
||||
return false;
|
||||
} else {
|
||||
this->addRef();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!TryAddHeapRef(obj)) return false;
|
||||
this->addRef();
|
||||
ReleaseHeapRef(obj); // Balance TryAddHeapRef.
|
||||
// TODO: consider optimizing for non-shared objects.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void BackRefFromAssociatedObject::releaseRef() {
|
||||
|
||||
Reference in New Issue
Block a user