mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
389 lines
12 KiB
C++
389 lines
12 KiB
C++
//===--- 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
|