Weak references. (#1459)
This commit is contained in:
@@ -2076,6 +2076,11 @@ task memory_escape2(type: RunKonanTest) {
|
|||||||
source = "runtime/memory/escape2.kt"
|
source = "runtime/memory/escape2.kt"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
task memory_weak0(type: RunKonanTest) {
|
||||||
|
goldValue = "Data(s=Hello)\nnull\nOK\n"
|
||||||
|
source = "runtime/memory/weak0.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']
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package runtime.memory.weak0
|
||||||
|
|
||||||
|
import kotlin.test.*
|
||||||
|
import konan.ref.*
|
||||||
|
|
||||||
|
data class Data(val s: String)
|
||||||
|
|
||||||
|
fun localWeak(): WeakReference<Data> {
|
||||||
|
val x = Data("Hello")
|
||||||
|
val weak = WeakReference(x)
|
||||||
|
|
||||||
|
println(weak.get())
|
||||||
|
return weak
|
||||||
|
}
|
||||||
|
|
||||||
|
fun multiWeak(): Array<WeakReference<Data>> {
|
||||||
|
val x = Data("Hello")
|
||||||
|
val weaks = Array(100, { WeakReference(x) } )
|
||||||
|
weaks.forEach {
|
||||||
|
it -> if (it.get()?.s != "Hello") throw Error("bad reference")
|
||||||
|
}
|
||||||
|
return weaks
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test fun runTest() {
|
||||||
|
val weak = localWeak()
|
||||||
|
val value = weak.get()
|
||||||
|
println(value?.toString())
|
||||||
|
|
||||||
|
val weaks = multiWeak()
|
||||||
|
weaks.forEach {
|
||||||
|
it -> if (it.get()?.s != null) throw Error("not null")
|
||||||
|
}
|
||||||
|
println("OK")
|
||||||
|
}
|
||||||
@@ -464,7 +464,7 @@ void DeinitInstanceBody(const TypeInfo* typeInfo, void* body) {
|
|||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
template<typename func>
|
template<typename func>
|
||||||
void traverseContainerObjectFields(ContainerHeader* container, func process) {
|
inline void traverseContainerObjectFields(ContainerHeader* container, func process) {
|
||||||
ObjHeader* obj = reinterpret_cast<ObjHeader*>(container + 1);
|
ObjHeader* obj = reinterpret_cast<ObjHeader*>(container + 1);
|
||||||
for (int object = 0; object < container->objectCount(); object++) {
|
for (int object = 0; object < container->objectCount(); object++) {
|
||||||
const TypeInfo* typeInfo = obj->type_info();
|
const TypeInfo* typeInfo = obj->type_info();
|
||||||
@@ -485,7 +485,7 @@ void traverseContainerObjectFields(ContainerHeader* container, func process) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename func>
|
template<typename func>
|
||||||
void traverseContainerReferredObjects(ContainerHeader* container, func process) {
|
inline void traverseContainerReferredObjects(ContainerHeader* container, func process) {
|
||||||
traverseContainerObjectFields(container, [process](ObjHeader** location) {
|
traverseContainerObjectFields(container, [process](ObjHeader** location) {
|
||||||
ObjHeader* ref = *location;
|
ObjHeader* ref = *location;
|
||||||
if (ref != nullptr) process(ref);
|
if (ref != nullptr) process(ref);
|
||||||
@@ -510,9 +510,6 @@ inline void processFinalizerQueue(MemoryState* state) {
|
|||||||
#if TRACE_MEMORY
|
#if TRACE_MEMORY
|
||||||
state->containers->erase(container);
|
state->containers->erase(container);
|
||||||
#endif
|
#endif
|
||||||
if (!isAggregatingFrozenContainer(container))
|
|
||||||
runDeallocationHooks(container);
|
|
||||||
|
|
||||||
CONTAINER_DESTROY_EVENT(state, container)
|
CONTAINER_DESTROY_EVENT(state, container)
|
||||||
konanFreeMemory(container);
|
konanFreeMemory(container);
|
||||||
atomicAdd(&allocCount, -1);
|
atomicAdd(&allocCount, -1);
|
||||||
@@ -835,8 +832,12 @@ MetaObjHeader* ObjHeader::createMetaObject(TypeInfo** location) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ObjHeader::destroyMetaObject(TypeInfo** location) {
|
void ObjHeader::destroyMetaObject(TypeInfo** location) {
|
||||||
TypeInfo* meta = *location;
|
MetaObjHeader* meta = *(reinterpret_cast<MetaObjHeader**>(location));
|
||||||
*location = nullptr;
|
*const_cast<const TypeInfo**>(location) = meta->typeInfo_;
|
||||||
|
if (meta->counter_ != nullptr) {
|
||||||
|
WeakReferenceCounterClear(meta->counter_);
|
||||||
|
UpdateRef(&meta->counter_, nullptr);
|
||||||
|
}
|
||||||
konanFreeMemory(meta);
|
konanFreeMemory(meta);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -890,29 +891,29 @@ void FreeAggregatingFrozenContainer(ContainerHeader* container) {
|
|||||||
MEMORY_LOG("Freeing subcontainers done\n");
|
MEMORY_LOG("Freeing subcontainers done\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void FreeContainer(ContainerHeader* header) {
|
void FreeContainer(ContainerHeader* container) {
|
||||||
RuntimeAssert(!header->permanent(), "this kind of container shalln't be freed");
|
RuntimeAssert(!container->permanent(), "this kind of container shalln't be freed");
|
||||||
auto state = memoryState;
|
auto state = memoryState;
|
||||||
|
|
||||||
CONTAINER_FREE_EVENT(state, header)
|
CONTAINER_FREE_EVENT(state, container)
|
||||||
|
|
||||||
if (isAggregatingFrozenContainer(header)) {
|
if (isAggregatingFrozenContainer(container)) {
|
||||||
FreeAggregatingFrozenContainer(header);
|
FreeAggregatingFrozenContainer(container);
|
||||||
return;
|
return;
|
||||||
|
} else {
|
||||||
|
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(header, [](ObjHeader** location) {
|
traverseContainerObjectFields(container, [](ObjHeader** location) {
|
||||||
UpdateRef(location, nullptr);
|
UpdateRef(location, nullptr);
|
||||||
});
|
});
|
||||||
|
|
||||||
// And release underlying memory.
|
// And release underlying memory.
|
||||||
if (!isFreeable(header)) {
|
if (isFreeable(container)) {
|
||||||
runDeallocationHooks(header);
|
container->setColor(CONTAINER_TAG_GC_BLACK);
|
||||||
} else {
|
if (!container->buffered())
|
||||||
header->setColor(CONTAINER_TAG_GC_BLACK);
|
scheduleDestroyContainer(state, container, false);
|
||||||
if (!header->buffered())
|
|
||||||
scheduleDestroyContainer(state, header, false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1232,7 +1233,8 @@ void* GetReservedObjectTail(ObjHeader* obj) {
|
|||||||
void SetRef(ObjHeader** location, const ObjHeader* object) {
|
void SetRef(ObjHeader** location, const ObjHeader* object) {
|
||||||
MEMORY_LOG("SetRef *%p: %p\n", location, object)
|
MEMORY_LOG("SetRef *%p: %p\n", location, object)
|
||||||
*const_cast<const ObjHeader**>(location) = object;
|
*const_cast<const ObjHeader**>(location) = object;
|
||||||
AddRef(object);
|
if (object != nullptr)
|
||||||
|
AddRef(object);
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjHeader** GetReturnSlotIfArena(ObjHeader** returnSlot, ObjHeader** localSlot) {
|
ObjHeader** GetReturnSlotIfArena(ObjHeader** returnSlot, ObjHeader** localSlot) {
|
||||||
@@ -1248,16 +1250,6 @@ ObjHeader** GetParamSlotIfArena(ObjHeader* param, ObjHeader** localSlot) {
|
|||||||
return reinterpret_cast<ObjHeader**>(reinterpret_cast<uintptr_t>(&chunk->arena) | ARENA_BIT);
|
return reinterpret_cast<ObjHeader**>(reinterpret_cast<uintptr_t>(&chunk->arena) | ARENA_BIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdateReturnRef(ObjHeader** returnSlot, const ObjHeader* object) {
|
|
||||||
if (isArenaSlot(returnSlot)) {
|
|
||||||
// Not a subject of reference counting.
|
|
||||||
if (object == nullptr || !isRefCounted(object)) return;
|
|
||||||
auto arena = initedArena(asArenaSlot(returnSlot));
|
|
||||||
returnSlot = arena->getSlot();
|
|
||||||
}
|
|
||||||
UpdateRef(returnSlot, object);
|
|
||||||
}
|
|
||||||
|
|
||||||
void UpdateRef(ObjHeader** location, const ObjHeader* object) {
|
void UpdateRef(ObjHeader** location, const ObjHeader* object) {
|
||||||
RuntimeAssert(!isArenaSlot(location), "must not be a slot");
|
RuntimeAssert(!isArenaSlot(location), "must not be a slot");
|
||||||
ObjHeader* old = *location;
|
ObjHeader* old = *location;
|
||||||
@@ -1273,6 +1265,35 @@ void UpdateRef(ObjHeader** location, const ObjHeader* object) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UpdateReturnRef(ObjHeader** returnSlot, const ObjHeader* object) {
|
||||||
|
if (isArenaSlot(returnSlot)) {
|
||||||
|
// Not a subject of reference counting.
|
||||||
|
if (object == nullptr || !isRefCounted(object)) return;
|
||||||
|
auto arena = initedArena(asArenaSlot(returnSlot));
|
||||||
|
returnSlot = arena->getSlot();
|
||||||
|
}
|
||||||
|
UpdateRef(returnSlot, object);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateRefIfNull(ObjHeader** location, const ObjHeader* object) {
|
||||||
|
if (object != nullptr) {
|
||||||
|
#if KONAN_NO_THREADS
|
||||||
|
ObjHeader* old = *location;
|
||||||
|
if (old == nullptr) {
|
||||||
|
AddRef(object);
|
||||||
|
*const_cast<const ObjHeader**>(location) = object;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
AddRef(object);
|
||||||
|
auto old = __sync_val_compare_and_swap(location, nullptr, const_cast<ObjHeader*>(object));
|
||||||
|
if (old != nullptr) {
|
||||||
|
// Failed to store, was not null.
|
||||||
|
ReleaseRef(object);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void EnterFrame(ObjHeader** start, int parameters, int count) {
|
void EnterFrame(ObjHeader** start, int parameters, int count) {
|
||||||
MEMORY_LOG("EnterFrame %p .. %p\n", start, start + count + parameters)
|
MEMORY_LOG("EnterFrame %p .. %p\n", start, start + count + parameters)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -242,6 +242,8 @@ struct ArrayHeader {
|
|||||||
struct MetaObjHeader {
|
struct MetaObjHeader {
|
||||||
// Pointer to the type info. Must be first, to match ArrayHeader and ObjHeader layout.
|
// Pointer to the type info. Must be first, to match ArrayHeader and ObjHeader layout.
|
||||||
const TypeInfo* typeInfo_;
|
const TypeInfo* typeInfo_;
|
||||||
|
// Strong reference to counter object.
|
||||||
|
ObjHeader* counter_;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline uint32_t ArrayDataSizeBytes(const ArrayHeader* obj) {
|
inline uint32_t ArrayDataSizeBytes(const ArrayHeader* obj) {
|
||||||
@@ -394,6 +396,10 @@ bool HasReservedObjectTail(ObjHeader* obj) RUNTIME_NOTHROW;
|
|||||||
// Returns the pointer to the reserved space, `HasReservedObjectTail(obj)` must be true.
|
// Returns the pointer to the reserved space, `HasReservedObjectTail(obj)` must be true.
|
||||||
void* GetReservedObjectTail(ObjHeader* obj) RUNTIME_NOTHROW;
|
void* GetReservedObjectTail(ObjHeader* obj) RUNTIME_NOTHROW;
|
||||||
|
|
||||||
|
// Weak reference operations.
|
||||||
|
// Atomically clears counter object reference.
|
||||||
|
void WeakReferenceCounterClear(ObjHeader* counter);
|
||||||
|
|
||||||
//
|
//
|
||||||
// Object reference management.
|
// Object reference management.
|
||||||
//
|
//
|
||||||
@@ -420,6 +426,8 @@ void* GetReservedObjectTail(ObjHeader* obj) RUNTIME_NOTHROW;
|
|||||||
void SetRef(ObjHeader** location, const ObjHeader* object) RUNTIME_NOTHROW;
|
void SetRef(ObjHeader** location, const ObjHeader* object) RUNTIME_NOTHROW;
|
||||||
// Updates location.
|
// Updates location.
|
||||||
void UpdateRef(ObjHeader** location, const ObjHeader* object) RUNTIME_NOTHROW;
|
void UpdateRef(ObjHeader** location, const ObjHeader* object) RUNTIME_NOTHROW;
|
||||||
|
// Updates location if it is null, atomically.
|
||||||
|
void UpdateRefIfNull(ObjHeader** location, const ObjHeader* object) RUNTIME_NOTHROW;
|
||||||
// Updates reference in return slot.
|
// Updates reference in return slot.
|
||||||
void UpdateReturnRef(ObjHeader** returnSlot, const ObjHeader* object) RUNTIME_NOTHROW;
|
void UpdateReturnRef(ObjHeader** returnSlot, const ObjHeader* object) RUNTIME_NOTHROW;
|
||||||
// Optimization: release all references in range.
|
// Optimization: release all references in range.
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2018 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
#include "Memory.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// TODO: an ugly hack with fixed offsets.
|
||||||
|
constexpr int referredOffset = 0;
|
||||||
|
constexpr int lockOffset = sizeof(void*);
|
||||||
|
|
||||||
|
#if !KONAN_NO_THREADS
|
||||||
|
|
||||||
|
inline void lock(int32_t* address) {
|
||||||
|
while (__sync_val_compare_and_swap(address, 0, 1) == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void unlock(int32_t* address) {
|
||||||
|
int old = __sync_val_compare_and_swap(address, 1, 0);
|
||||||
|
RuntimeAssert(old == 1, "Incorrect lock state");
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
OBJ_GETTER(makeWeakReferenceCounter, void*);
|
||||||
|
|
||||||
|
// See Weak.kt for implementation details.
|
||||||
|
// Retrieve link on the counter object.
|
||||||
|
OBJ_GETTER(Konan_getWeakReferenceCounter, ObjHeader* referred) {
|
||||||
|
MetaObjHeader* meta = referred->meta_object();
|
||||||
|
if (meta->counter_ == nullptr) {
|
||||||
|
ObjHolder counterHolder;
|
||||||
|
// Cast unneeded, just to emphasize we store an object reference as void*.
|
||||||
|
ObjHeader* counter = makeWeakReferenceCounter(reinterpret_cast<void*>(referred), counterHolder.slot());
|
||||||
|
UpdateRefIfNull(&meta->counter_, counter);
|
||||||
|
}
|
||||||
|
RETURN_OBJ(meta->counter_);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Materialize a weak reference to either null or the real reference.
|
||||||
|
OBJ_GETTER(Konan_WeakReferenceCounter_get, ObjHeader* counter) {
|
||||||
|
ObjHeader** referredAddress = reinterpret_cast<ObjHeader**>(reinterpret_cast<char*>(counter + 1) + referredOffset);
|
||||||
|
#if KONAN_NO_THREADS
|
||||||
|
RETURN_OBJ(*referredAddress);
|
||||||
|
#else
|
||||||
|
int32_t* lockAddress = reinterpret_cast<int32_t*>(reinterpret_cast<char*>(counter + 1) + lockOffset);
|
||||||
|
// Spinlock.
|
||||||
|
lock(lockAddress);
|
||||||
|
ObjHolder holder(*referredAddress);
|
||||||
|
unlock(lockAddress);
|
||||||
|
RETURN_OBJ(holder.obj());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void WeakReferenceCounterClear(ObjHeader* counter) {
|
||||||
|
ObjHeader** referredAddress = reinterpret_cast<ObjHeader**>(reinterpret_cast<char*>(counter + 1) + referredOffset);
|
||||||
|
// Note, that we don't do UpdateRef here, as reference is weak.
|
||||||
|
#if KONAN_NO_THREADS
|
||||||
|
*referredAddress = nullptr;
|
||||||
|
#else
|
||||||
|
int32_t* lockAddress = reinterpret_cast<int32_t*>(reinterpret_cast<char*>(counter + 1) + lockOffset);
|
||||||
|
// Spinlock.
|
||||||
|
lock(lockAddress);
|
||||||
|
*referredAddress = nullptr;
|
||||||
|
unlock(lockAddress);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
} // extern "C"
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2018 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package konan.ref
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class WeakReference encapsulates weak reference to an object, which could be used to either
|
||||||
|
* retrieve a strong reference to an object, or return null, if object was already destoyed by
|
||||||
|
* the memory manager.
|
||||||
|
*/
|
||||||
|
class WeakReference<T> {
|
||||||
|
/**
|
||||||
|
* Creates a weak reference object pointing to an object. Weak reference doesn't prevent
|
||||||
|
* removing object, and is nullified once object is collected.
|
||||||
|
*/
|
||||||
|
constructor(referred: T) {
|
||||||
|
if (referred == null) throw Error("Weak reference to null?")
|
||||||
|
pointer = getWeakReferenceCounter(referred)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Backing store for the object pointer, inaccessible directly.
|
||||||
|
*/
|
||||||
|
@PublishedApi
|
||||||
|
internal var pointer: WeakReferenceCounter?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears reference to an object.
|
||||||
|
*/
|
||||||
|
public fun clear() {
|
||||||
|
pointer = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns either reference to an object or null, if it was collected.
|
||||||
|
*/
|
||||||
|
@Suppress("NON_PUBLIC_CALL_FROM_PUBLIC_INLINE")
|
||||||
|
public inline fun <reified T> WeakReference<T>.get() = pointer?.get() as T?
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2018 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package konan.ref
|
||||||
|
|
||||||
|
import kotlinx.cinterop.COpaquePointer
|
||||||
|
import konan.internal.ExportForCppRuntime
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Theory of operations:
|
||||||
|
*
|
||||||
|
* Weak references in Kotlin/Native are implemented in the following way. Whenever weak reference to an
|
||||||
|
* object is created, we atomically modify type info pointer in the object to point into a metaobject.
|
||||||
|
* This metaobject contains a strong reference to the counter object (instance of WeakReferenceCounter class).
|
||||||
|
* Every other weak reference contains a strong reference to the counter object.
|
||||||
|
*
|
||||||
|
* [weak1] [weak2]
|
||||||
|
* \ /
|
||||||
|
* V V
|
||||||
|
* .......[Counter] <----
|
||||||
|
* . |
|
||||||
|
* . |
|
||||||
|
* ->[Object] -> [Meta]-
|
||||||
|
*
|
||||||
|
* References from weak reference objects to the counter and from the metaobject to the counter are strong,
|
||||||
|
* and from the counter to the object is nullably weak. So whenever an object dies, if it has a metaobject,
|
||||||
|
* it is traversed to find a counter object, and atomically nullify reference to the object. Afterward, all attempts
|
||||||
|
* to get the object would yield null.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Clear holding the counter object, which refers to the actual object.
|
||||||
|
internal class WeakReferenceCounter(var referred: COpaquePointer?) {
|
||||||
|
// Spinlock, potentially taken when materializing or removing 'referred' object.
|
||||||
|
var lock: Int = 0
|
||||||
|
|
||||||
|
@SymbolName("Konan_WeakReferenceCounter_get")
|
||||||
|
internal external fun get(): Any?
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a counter from non-null object.
|
||||||
|
@SymbolName("Konan_getWeakReferenceCounter")
|
||||||
|
external internal fun getWeakReferenceCounter(referent: Any): WeakReferenceCounter
|
||||||
|
|
||||||
|
// Create a counter object.
|
||||||
|
@ExportForCppRuntime
|
||||||
|
internal fun makeWeakReferenceCounter(referred: COpaquePointer) = WeakReferenceCounter(referred)
|
||||||
|
|
||||||
Reference in New Issue
Block a user