Skip to content

Commit 9d2dfc0

Browse files
committed
Merge pull request #1454 from glessard/weakref-threadsafety
[runtime] Thread safety for weak references
2 parents 8ebf7bc + c87309e commit 9d2dfc0

File tree

5 files changed

+369
-52
lines changed

5 files changed

+369
-52
lines changed

include/swift/Runtime/HeapObject.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -634,9 +634,15 @@ static inline void swift_unownedTakeAssign(UnownedReference *dest,
634634

635635
/// A weak reference value object. This is ABI.
636636
struct WeakReference {
637-
HeapObject *Value;
637+
uintptr_t Value;
638638
};
639639

640+
/// Return true if this is a native weak reference
641+
///
642+
/// \param ref - never null
643+
/// \return true if ref is a native weak reference
644+
bool isNativeSwiftWeakReference(WeakReference *ref);
645+
640646
/// Initialize a weak reference.
641647
///
642648
/// \param ref - never null

stdlib/public/runtime/HeapObject.cpp

Lines changed: 91 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -779,73 +779,142 @@ void swift::swift_deallocObject(HeapObject *object,
779779
}
780780
}
781781

782+
enum: uintptr_t {
783+
WR_NATIVE = 1<<(swift::heap_object_abi::ObjCReservedLowBits),
784+
WR_READING = 1<<(swift::heap_object_abi::ObjCReservedLowBits+1),
785+
786+
WR_NATIVEMASK = WR_NATIVE | swift::heap_object_abi::ObjCReservedBitsMask,
787+
};
788+
789+
static_assert(WR_READING < alignof(void*),
790+
"weakref lock bit mustn't interfere with real pointer bits");
791+
792+
enum: short {
793+
WR_SPINLIMIT = 64,
794+
};
795+
796+
bool swift::isNativeSwiftWeakReference(WeakReference *ref) {
797+
return (ref->Value & WR_NATIVEMASK) == WR_NATIVE;
798+
}
799+
782800
void swift::swift_weakInit(WeakReference *ref, HeapObject *value) {
783-
ref->Value = value;
801+
ref->Value = (uintptr_t)value | WR_NATIVE;
784802
SWIFT_RT_ENTRY_CALL(swift_unownedRetain)(value);
785803
}
786804

787805
void swift::swift_weakAssign(WeakReference *ref, HeapObject *newValue) {
788806
SWIFT_RT_ENTRY_CALL(swift_unownedRetain)(newValue);
789-
auto oldValue = ref->Value;
790-
ref->Value = newValue;
807+
auto oldValue = (HeapObject*) (ref->Value & ~WR_NATIVE);
808+
ref->Value = (uintptr_t)newValue | WR_NATIVE;
791809
SWIFT_RT_ENTRY_CALL(swift_unownedRelease)(oldValue);
792810
}
793811

794812
HeapObject *swift::swift_weakLoadStrong(WeakReference *ref) {
795-
auto object = ref->Value;
796-
if (object == nullptr) return nullptr;
813+
if (ref->Value == (uintptr_t)nullptr) {
814+
return nullptr;
815+
}
816+
817+
// ref might be visible to other threads
818+
auto ptr = __atomic_fetch_or(&ref->Value, WR_READING, __ATOMIC_RELAXED);
819+
while (ptr & WR_READING) {
820+
short c = 0;
821+
while (__atomic_load_n(&ref->Value, __ATOMIC_RELAXED) & WR_READING) {
822+
if (++c == WR_SPINLIMIT) {
823+
sched_yield();
824+
c -= 1;
825+
}
826+
}
827+
ptr = __atomic_fetch_or(&ref->Value, WR_READING, __ATOMIC_RELAXED);
828+
}
829+
830+
auto object = (HeapObject*)(ptr & ~WR_NATIVE);
831+
if (object == nullptr) {
832+
__atomic_store_n(&ref->Value, (uintptr_t)nullptr, __ATOMIC_RELAXED);
833+
return nullptr;
834+
}
797835
if (object->refCount.isDeallocating()) {
836+
__atomic_store_n(&ref->Value, (uintptr_t)nullptr, __ATOMIC_RELAXED);
798837
SWIFT_RT_ENTRY_CALL(swift_unownedRelease)(object);
799-
ref->Value = nullptr;
800838
return nullptr;
801839
}
802-
return swift_tryRetain(object);
840+
auto result = swift_tryRetain(object);
841+
__atomic_store_n(&ref->Value, ptr, __ATOMIC_RELAXED);
842+
return result;
803843
}
804844

805845
HeapObject *swift::swift_weakTakeStrong(WeakReference *ref) {
806-
auto result = swift_weakLoadStrong(ref);
807-
swift_weakDestroy(ref);
846+
auto object = (HeapObject*) (ref->Value & ~WR_NATIVE);
847+
if (object == nullptr) return nullptr;
848+
auto result = swift_tryRetain(object);
849+
ref->Value = (uintptr_t)nullptr;
850+
swift_unownedRelease(object);
808851
return result;
809852
}
810853

