| //===--- WeakReference.h - Swift weak references ----------------*- C++ -*-===// |
| // |
| // This source file is part of the Swift.org open source project |
| // |
| // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors |
| // Licensed under Apache License v2.0 with Runtime Library Exception |
| // |
| // See https://swift.org/LICENSE.txt for license information |
| // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // Swift weak reference implementation. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef SWIFT_RUNTIME_WEAKREFERENCE_H |
| #define SWIFT_RUNTIME_WEAKREFERENCE_H |
| |
| #include "swift/Runtime/Config.h" |
| #include "swift/Runtime/HeapObject.h" |
| #include "swift/Runtime/Metadata.h" |
| |
| #if SWIFT_OBJC_INTEROP |
| #include "swift/Runtime/ObjCBridge.h" |
| #endif |
| |
| #include "Private.h" |
| |
| #include <cstdint> |
| |
| namespace swift { |
| |
| // Note: This implementation of unknown weak makes several assumptions |
| // about ObjC's weak variables implementation: |
| // * Nil is stored verbatim. |
| // * Tagged pointer objects are stored verbatim with no side table entry. |
| // * Ordinary objects are stored with the LSB two bits (64-bit) or |
| // one bit (32-bit) all clear. The stored value otherwise need not be |
| // the pointed-to object. |
| // |
| // The Swift 3 implementation of unknown weak makes the following |
| // additional assumptions: |
| // * Ordinary objects are stored *verbatim* with the LSB *three* bits (64-bit) |
| // or *two* bits (32-bit) all clear. |
| |
| // Thread-safety: |
| // |
| // Reading a weak reference must be thread-safe with respect to: |
| // * concurrent readers |
| // * concurrent weak reference zeroing due to deallocation of the |
| // pointed-to object |
| // * concurrent ObjC readers or zeroing (for non-native weak storage) |
| // |
| // Reading a weak reference is NOT thread-safe with respect to: |
| // * concurrent writes to the weak variable other than zeroing |
| // * concurrent destruction of the weak variable |
| // |
| // Writing a weak reference must be thread-safe with respect to: |
| // * concurrent weak reference zeroing due to deallocation of the |
| // pointed-to object |
| // * concurrent ObjC zeroing (for non-native weak storage) |
| // |
| // Writing a weak reference is NOT thread-safe with respect to: |
| // * concurrent reads |
| // * concurrent writes other than zeroing |
| |
| class WeakReferenceBits { |
| // On ObjC platforms, a weak variable may be controlled by the ObjC |
| // runtime or by the Swift runtime. NativeMarkerMask and NativeMarkerValue |
| // are used to distinguish them. |
| // if ((ptr & NativeMarkerMask) == NativeMarkerValue) it's Swift |
| // else it's ObjC |
| // NativeMarkerMask incorporates the ObjC tagged pointer bits |
| // plus one more bit that is set in Swift-controlled weak pointer values. |
| // Non-ObjC platforms don't use any markers. |
| enum : uintptr_t { |
| #if !SWIFT_OBJC_INTEROP |
| NativeMarkerMask = 0, |
| NativeMarkerValue = 0 |
| #elif __x86_64__ |
| NativeMarkerMask = SWIFT_ABI_X86_64_OBJC_WEAK_REFERENCE_MARKER_MASK, |
| NativeMarkerValue = SWIFT_ABI_X86_64_OBJC_WEAK_REFERENCE_MARKER_VALUE |
| #elif __i386__ |
| NativeMarkerMask = SWIFT_ABI_I386_OBJC_WEAK_REFERENCE_MARKER_MASK, |
| NativeMarkerValue = SWIFT_ABI_I386_OBJC_WEAK_REFERENCE_MARKER_VALUE |
| #elif __arm__ |
| NativeMarkerMask = SWIFT_ABI_ARM_OBJC_WEAK_REFERENCE_MARKER_MASK, |
| NativeMarkerValue = SWIFT_ABI_ARM_OBJC_WEAK_REFERENCE_MARKER_VALUE |
| #elif __arm64__ |
| NativeMarkerMask = SWIFT_ABI_ARM64_OBJC_WEAK_REFERENCE_MARKER_MASK, |
| NativeMarkerValue = SWIFT_ABI_ARM64_OBJC_WEAK_REFERENCE_MARKER_VALUE |
| #else |
| #error unknown architecture |
| #endif |
| }; |
| |
| static_assert((NativeMarkerMask & NativeMarkerValue) == NativeMarkerValue, |
| "native marker value must fall within native marker mask"); |
| static_assert((NativeMarkerMask & heap_object_abi::SwiftSpareBitsMask) |
| == NativeMarkerMask, |
| "native marker mask must fall within Swift spare bits"); |
| #if SWIFT_OBJC_INTEROP |
| static_assert((NativeMarkerMask & heap_object_abi::ObjCReservedBitsMask) |
| == heap_object_abi::ObjCReservedBitsMask, |
| "native marker mask must contain all ObjC tagged pointer bits"); |
| static_assert((NativeMarkerValue & heap_object_abi::ObjCReservedBitsMask) |
| == 0, |
| "native marker value must not interfere with ObjC bits"); |
| #endif |
| |
| uintptr_t bits; |
| |
| public: |
| LLVM_ATTRIBUTE_ALWAYS_INLINE |
| WeakReferenceBits() { } |
| |
| LLVM_ATTRIBUTE_ALWAYS_INLINE |
| WeakReferenceBits(HeapObjectSideTableEntry *newValue) { |
| setNativeOrNull(newValue); |
| } |
| |
| LLVM_ATTRIBUTE_ALWAYS_INLINE |
| bool isNativeOrNull() const { |
| return bits == 0 || (bits & NativeMarkerMask) == NativeMarkerValue; |
| } |
| |
| LLVM_ATTRIBUTE_ALWAYS_INLINE |
| HeapObjectSideTableEntry *getNativeOrNull() const { |
| assert(isNativeOrNull()); |
| if (bits == 0) |
| return nullptr; |
| else |
| return |
| reinterpret_cast<HeapObjectSideTableEntry *>(bits & ~NativeMarkerMask); |
| } |
| |
| LLVM_ATTRIBUTE_ALWAYS_INLINE |
| void setNativeOrNull(HeapObjectSideTableEntry *newValue) { |
| assert((uintptr_t(newValue) & NativeMarkerMask) == 0); |
| if (newValue) |
| bits = uintptr_t(newValue) | NativeMarkerValue; |
| else |
| bits = 0; |
| } |
| }; |
| |
| |
| class WeakReference { |
| union { |
| std::atomic<WeakReferenceBits> nativeValue; |
| #if SWIFT_OBJC_INTEROP |
| id nonnativeValue; |
| #endif |
| }; |
| |
| void destroyOldNativeBits(WeakReferenceBits oldBits) { |
| auto oldSide = oldBits.getNativeOrNull(); |
| if (oldSide) |
| oldSide->decrementWeak(); |
| } |
| |
| HeapObject *nativeLoadStrongFromBits(WeakReferenceBits bits) { |
| auto side = bits.getNativeOrNull(); |
| return side ? side->tryRetain() : nullptr; |
| } |
| |
| HeapObject *nativeTakeStrongFromBits(WeakReferenceBits bits) { |
| auto side = bits.getNativeOrNull(); |
| if (side) { |
| side->decrementWeak(); |
| return side->tryRetain(); |
| } else { |
| return nullptr; |
| } |
| } |
| |
| void nativeCopyInitFromBits(WeakReferenceBits srcBits) { |
| auto side = srcBits.getNativeOrNull(); |
| if (side) |
| side = side->incrementWeak(); |
| |
| nativeValue.store(WeakReferenceBits(side), std::memory_order_relaxed); |
| } |
| |
| public: |
| |
| WeakReference() = default; |
| |
| WeakReference(std::nullptr_t) |
| : nativeValue(WeakReferenceBits(nullptr)) { } |
| |
| WeakReference(const WeakReference& rhs) = delete; |
| |
| |
| void nativeInit(HeapObject *object) { |
| auto side = object ? object->refCounts.formWeakReference() : nullptr; |
| nativeValue.store(WeakReferenceBits(side), std::memory_order_relaxed); |
| } |
| |
| void nativeDestroy() { |
| auto oldBits = nativeValue.load(std::memory_order_relaxed); |
| nativeValue.store(nullptr, std::memory_order_relaxed); |
| destroyOldNativeBits(oldBits); |
| } |
| |
| void nativeAssign(HeapObject *newObject) { |
| if (newObject) { |
| assert(objectUsesNativeSwiftReferenceCounting(newObject) && |
| "weak assign native with non-native new object"); |
| } |
| |
| auto newSide = |
| newObject ? newObject->refCounts.formWeakReference() : nullptr; |
| auto newBits = WeakReferenceBits(newSide); |
| |
| auto oldBits = nativeValue.load(std::memory_order_relaxed); |
| nativeValue.store(newBits, std::memory_order_relaxed); |
| |
| assert(oldBits.isNativeOrNull() && |
| "weak assign native with non-native old object"); |
| destroyOldNativeBits(oldBits); |
| } |
| |
| HeapObject *nativeLoadStrong() { |
| auto bits = nativeValue.load(std::memory_order_relaxed); |
| return nativeLoadStrongFromBits(bits); |
| } |
| |
| HeapObject *nativeTakeStrong() { |
| auto bits = nativeValue.load(std::memory_order_relaxed); |
| nativeValue.store(nullptr, std::memory_order_relaxed); |
| return nativeTakeStrongFromBits(bits); |
| } |
| |
| void nativeCopyInit(WeakReference *src) { |
| auto srcBits = src->nativeValue.load(std::memory_order_relaxed); |
| return nativeCopyInitFromBits(srcBits); |
| } |
| |
| void nativeTakeInit(WeakReference *src) { |
| auto srcBits = src->nativeValue.load(std::memory_order_relaxed); |
| assert(srcBits.isNativeOrNull()); |
| src->nativeValue.store(nullptr, std::memory_order_relaxed); |
| nativeValue.store(srcBits, std::memory_order_relaxed); |
| } |
| |
| void nativeCopyAssign(WeakReference *src) { |
| if (this == src) return; |
| nativeDestroy(); |
| nativeCopyInit(src); |
| } |
| |
| void nativeTakeAssign(WeakReference *src) { |
| if (this == src) return; |
| nativeDestroy(); |
| nativeTakeInit(src); |
| } |
| |
| #if SWIFT_OBJC_INTEROP |
| private: |
| void nonnativeInit(id object) { |
| objc_initWeak(&nonnativeValue, object); |
| } |
| |
| void initWithNativeness(void *object, bool isNative) { |
| if (isNative) |
| nativeInit(static_cast<HeapObject *>(object)); |
| else |
| nonnativeInit(static_cast<id>(object)); |
| } |
| |
| void nonnativeDestroy() { |
| objc_destroyWeak(&nonnativeValue); |
| } |
| |
| void destroyWithNativeness(bool isNative) { |
| if (isNative) |
| nativeDestroy(); |
| else |
| nonnativeDestroy(); |
| } |
| |
| public: |
| |
| void unknownInit(void *object) { |
| if (isObjCTaggedPointerOrNull(object)) { |
| nonnativeValue = static_cast<id>(object); |
| } else { |
| bool isNative = objectUsesNativeSwiftReferenceCounting(object); |
| initWithNativeness(object, isNative); |
| } |
| } |
| |
| void unknownDestroy() { |
| auto oldBits = nativeValue.load(std::memory_order_relaxed); |
| destroyWithNativeness(oldBits.isNativeOrNull()); |
| } |
| |
| void unknownAssign(void *newObject) { |
| // If the new value is not allocated, simply destroy any old value. |
| if (isObjCTaggedPointerOrNull(newObject)) { |
| unknownDestroy(); |
| nonnativeValue = static_cast<id>(newObject); |
| return; |
| } |
| |
| bool newIsNative = objectUsesNativeSwiftReferenceCounting(newObject); |
| |
| auto oldBits = nativeValue.load(std::memory_order_relaxed); |
| bool oldIsNative = oldBits.isNativeOrNull(); |
| |
| // If they're both native, use the native function. |
| if (oldIsNative && newIsNative) |
| return nativeAssign(static_cast<HeapObject *>(newObject)); |
| |
| // If neither is native, use ObjC. |
| if (!oldIsNative && !newIsNative) |
| return (void) objc_storeWeak(&nonnativeValue, static_cast<id>(newObject)); |
| |
| // They don't match. Destroy and re-initialize. |
| destroyWithNativeness(oldIsNative); |
| initWithNativeness(newObject, newIsNative); |
| } |
| |
| void *unknownLoadStrong() { |
| auto bits = nativeValue.load(std::memory_order_relaxed); |
| if (bits.isNativeOrNull()) |
| return nativeLoadStrongFromBits(bits); |
| else |
| return objc_loadWeakRetained(&nonnativeValue); |
| } |
| |
| void *unknownTakeStrong() { |
| auto bits = nativeValue.load(std::memory_order_relaxed); |
| if (bits.isNativeOrNull()) { |
| nativeValue.store(nullptr, std::memory_order_relaxed); |
| return nativeTakeStrongFromBits(bits); |
| } |
| else { |
| id result = objc_loadWeakRetained(&nonnativeValue); |
| objc_destroyWeak(&nonnativeValue); |
| return result; |
| } |
| } |
| |
| void unknownCopyInit(WeakReference *src) { |
| auto srcBits = src->nativeValue.load(std::memory_order_relaxed); |
| if (srcBits.isNativeOrNull()) |
| nativeCopyInitFromBits(srcBits); |
| else |
| objc_copyWeak(&nonnativeValue, &src->nonnativeValue); |
| } |
| |
| void unknownTakeInit(WeakReference *src) { |
| auto srcBits = src->nativeValue.load(std::memory_order_relaxed); |
| if (srcBits.isNativeOrNull()) |
| nativeTakeInit(src); |
| else |
| objc_moveWeak(&nonnativeValue, &src->nonnativeValue); |
| } |
| |
| void unknownCopyAssign(WeakReference *src) { |
| if (this == src) return; |
| unknownDestroy(); |
| unknownCopyInit(src); |
| } |
| |
| void unknownTakeAssign(WeakReference *src) { |
| if (this == src) return; |
| unknownDestroy(); |
| unknownTakeInit(src); |
| } |
| |
| // SWIFT_OBJC_INTEROP |
| #endif |
| |
| }; |
| |
| static_assert(sizeof(WeakReference) == sizeof(void*), |
| "incorrect WeakReference size"); |
| static_assert(alignof(WeakReference) == alignof(void*), |
| "incorrect WeakReference alignment"); |
| |
| // namespace swift |
| } |
| |
| #endif |