Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,10 @@ class BorrowingReference final {
}

private:
// WeakReference<T> -> BorrowingReference<T> Lock-constructor
explicit BorrowingReference(const WeakReference<T>& ref) : _value(ref._value), _state(ref._state) {
_state->strongRefCount++;
}
// WeakReference<T> -> BorrowingReference<T> Lock-constructor.
// The caller (`WeakReference<T>::lock()`) has already claimed the strong ref count via
// `tryIncrementStrongRefCount()`, so this must NOT increment it again.
explicit BorrowingReference(const WeakReference<T>& ref) : _value(ref._value), _state(ref._state) {}

private:
// BorrowingReference<C> -> BorrowingReference<T> Cast-constructor
Expand Down
17 changes: 17 additions & 0 deletions packages/react-native-nitro-modules/cpp/utils/ReferenceState.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,23 @@ struct ReferenceState {
return oldRefCount <= 1;
}

/**
* Increments the strong ref count by one - unless it is already zero, and returns whether it did.
*
* A zero strong count means the final strong release is already under way (`~BorrowingReference` decrements
* the count BEFORE calling `forceDestroyValue()`), so handing out a strong reference in that window would
* resurrect a dying value. This is `weak_ptr::lock()`'s increment-if-not-zero.
*/
inline bool tryIncrementStrongRefCount() {
size_t count = strongRefCount.load();
while (count != 0) {
if (strongRefCount.compare_exchange_weak(count, count + 1)) {
return true;
}
}
return false;
}

explicit ReferenceState() : strongRefCount(1), weakRefCount(0), isDeleted(false) {}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ BorrowingReference<T> WeakReference<T>::lock() const {
// return nullptr
return BorrowingReference<T>();
}
if (!_state->tryIncrementStrongRefCount()) {
// the last strong reference is mid-release - the value is about to be destroyed, it just hasn't
// flagged `isDeleted` yet.
return BorrowingReference<T>();
}

return BorrowingReference(*this);
}
Expand Down