910e62b5创建于 1月15日历史提交
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef GIN_WEAK_CELL_H_
#define GIN_WEAK_CELL_H_

#include "base/check.h"
#include "base/functional/bind.h"
#include "base/types/pass_key.h"
#include "v8/include/cppgc/allocation.h"
#include "v8/include/cppgc/garbage-collected.h"
#include "v8/include/cppgc/member.h"
#include "v8/include/cppgc/persistent.h"
#include "v8/include/cppgc/visitor.h"
#include "v8/include/v8-cppgc.h"
#include "v8/include/v8-isolate.h"

namespace gin {

template <typename T>
class WeakCellFactory;

// A `WeakCell<T>` provides a GC-safe pattern for classes that want to:
// - expose weak references to themselves
// - invalidate the weak references *before* the object becomes unreachable.
//
// This differs from `cppgc::WeakMember<T>` in the last point, as
// `cppgc::WeakMember<T>` only becomes implicitly null after the referenced `T`
// is no longer reachable. In Chrome, a common use of early invalidation is to
// cancel callbacks that have not yet run.
//
// If early invalidation is not needed, please use `cppgc::WeakMember<T>`!
//
// Like many Oilpan types, this class is thread-unsafe.
//
// Note: This is the GC-safe version of `base::WeakPtrFactory<T>` +
// `base::WeakPtr<T>`. `base::WeakPtrFactory<T>` + `base::WeakPtr<T>` are
// GC-unsafe and should not be embedded in objects that live on the Oilpan heap.
template <typename T>
class WeakCell final : public cppgc::GarbageCollected<WeakCell<T>> {
 public:
  // Returns a pointer to the referenced object, or null if:
  // - the cell has been invalidated by its factory
  // - or the referenced object is no longer reachable.
  T* Get() const { return ptr_.Get(); }

  void Trace(cppgc::Visitor* visitor) const { visitor->Trace(ptr_); }

  // Internal helpers for `WeakCellFactory` implementation.
  explicit WeakCell(base::PassKey<WeakCellFactory<T>>, T* ptr) : ptr_(ptr) {}
  void Invalidate(base::PassKey<WeakCellFactory<T>>) { ptr_ = nullptr; }

 private:
  cppgc::WeakMember<T> ptr_;
};

#define GIN_DISALLOW_NEW()                     \
 public:                                       \
  void* operator new(size_t, void* location) { \
    return location;                           \
  }                                            \
                                               \
 private:                                      \
  void* operator new(size_t) = delete

// A `WeakCellFactory<T>` vends out a pointer to a `WeakCell<T>`, and allows the
// owning class to invalidate `WeakCell<T>`s that have already been handed out.
//
// Usage overview:
//
// class DatabaseScheduler : public GarbageCollected<DatabaseScheduler> {
//  public:
//   ...
//
//   void DoWork();
//   void CancelWork();
//
//  private:
//   // Note: field ordering for `WeakCellFactory` does not matter, and it does
//   // *not* have to be the last field in a class.
//   WeakCellFactory<DatabaseScheduler> weak_factory_{this};
//   // Note: this is *not* a cross-thread task runner. In Blink, many task
//   // queues are multiplexed onto one thread.
//   scoped_refptr<base::TaskRunner> db_task_queue;
// };
//
// void DatabaseScheduler::DoWork() {
//   // IMPORTANT: the `WrapPersistent()` around the `WeakCell<T>` argument is
//   // mandatory, as `WeakCell<T>` itself is allocated on the Oilpan heap.
//   db_task_queue_->PostTask(
//       FROM_HERE,
//       base::BindOnce(
//           &DatabaseScheduler::DoRealWork,
//           WrapPersistent(weak_factory_.GetWeakCell(
//               v8::Isolate::GetCurrent()->GetCppHeap()
//                                        ->GetAllocationHandle()))));
// }
//
// void DatabaseScheduler::CancelWork() {
//   // Any already-posted but not-yet-run tasks using a `WeakCell<T>` as the
//   // receiver will not run.
//   // However, any subsequent calls to `DoWork()` above *will* schedule new
//   // callbacks that will run unless `CancelWork()` is called again.
//   weak_factory_.Invalidate();
// }
template <typename T>
class WeakCellFactory {
  GIN_DISALLOW_NEW();

 public:
  explicit WeakCellFactory(T* ptr) : ptr_(ptr) {}

  WeakCell<T>* GetWeakCell(cppgc::AllocationHandle& allocation_handle) {
    if (!weak_cell_) {
      weak_cell_ = cppgc::MakeGarbageCollected<WeakCell<T>>(
          allocation_handle, base::PassKey<WeakCellFactory<T>>(), ptr_.Get());
    }
    DCHECK(weak_cell_);
    return weak_cell_.Get();
  }

  bool HasWeakCells() const { return weak_cell_; }

  // Invalidates the previous `WeakCell<T>` so that `previous_cell->Get()`
  // returns null. Future calls to `GetWeakCell()` will return a *new* and
  // *non-null* cell.
  void Invalidate() {
    if (!weak_cell_) {
      return;
    }
    weak_cell_->Invalidate(base::PassKey<WeakCellFactory<T>>());
    weak_cell_ = nullptr;
  }

  void Trace(cppgc::Visitor* visitor) const {
    visitor->Trace(ptr_);
    visitor->Trace(weak_cell_);
  }

 private:
  const cppgc::WeakMember<T> ptr_;
  cppgc::Member<WeakCell<T>> weak_cell_;
};

}  // namespace gin

namespace base {

template <typename T>
struct IsWeakReceiver<cppgc::Persistent<gin::WeakCell<T>>> : std::true_type {};

template <typename T>
struct BindUnwrapTraits<cppgc::Persistent<gin::WeakCell<T>>> {
  static T* Unwrap(const cppgc::Persistent<gin::WeakCell<T>>& wrapped) {
    return wrapped->Get();
  }
};

template <typename T>
struct MaybeValidTraits<cppgc::Persistent<gin::WeakCell<T>>> {
  static constexpr bool MaybeValid(
      const cppgc::Persistent<gin::WeakCell<T>>& p) {
    // Not necessarily called on `Persistent<T>` and `WeakCell<T>`'s owning
    // thread, so the only possible implementation is to assume the weak cell
    // has not been invalidated.
    return true;
  }
};

}  // namespace base

#endif  // GIN_WEAK_CELL_H_