blob: a0f86e9d77037d80344b180b37dde6ccafbbfe0e [file] [log] [blame]
//===--- 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 defined(__x86_64__)
NativeMarkerMask = SWIFT_ABI_X86_64_OBJC_WEAK_REFERENCE_MARKER_MASK,
NativeMarkerValue = SWIFT_ABI_X86_64_OBJC_WEAK_REFERENCE_MARKER_VALUE
#elif defined(__i386__)
NativeMarkerMask = SWIFT_ABI_I386_OBJC_WEAK_REFERENCE_MARKER_MASK,
NativeMarkerValue = SWIFT_ABI_I386_OBJC_WEAK_REFERENCE_MARKER_VALUE
#elif defined(__arm__) || defined(_M_ARM)
NativeMarkerMask = SWIFT_ABI_ARM_OBJC_WEAK_REFERENCE_MARKER_MASK,
NativeMarkerValue = SWIFT_ABI_ARM_OBJC_WEAK_REFERENCE_MARKER_VALUE
#elif defined(__arm64__) || defined(__aarch64__) || defined(_M_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