diff --git a/backend.native/tests/framework/values/values.swift b/backend.native/tests/framework/values/values.swift index 69f6183795e..be34a30bee1 100644 --- a/backend.native/tests/framework/values/values.swift +++ b/backend.native/tests/framework/values/values.swift @@ -805,7 +805,7 @@ func testWeakRefs0(frozen: Bool) throws { try test1() try test2() try test3() - // try test4() + try test4() } var falseFlag = false diff --git a/runtime/src/main/cpp/Memory.cpp b/runtime/src/main/cpp/Memory.cpp index 187e3738343..896fb662b57 100644 --- a/runtime/src/main/cpp/Memory.cpp +++ b/runtime/src/main/cpp/Memory.cpp @@ -989,6 +989,11 @@ void traverseStronglyConnectedComponent(ContainerHeader* start, } } +template +inline bool tryIncrementRC(ContainerHeader* container) { + return container->tryIncRefCount(); +} + #if !USE_GC template @@ -1377,6 +1382,29 @@ inline void addHeapRef(const ObjHeader* header) { addHeapRef(const_cast(container)); } +inline bool tryAddHeapRef(ContainerHeader* container) { + switch (container->tag()) { + case CONTAINER_TAG_STACK: + break; + case CONTAINER_TAG_LOCAL: + if (!tryIncrementRC(container)) return false; + break; + /* case CONTAINER_TAG_FROZEN: case CONTAINER_TAG_SHARED: */ + default: + if (!tryIncrementRC(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 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(const_cast(object)); } diff --git a/runtime/src/main/cpp/Memory.h b/runtime/src/main/cpp/Memory.h index 9e644628f35..fdb7dfca0db 100644 --- a/runtime/src/main/cpp/Memory.h +++ b/runtime/src/main/cpp/Memory.h @@ -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 + 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(); + return true; + } else { + return false; + } + } + } + template inline int decRefCount() { #ifdef KONAN_NO_THREADS diff --git a/runtime/src/main/cpp/MemoryPrivate.hpp b/runtime/src/main/cpp/MemoryPrivate.hpp index 5f28c17b8ba..aa43e61ca97 100644 --- a/runtime/src/main/cpp/MemoryPrivate.hpp +++ b/runtime/src/main/cpp/MemoryPrivate.hpp @@ -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); diff --git a/runtime/src/main/cpp/MemorySharedRefs.cpp b/runtime/src/main/cpp/MemorySharedRefs.cpp index 719d9c2e3ee..fab850cf73a 100644 --- a/runtime/src/main/cpp/MemorySharedRefs.cpp +++ b/runtime/src/main/cpp/MemorySharedRefs.cpp @@ -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() {