blob: 10b2052719c1623205e352deac5d960bf4128f10 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FBL_REF_COUNTED_UPGRADEABLE_H_
#define FBL_REF_COUNTED_UPGRADEABLE_H_
#include <zircon/assert.h>
#include <fbl/macros.h>
#include <fbl/ref_counted_internal.h>
#include <fbl/ref_ptr.h>
#include <atomic>
namespace fbl {
namespace internal {
// A variant of the standard RefCouted base class which allows an
// "UpgradeFromRaw" operation which can be used to give weak-pointer like
// behavior for very specific use cases.
//
template <bool EnableAdoptionValidator>
class RefCountedUpgradeableBase : public RefCountedBase<EnableAdoptionValidator> {
public:
RefCountedUpgradeableBase() = default;
~RefCountedUpgradeableBase() = default;
// RefCountedUpgradeableBase<> instances may not be copied, assigned or moved.
DISALLOW_COPY_ASSIGN_AND_MOVE(RefCountedUpgradeableBase);
// This method must only be called from MakeRefPtrUpgradeFromRaw. See its
// comments for details in the proper use of this method. The actual job of
// this function is to atomically increment the refcount if the refcount is
// greater than zero.
//
// This method returns false if the object was found with an invalid
// refcount (refcount was <= 0), and true if the refcount was not zero and
// it was incremented.
//
// The procedure used is the while-CAS loop with the advantage that
// compare_exchange on failure updates |old| on failure (to exchange) so the
// loop does not have to do a separate load.
//
bool AddRefMaybeInDestructor() const __WARN_UNUSED_RESULT {
int32_t old = this->ref_count_.load(std::memory_order_acquire);
do {
if (old <= 0) {
return false;
}
} while (!this->ref_count_.compare_exchange_weak(old, old + 1, std::memory_order_acq_rel,
std::memory_order_acquire));
return true;
}
};
} // namespace internal
// A variant of the standard RefCouted base class which allows an
// "UpgradeFromRaw" operation which can be used to give weak-pointer like
// behavior for a very specific use case in the kernel.
//
template <typename T, bool EnableAdoptionValidator = ZX_DEBUG_ASSERT_IMPLEMENTED>
class RefCountedUpgradeable : private internal::RefCountedUpgradeableBase<EnableAdoptionValidator> {
public:
RefCountedUpgradeable() = default;
~RefCountedUpgradeable() = default;
using internal::RefCountedBase<EnableAdoptionValidator>::AddRef;
using internal::RefCountedBase<EnableAdoptionValidator>::Release;
using internal::RefCountedBase<EnableAdoptionValidator>::Adopt;
using internal::RefCountedBase<EnableAdoptionValidator>::ref_count_debug;
using internal::RefCountedUpgradeableBase<EnableAdoptionValidator>::AddRefMaybeInDestructor;
// RefCountedUpgradeable<> instances may not be copied, assigned or moved.
DISALLOW_COPY_ASSIGN_AND_MOVE(RefCountedUpgradeable);
};
// Constructs a RefPtr from a raw T* which is being held alive by RefPtr
// with the caveat that the existing RefPtr might be in the process of
// destructing the T object. When the T object is in the destructor, the
// resulting RefPtr is null, otherwise the resulting RefPtr points to T*
// with the updated reference count.
//
// The only way for this to be a valid pattern is that the call is made
// while holding |lock| and that the same lock also is used to protect the
// value of T* .
//
// This pattern is needed in collaborating objects which cannot hold a
// RefPtr to each other because it would cause a reference cycle. Instead
// there is a raw pointer from one to the other and a RefPtr in the
// other direction. When needed the raw pointer can be upgraded via
// MakeRefPtrUpgradeFromRaw() and operated outside |lock|.
//
// For example:
//
// class Holder: public RefCounted<Holder> {
// public:
// void add_client(Client* c) {
// Autolock al(&lock_);
// client_ = c;
// }
//
// void remove_client() {
// Autolock al(&lock_);
// client_ = nullptr;
// }
//
// void PassClient(Bar* bar) {
// fbl::RefPtr<Client> rc_client;
// {
// Autolock al(&lock_);
// if (client_) {
// rc_client = fbl::internal::MakeRefPtrUpgradeFromRaw(client_, lock_);
// }
// }
//
// if (rc_client) {
// bar->Client(std::move(rc_client)); // Bar might keep a ref to client.
// } else {
// bar->OnNoClient();
// }
// }
//
// private:
// fbl::Mutex lock_;
// Client* client __TA_REQUIRES(lock_);
// };
//
// class Client: public RefCounted<Client> {
// public:
// Client(RefPtr<Holder> holder) : holder_(std::move(holder)) {
// holder_->add_client(this);
// }
//
// ~Client() {
// holder_->remove_client();
// }
// private:
// fbl::RefPtr<Holder> holder_;
// };
//
//
template <typename T, typename LockCapability>
inline RefPtr<T> MakeRefPtrUpgradeFromRaw(T* ptr, const LockCapability& lock) __TA_REQUIRES(lock) {
if (!ptr->AddRefMaybeInDestructor()) {
return nullptr;
}
return ImportFromRawPtr(ptr);
}
} // namespace fbl
#endif // FBL_REF_COUNTED_UPGRADEABLE_H_