811854
void swift::swift_weakDestroy(WeakReference *ref) {
812-
auto tmp = ref->Value;
813-
ref->Value = nullptr;
855+
auto tmp = (HeapObject*) (ref->Value & ~WR_NATIVE);
856+
ref->Value = (uintptr_t)nullptr;
814857
SWIFT_RT_ENTRY_CALL(swift_unownedRelease)(tmp);
815858
}
816859

817860
void swift::swift_weakCopyInit(WeakReference *dest, WeakReference *src) {
818-
auto object = src->Value;
861+
if (src->Value == (uintptr_t)nullptr) {
862+
dest->Value = (uintptr_t)nullptr;
863+
return;
864+
}
865+
866+
// src might be visible to other threads
867+
auto ptr = __atomic_fetch_or(&src->Value, WR_READING, __ATOMIC_RELAXED);
868+
while (ptr & WR_READING) {
869+
short c = 0;
870+
while (__atomic_load_n(&src->Value, __ATOMIC_RELAXED) & WR_READING) {
871+
if (++c == WR_SPINLIMIT) {
872+
sched_yield();
873+
c -= 1;
874+
}
875+
}
876+
ptr = __atomic_fetch_or(&src->Value, WR_READING, __ATOMIC_RELAXED);
877+
}
878+
879+
auto object = (HeapObject*)(ptr & ~WR_NATIVE);
819880
if (object == nullptr) {
820-
dest->Value = nullptr;
881+
__atomic_store_n(&src->Value, (uintptr_t)nullptr, __ATOMIC_RELAXED);
882+
dest->Value = (uintptr_t)nullptr;
821883
} else if (object->refCount.isDeallocating()) {
822-
src->Value = nullptr;
823-
dest->Value = nullptr;
884+
__atomic_store_n(&src->Value, (uintptr_t)nullptr, __ATOMIC_RELAXED);
824885
SWIFT_RT_ENTRY_CALL(swift_unownedRelease)(object);
886+
dest->Value = (uintptr_t)nullptr;
825887
} else {
826-
dest->Value = object;
827888
SWIFT_RT_ENTRY_CALL(swift_unownedRetain)(object);
889+
__atomic_store_n(&src->Value, ptr, __ATOMIC_RELAXED);
890+
dest->Value = (uintptr_t)object | WR_NATIVE;
828891
}
829892
}
830893

