Don't allow GC in BackRefFromAssociatedObject::tryAddRef
This function is invoked while a non-recursive os_lock is taken, and GC can provoke Obj-C object deallocation taking the same lock recursively.
This commit is contained in:
committed by
GitHub
parent
93df6c45ac
commit
34cd218e4a
@@ -0,0 +1,6 @@
|
|||||||
|
#import <Foundation/NSObject.h>
|
||||||
|
|
||||||
|
@interface WeakRefHolder : NSObject
|
||||||
|
@property (weak) id obj;
|
||||||
|
-(void)loadManyTimes;
|
||||||
|
@end;
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import kotlinx.cinterop.*
|
||||||
|
import kotlin.test.*
|
||||||
|
import objcTests.*
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testTryRetainGC() {
|
||||||
|
kotlin.native.internal.GC.collect()
|
||||||
|
val weakRefHolder = WeakRefHolder()
|
||||||
|
createGarbageNSObjects(weakRefHolder)
|
||||||
|
weakRefHolder.obj = object : NSObject() {}
|
||||||
|
// Loading weak ref takes a lock. If K/N runtime runs GC while the lock is taken,
|
||||||
|
// then it releases garbage objects and thus Obj-C runtime might take a recursive lock
|
||||||
|
// and abort in _os_unfair_lock_recursive_abort.
|
||||||
|
weakRefHolder.loadManyTimes()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createGarbageNSObjects(weakRefHolder: WeakRefHolder) {
|
||||||
|
autoreleasepool {
|
||||||
|
repeat(100) {
|
||||||
|
// Assigning the object to a weak reference so Obj-C would take a lock when deallocating it:
|
||||||
|
weakRefHolder.obj = NSObject()
|
||||||
|
}
|
||||||
|
weakRefHolder.obj = null
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
#import "tryRetainGC.h"
|
||||||
|
#import <Foundation/NSArray.h>
|
||||||
|
|
||||||
|
@implementation WeakRefHolder
|
||||||
|
-(void)loadManyTimes {
|
||||||
|
NSMutableArray* array = [NSMutableArray new];
|
||||||
|
for (int i = 0; i < 10000; ++i) {
|
||||||
|
[array addObject:self.obj];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@end;
|
||||||
@@ -1616,23 +1616,23 @@ inline bool tryAddHeapRef(const ObjHeader* header) {
|
|||||||
return (container != nullptr) ? tryAddHeapRef(container) : true;
|
return (container != nullptr) ? tryAddHeapRef(container) : true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <bool Strict>
|
template <bool Strict, bool CanCollect>
|
||||||
inline void releaseHeapRef(ContainerHeader* container) {
|
inline void releaseHeapRef(ContainerHeader* container) {
|
||||||
MEMORY_LOG("ReleaseHeapRef %p: rc=%d\n", container, container->refCount())
|
MEMORY_LOG("ReleaseHeapRef %p: rc=%d\n", container, container->refCount())
|
||||||
UPDATE_RELEASEREF_STAT(memoryState, container, needAtomicAccess(container), canBeCyclic(container), 0)
|
UPDATE_RELEASEREF_STAT(memoryState, container, needAtomicAccess(container), canBeCyclic(container), 0)
|
||||||
if (container->tag() != CONTAINER_TAG_STACK) {
|
if (container->tag() != CONTAINER_TAG_STACK) {
|
||||||
if (Strict)
|
if (Strict)
|
||||||
enqueueDecrementRC</* CanCollect = */ true>(container);
|
enqueueDecrementRC</* CanCollect = */ CanCollect>(container);
|
||||||
else
|
else
|
||||||
decrementRC(container);
|
decrementRC(container);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <bool Strict>
|
template <bool Strict, bool CanCollect = true>
|
||||||
inline void releaseHeapRef(const ObjHeader* header) {
|
inline void releaseHeapRef(const ObjHeader* header) {
|
||||||
auto* container = header->container();
|
auto* container = header->container();
|
||||||
if (container != nullptr)
|
if (container != nullptr)
|
||||||
releaseHeapRef<Strict>(const_cast<ContainerHeader*>(container));
|
releaseHeapRef<Strict, CanCollect>(const_cast<ContainerHeader*>(container));
|
||||||
}
|
}
|
||||||
|
|
||||||
// We use first slot as place to store frame-local arena container.
|
// We use first slot as place to store frame-local arena container.
|
||||||
@@ -3135,6 +3135,13 @@ void ReleaseHeapRefRelaxed(const ObjHeader* object) {
|
|||||||
releaseHeapRef<false>(const_cast<ObjHeader*>(object));
|
releaseHeapRef<false>(const_cast<ObjHeader*>(object));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ReleaseHeapRefNoCollectStrict(const ObjHeader* object) {
|
||||||
|
releaseHeapRef<true, /* CanCollect = */ false>(const_cast<ObjHeader*>(object));
|
||||||
|
}
|
||||||
|
void ReleaseHeapRefNoCollectRelaxed(const ObjHeader* object) {
|
||||||
|
releaseHeapRef<false, /* CanCollect = */ false>(const_cast<ObjHeader*>(object));
|
||||||
|
}
|
||||||
|
|
||||||
ForeignRefContext InitLocalForeignRef(ObjHeader* object) {
|
ForeignRefContext InitLocalForeignRef(ObjHeader* object) {
|
||||||
return initLocalForeignRef(object);
|
return initLocalForeignRef(object);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ extern "C" {
|
|||||||
bool TryAddHeapRef(const ObjHeader* object);
|
bool TryAddHeapRef(const ObjHeader* object);
|
||||||
|
|
||||||
MODEL_VARIANTS(void, ReleaseHeapRef, const ObjHeader* object);
|
MODEL_VARIANTS(void, ReleaseHeapRef, const ObjHeader* object);
|
||||||
|
MODEL_VARIANTS(void, ReleaseHeapRefNoCollect, const ObjHeader* object);
|
||||||
|
|
||||||
void Kotlin_ObjCExport_releaseAssociatedObject(void* associatedObject);
|
void Kotlin_ObjCExport_releaseAssociatedObject(void* associatedObject);
|
||||||
|
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ bool BackRefFromAssociatedObject::tryAddRef() {
|
|||||||
RuntimeAssert(isForeignRefAccessible(obj_, context_), "Cannot be inaccessible because of the check above");
|
RuntimeAssert(isForeignRefAccessible(obj_, context_), "Cannot be inaccessible because of the check above");
|
||||||
// TODO: This is a very weird way to ask for "unsafe" addRef.
|
// TODO: This is a very weird way to ask for "unsafe" addRef.
|
||||||
addRef<ErrorPolicy::kIgnore>();
|
addRef<ErrorPolicy::kIgnore>();
|
||||||
ReleaseHeapRef(obj); // Balance TryAddHeapRef.
|
ReleaseHeapRefNoCollect(obj); // Balance TryAddHeapRef.
|
||||||
// TODO: consider optimizing for non-shared objects.
|
// TODO: consider optimizing for non-shared objects.
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -33,6 +33,10 @@ void ReleaseHeapRef(const ObjHeader* object) {
|
|||||||
ReleaseHeapRefRelaxed(object);
|
ReleaseHeapRefRelaxed(object);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ReleaseHeapRefNoCollect(const ObjHeader* object) {
|
||||||
|
ReleaseHeapRefNoCollectRelaxed(object);
|
||||||
|
}
|
||||||
|
|
||||||
void ZeroStackRef(ObjHeader** location) {
|
void ZeroStackRef(ObjHeader** location) {
|
||||||
ZeroStackRefRelaxed(location);
|
ZeroStackRefRelaxed(location);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,10 @@ void ReleaseHeapRef(const ObjHeader* object) {
|
|||||||
ReleaseHeapRefStrict(object);
|
ReleaseHeapRefStrict(object);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ReleaseHeapRefNoCollect(const ObjHeader* object) {
|
||||||
|
ReleaseHeapRefNoCollectStrict(object);
|
||||||
|
}
|
||||||
|
|
||||||
void SetStackRef(ObjHeader** location, const ObjHeader* object) {
|
void SetStackRef(ObjHeader** location, const ObjHeader* object) {
|
||||||
SetStackRefStrict(location, object);
|
SetStackRefStrict(location, object);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user