Are weak variables thread safe wrt object destruction?

I wonder are weak variables really not thread safe wrt object destruction as stated there
So if I read weak variable XYZ in thread A at the same moment thread B leaving scope where strong variable XYZ is defined, there is a chance that it will crash?

1 Like

According to Swift for C++ Practitioners, Part 2: Reference Types & Optionals | Doug's Compiler Corner, you can promote weak to strong references that won't be deallocated:

A weak variable must have optional type, because the object it points to might go away at any point. If the object it points to is destroyed, then the weak reference will get the value nil. Code that works with weak references will naturally "promote" the weak reference to a strong reference when working with it, because it's the same operation as determining whether any other optional has a value in it:

if let a = b.a {

}
1 Like

Thanks for reply, byt my question isn’t about zeroing weak ref, I’m concerning about thread safety.

As stated in reference I linked above

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

So my question is still the same, is it possible to crash if one thread reads weak var while other is destructing it?

If you mean destructing the object that the weak reference is pointing to, no, weak references should be thread-safe against that. If you mean destructing the weak reference that you're trying to read, then yes, that can crash. It should not be possible to get yourself into the latter situation without using unsafe features, though.

3 Likes

Thanks for reply, still wonder how it works:

  HeapObject *nativeLoadStrongFromBits(WeakReferenceBits bits) {
    auto side = bits.getNativeOrNull();
    return side ? side->tryRetain() : nullptr;
  }

Code from there

Isn't it possible race condition to occur between nativeLoadStrongFromBits and decrement of strong ref

Looks like i'm missing something tbh

UPD:
Found out that nativeLoadStrongFromBits and decrement of strong ref derives to these method calls. Which looks mutually synchronized like almost everything in RefCount.h. Is it a sauce that makes weak ref safe to destruction?

  template <PerformDeinit performDeinit>
  SWIFT_ALWAYS_INLINE
  bool doDecrement(uint32_t dec) {
    auto oldbits = refCounts.load(SWIFT_MEMORY_ORDER_CONSUME);
    RefCountBits newbits;
    
    // constant propagation will remove this in swift_release, it should only
    // be present in swift_release_n
    if (dec != 1 && oldbits.isImmortal(true)) {
      return false;
    }
    
    do {
      newbits = oldbits;
      bool fast =
        newbits.decrementStrongExtraRefCount(dec);
      if (SWIFT_UNLIKELY(!fast)) {
        if (oldbits.isImmortal(false)) {
            return false;
        }
        // Slow paths include side table; deinit; underflow
        return doDecrementSlow<performDeinit>(oldbits, dec);
      }
    } while (!refCounts.compare_exchange_weak(oldbits, newbits,
                                              std::memory_order_release,
                                              std::memory_order_relaxed));

    return false;  // don't deinit
  }

  SWIFT_ALWAYS_INLINE
  bool tryIncrement() {
    auto oldbits = refCounts.load(SWIFT_MEMORY_ORDER_CONSUME);
    RefCountBits newbits;
    do {
      if (!oldbits.hasSideTable() && oldbits.getIsDeiniting())
        return false;

      newbits = oldbits;
      bool fast = newbits.incrementStrongExtraRefCount(1);
      if (SWIFT_UNLIKELY(!fast)) {
        if (oldbits.isImmortal(false))
          return true;
        return tryIncrementSlow(oldbits);
      }
    } while (!refCounts.compare_exchange_weak(oldbits, newbits,
                                              std::memory_order_relaxed));
    return true;
  }

Btw, when we use optional chaining like this:
obj?.doSomeStuff()

Does it rules to loading strong ref?

Invocation any of these methods:
nativeLoadStrongFromBits or nativeTakeStrongFromBits?