//===--- 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