831894
void swift::swift_weakTakeInit(WeakReference *dest, WeakReference *src) {
832-
auto object = src->Value;
833-
dest->Value = object;
834-
if (object != nullptr && object->refCount.isDeallocating()) {
835-
dest->Value = nullptr;
895+
auto object = (HeapObject*) (src->Value & ~WR_NATIVE);
896+
if (object == nullptr) {
897+
dest->Value = (uintptr_t)nullptr;
898+
} else if (object->refCount.isDeallocating()) {
899+
dest->Value = (uintptr_t)nullptr;
836900
SWIFT_RT_ENTRY_CALL(swift_unownedRelease)(object);
901+
} else {
902+
dest->Value = (uintptr_t)object | WR_NATIVE;
837903
}
904+
src->Value = (uintptr_t)nullptr;
838905
}
839906

840907
void swift::swift_weakCopyAssign(WeakReference *dest, WeakReference *src) {
841-
if (auto object = dest->Value) {
908+
if (dest->Value) {
909+
auto object = (HeapObject*) (dest->Value & ~WR_NATIVE);
842910
SWIFT_RT_ENTRY_CALL(swift_unownedRelease)(object);
843911
}
844912
swift_weakCopyInit(dest, src);
845913
}
846914

847915
void swift::swift_weakTakeAssign(WeakReference *dest, WeakReference *src) {
848-
if (auto object = dest->Value) {
916+
if (dest->Value) {
917+
auto object = (HeapObject*) (dest->Value & ~WR_NATIVE);
849918
SWIFT_RT_ENTRY_CALL(swift_unownedRelease)(object);
850919
}
851920
swift_weakTakeInit(dest, src);

stdlib/public/runtime/SwiftObject.mm

Lines changed: 38 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1025,7 +1025,7 @@ static void doWeakDestroy(WeakReference *addr, bool valueIsNative) {
10251025

10261026
void swift::swift_unknownWeakInit(WeakReference *addr, void *value) {
10271027
if (isObjCTaggedPointerOrNull(value)) {
1028-
addr->Value = (HeapObject*) value;
1028+
addr->Value = (uintptr_t) value;
10291029
return;
10301030
}
10311031
doWeakInit(addr, value, usesNativeSwiftReferenceCounting_allocated(value));
@@ -1036,18 +1036,18 @@ static void doWeakDestroy(WeakReference *addr, bool valueIsNative) {
10361036
// and re-initialize.
10371037
if (isObjCTaggedPointerOrNull(newValue)) {
10381038
swift_unknownWeakDestroy(addr);
1039-
addr->Value = (HeapObject*) newValue;
1039+
addr->Value = (uintptr_t) newValue;
10401040
return;
10411041
}
10421042

10431043
bool newIsNative = usesNativeSwiftReferenceCounting_allocated(newValue);
10441044

10451045
// If the existing value is not allocated, this is just an initialize.
1046-
void *oldValue = addr->Value;
1046+
void *oldValue = (void*) addr->Value;
10471047
if (isObjCTaggedPointerOrNull(oldValue))
10481048
return doWeakInit(addr, newValue, newIsNative);
10491049

1050-
bool oldIsNative = usesNativeSwiftReferenceCounting_allocated(oldValue);
1050+
bool oldIsNative = isNativeSwiftWeakReference(addr);
10511051

10521052
// If they're both native, we can use the native function.
10531053
if (oldIsNative && newIsNative)
@@ -1064,53 +1064,62 @@ static void doWeakDestroy(WeakReference *addr, bool valueIsNative) {
10641064
}
10651065

10661066
void *swift::swift_unknownWeakLoadStrong(WeakReference *addr) {
1067-
void *value = addr->Value;
1068-
if (isObjCTaggedPointerOrNull(value)) return value;
1069-
1070-
if (usesNativeSwiftReferenceCounting_allocated(value)) {
1067+
if (isNativeSwiftWeakReference(addr)) {
10711068
return swift_weakLoadStrong(addr);
1072-
} else {
1073-
return (void*) objc_loadWeakRetained((id*) &addr->Value);
10741069
}
1075-
}
10761070

1077-
void *swift::swift_unknownWeakTakeStrong(WeakReference *addr) {
1078-
void *value = addr->Value;
1071+
void *value = (void*) addr->Value;
10791072
if (isObjCTaggedPointerOrNull(value)) return value;
10801073

1081-
if (usesNativeSwiftReferenceCounting_allocated(value)) {
1074+
return (void*) objc_loadWeakRetained((id*) &addr->Value);
1075+
}
1076+
1077+
void *swift::swift_unknownWeakTakeStrong(WeakReference *addr) {
1078+
if (isNativeSwiftWeakReference(addr)) {
10821079
return swift_weakTakeStrong(addr);
1083-
} else {
1084-
void *result = (void*) objc_loadWeakRetained((id*) &addr->Value);
1085-
objc_destroyWeak((id*) &addr->Value);
1086-
return result;
10871080
}
1081+
1082+
void *value = (void*) addr->Value;
1083+
if (isObjCTaggedPointerOrNull(value)) return value;
1084+
1085+
void *result = (void*) objc_loadWeakRetained((id*) &addr->Value);
1086+
objc_destroyWeak((id*) &addr->Value);
1087+
return result;
10881088
}
10891089

10901090
void swift::swift_unknownWeakDestroy(WeakReference *addr) {
1091+
if (isNativeSwiftWeakReference(addr)) {
1092+
return swift_weakDestroy(addr);
1093+
}
1094+
10911095
id object = (id) addr->Value;
10921096
if (isObjCTaggedPointerOrNull(object)) return;
1093-
doWeakDestroy(addr, usesNativeSwiftReferenceCounting_allocated(object));
1097+
objc_destroyWeak((id*) &addr->Value);
10941098
}
1099+
10951100
void swift::swift_unknownWeakCopyInit(WeakReference *dest, WeakReference *src) {
1101+
if (isNativeSwiftWeakReference(src)) {
1102+
return swift_weakCopyInit(dest, src);
1103+
}
1104+
10961105
id object = (id) src->Value;
10971106
if (isObjCTaggedPointerOrNull(object)) {
1098-
dest->Value = (HeapObject*) object;
1099-
return;
1107+
dest->Value = (uintptr_t) object;
1108+
} else {
1109+
objc_copyWeak((id*) &dest->Value, (id*) src);
11001110
}
1101-
if (usesNativeSwiftReferenceCounting_allocated(object))
1102-
return swift_weakCopyInit(dest, src);
1103-
objc_copyWeak((id*) &dest->Value, (id*) src);
11041111
}
11051112
void swift::swift_unknownWeakTakeInit(WeakReference *dest, WeakReference *src) {
1113+
if (isNativeSwiftWeakReference(src)) {
1114+
return swift_weakTakeInit(dest, src);
1115+
}
1116+
11061117
id object = (id) src->Value;
11071118
if (isObjCTaggedPointerOrNull(object)) {
1108-
dest->Value = (HeapObject*) object;
1109-
return;
1119+
dest->Value = (uintptr_t) object;
1120+
} else {
1121+
objc_moveWeak((id*) &dest->Value, (id*) &src->Value);
11101122
}
1111-
if (usesNativeSwiftReferenceCounting_allocated(object))
1112-
return swift_weakTakeInit(dest, src);
1113-
objc_moveWeak((id*) &dest->Value, (id*) &src->Value);
11141123
}
11151124
void swift::swift_unknownWeakCopyAssign(WeakReference *dest, WeakReference *src) {
11161125
if (dest == src) return;

0 commit comments

Comments
 (0)