Files
2026-07-13 12:40:42 +08:00

443 lines
13 KiB
C++

// Copyright (c) 2026 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// The file has been adapted from the PyTorch project.
// Licensed under BSD-style license:
// https://github.com/pytorch/pytorch/blob/main/LICENSE
#pragma once
#include <atomic>
#include <cstdint>
#include <type_traits>
#include <utility>
namespace c10 {
// Forward declarations
class intrusive_ptr_target;
namespace raw {
namespace intrusive_ptr {
inline void incref(intrusive_ptr_target* self);
inline void decref(intrusive_ptr_target* self);
} // namespace intrusive_ptr
namespace weak_intrusive_ptr {
inline void incref(intrusive_ptr_target* self);
inline void decref(intrusive_ptr_target* self);
} // namespace weak_intrusive_ptr
struct DontIncreaseRefcount {};
} // namespace raw
namespace detail {
constexpr uint64_t kImpracticallyHugeReferenceCount = 0x0FFFFFFF;
constexpr uint64_t kImpracticallyHugeWeakReferenceCount =
(kImpracticallyHugeReferenceCount << 32);
constexpr uint64_t kReferenceCountOne = 1;
constexpr uint64_t kWeakReferenceCountOne = (kReferenceCountOne << 32);
constexpr uint64_t kUniqueRef = (kReferenceCountOne | kWeakReferenceCountOne);
inline uint32_t refcount(uint64_t combined_refcount) {
return static_cast<uint32_t>(combined_refcount);
}
inline uint32_t weakcount(uint64_t combined_refcount) {
// Bit 63 is reserved for kHasPyObject in PyTorch (a flag indicating a live
// Python wrapper). This compat layer does not implement the PyObject path,
// so the bit will never be set, but we mask it out here to match PyTorch's
// extraction logic and remain numerically correct if the bit were ever set.
return static_cast<uint32_t>((combined_refcount & ~(uint64_t(1) << 63)) >>
32);
}
inline uint64_t atomic_combined_refcount_increment(
std::atomic<uint64_t>* combined_refcount, uint64_t inc) {
return combined_refcount->fetch_add(inc, std::memory_order_relaxed) + inc;
}
inline uint64_t atomic_combined_refcount_decrement(
std::atomic<uint64_t>* combined_refcount, uint64_t dec) {
return combined_refcount->fetch_sub(dec, std::memory_order_acq_rel) - dec;
}
inline uint32_t atomic_weakcount_increment(
std::atomic<uint64_t>* combined_refcount) {
return weakcount(atomic_combined_refcount_increment(combined_refcount,
kWeakReferenceCountOne));
}
inline uint32_t atomic_weakcount_decrement(
std::atomic<uint64_t>* combined_refcount) {
return weakcount(atomic_combined_refcount_decrement(combined_refcount,
kWeakReferenceCountOne));
}
template <class T>
struct intrusive_target_default_null_type final {
static constexpr T* singleton() noexcept { return nullptr; }
};
} // namespace detail
class intrusive_ptr_target {
public:
intrusive_ptr_target() noexcept : combined_refcount_(0) {}
intrusive_ptr_target(intrusive_ptr_target&& /*other*/) noexcept
: intrusive_ptr_target() {}
intrusive_ptr_target& operator=(intrusive_ptr_target&& /*other*/) noexcept {
return *this;
}
intrusive_ptr_target(const intrusive_ptr_target& /*other*/) noexcept
: intrusive_ptr_target() {}
intrusive_ptr_target& operator=(
const intrusive_ptr_target& /*other*/) noexcept {
return *this;
}
uint32_t refcount() const {
return detail::refcount(combined_refcount_.load(std::memory_order_relaxed));
}
uint32_t weakcount() const {
return detail::weakcount(
combined_refcount_.load(std::memory_order_relaxed));
}
protected:
virtual ~intrusive_ptr_target() = default;
private:
mutable std::atomic<uint64_t> combined_refcount_;
template <typename T, typename NullType>
friend class intrusive_ptr;
template <typename T, typename NullType>
friend class weak_intrusive_ptr;
friend inline void raw::intrusive_ptr::incref(intrusive_ptr_target* self);
friend inline void raw::intrusive_ptr::decref(intrusive_ptr_target* self);
friend inline void raw::weak_intrusive_ptr::incref(
intrusive_ptr_target* self);
friend inline void raw::weak_intrusive_ptr::decref(
intrusive_ptr_target* self);
};
namespace raw {
namespace intrusive_ptr {
inline void incref(intrusive_ptr_target* self) {
if (self) {
detail::atomic_combined_refcount_increment(&self->combined_refcount_,
detail::kReferenceCountOne);
}
}
inline void decref(intrusive_ptr_target* self) {
if (self) {
uint64_t new_count = detail::atomic_combined_refcount_decrement(
&self->combined_refcount_, detail::kReferenceCountOne);
if (detail::refcount(new_count) == 0) {
// All strong references gone; release the implicit weak reference
// (strong refs count as +1 to weakcount per the kUniqueRef invariant).
if (detail::atomic_weakcount_decrement(&self->combined_refcount_) == 0) {
delete self;
}
}
}
}
} // namespace intrusive_ptr
namespace weak_intrusive_ptr {
inline void incref(intrusive_ptr_target* self) {
if (self) {
detail::atomic_weakcount_increment(&self->combined_refcount_);
}
}
inline void decref(intrusive_ptr_target* self) {
if (self) {
if (detail::atomic_weakcount_decrement(&self->combined_refcount_) == 0) {
delete self;
}
}
}
} // namespace weak_intrusive_ptr
} // namespace raw
template <class TTarget, class NullType>
class weak_intrusive_ptr;
template <class TTarget,
class NullType = detail::intrusive_target_default_null_type<TTarget>>
class intrusive_ptr final {
private:
static_assert(
std::is_base_of_v<TTarget,
std::remove_pointer_t<decltype(NullType::singleton())>>,
"NullType::singleton() must return a element_type* pointer");
TTarget* target_;
template <class TTarget2, class NullType2>
friend class intrusive_ptr;
friend class weak_intrusive_ptr<TTarget, NullType>;
void retain_() noexcept {
if (target_ != NullType::singleton()) {
detail::atomic_combined_refcount_increment(&target_->combined_refcount_,
detail::kReferenceCountOne);
}
}
void reset_() noexcept {
if (target_ != NullType::singleton()) {
uint64_t new_count = detail::atomic_combined_refcount_decrement(
&target_->combined_refcount_, detail::kReferenceCountOne);
if (detail::refcount(new_count) == 0) {
// All strong references gone; release the implicit weak reference
// (strong refs count as +1 to weakcount per the kUniqueRef invariant).
if (detail::atomic_weakcount_decrement(&target_->combined_refcount_) ==
0) {
delete target_;
}
}
target_ = NullType::singleton();
}
}
public:
using element_type = TTarget;
using pointer = TTarget*;
intrusive_ptr() noexcept : target_(NullType::singleton()) {}
intrusive_ptr(std::nullptr_t) noexcept : target_(NullType::singleton()) {}
explicit intrusive_ptr(TTarget* raw) : target_(raw) {
if (target_ != NullType::singleton()) {
target_->combined_refcount_.store(detail::kUniqueRef,
std::memory_order_relaxed);
}
}
intrusive_ptr(const intrusive_ptr& rhs) : target_(rhs.target_) { retain_(); }
intrusive_ptr(intrusive_ptr&& rhs) noexcept : target_(rhs.target_) {
rhs.target_ = NullType::singleton();
}
template <typename From, typename FromNullType>
/* implicit */ intrusive_ptr(
const intrusive_ptr<From, FromNullType>& rhs) noexcept
: target_(rhs.target_) {
static_assert(std::is_convertible_v<From*, TTarget*>,
"Source type must be convertible to target type");
retain_();
}
template <typename From, typename FromNullType>
/* implicit */ intrusive_ptr(intrusive_ptr<From, FromNullType>&& rhs) noexcept
: target_(rhs.target_) {
static_assert(std::is_convertible_v<From*, TTarget*>,
"Source type must be convertible to target type");
rhs.target_ = FromNullType::singleton();
}
~intrusive_ptr() { reset_(); }
intrusive_ptr& operator=(const intrusive_ptr& rhs) {
if (this != &rhs) {
reset_();
target_ = rhs.target_;
retain_();
}
return *this;
}
intrusive_ptr& operator=(intrusive_ptr&& rhs) noexcept {
if (this != &rhs) {
reset_();
target_ = rhs.target_;
rhs.target_ = NullType::singleton();
}
return *this;
}
// Takes ownership of a raw pointer without incrementing the refcount.
static intrusive_ptr reclaim(TTarget* raw_ptr) {
intrusive_ptr result;
result.target_ = raw_ptr;
return result;
}
// unsafe_adopt is a PyTorch API compatibility alias for reclaim().
// Both adopt a raw pointer without incrementing the refcount; prefer
// reclaim() in new code.
static intrusive_ptr unsafe_adopt(TTarget* raw_ptr) {
return reclaim(raw_ptr);
}
TTarget* get() const noexcept { return target_; }
TTarget& operator*() const { return *target_; }
TTarget* operator->() const { return target_; }
explicit operator bool() const noexcept {
return target_ != NullType::singleton();
}
uint32_t use_count() const noexcept {
if (target_ == NullType::singleton()) {
return 0;
}
return target_->refcount();
}
bool defined() const noexcept { return target_ != NullType::singleton(); }
bool unique() const noexcept { return use_count() == 1; }
void reset() noexcept { reset_(); }
void swap(intrusive_ptr& other) noexcept {
using std::swap;
swap(target_, other.target_);
}
[[deprecated(
"intrusive_ptr::release is unsafe; use reclaim() or explicit ownership "
"transfer instead")]] TTarget*
release() noexcept {
TTarget* result = target_;
target_ = NullType::singleton();
return result;
}
bool operator==(const intrusive_ptr& rhs) const noexcept {
return target_ == rhs.target_;
}
bool operator!=(const intrusive_ptr& rhs) const noexcept {
return target_ != rhs.target_;
}
bool operator==(std::nullptr_t) const noexcept {
return target_ == NullType::singleton();
}
bool operator!=(std::nullptr_t) const noexcept {
return target_ != NullType::singleton();
}
};
template <class TTarget,
class NullType = detail::intrusive_target_default_null_type<TTarget>>
class weak_intrusive_ptr final {
private:
TTarget* target_;
template <class TTarget2, class NullType2>
friend class weak_intrusive_ptr;
friend class intrusive_ptr<TTarget, NullType>;
void retain_() {
if (target_ != NullType::singleton()) {
detail::atomic_weakcount_increment(&target_->combined_refcount_);
}
}
void reset_() noexcept {
if (target_ != NullType::singleton()) {
if (detail::atomic_weakcount_decrement(&target_->combined_refcount_) ==
0) {
delete target_;
}
target_ = NullType::singleton();
}
}
public:
using element_type = TTarget;
weak_intrusive_ptr() noexcept : target_(NullType::singleton()) {}
weak_intrusive_ptr(const intrusive_ptr<TTarget, NullType>& p)
: target_(p.target_) {
retain_();
}
weak_intrusive_ptr(const weak_intrusive_ptr& rhs) : target_(rhs.target_) {
retain_();
}
weak_intrusive_ptr(weak_intrusive_ptr&& rhs) noexcept : target_(rhs.target_) {
rhs.target_ = NullType::singleton();
}
~weak_intrusive_ptr() { reset_(); }
weak_intrusive_ptr& operator=(const weak_intrusive_ptr& rhs) {
if (this != &rhs) {
reset_();
target_ = rhs.target_;
retain_();
}
return *this;
}
weak_intrusive_ptr& operator=(weak_intrusive_ptr&& rhs) {
if (this != &rhs) {
reset_();
target_ = rhs.target_;
rhs.target_ = NullType::singleton();
}
return *this;
}
intrusive_ptr<TTarget, NullType> lock() const {
if (target_ == NullType::singleton()) {
return intrusive_ptr<TTarget, NullType>();
}
auto& atomic = target_->combined_refcount_;
uint64_t count = atomic.load(std::memory_order_relaxed);
while (true) {
if (detail::refcount(count) == 0) {
return intrusive_ptr<TTarget, NullType>();
}
if (atomic.compare_exchange_weak(count,
count + detail::kReferenceCountOne,
std::memory_order_acq_rel,
std::memory_order_relaxed)) {
return intrusive_ptr<TTarget, NullType>::unsafe_adopt(target_);
}
}
}
uint32_t use_count() const {
if (target_ == NullType::singleton()) {
return 0;
}
return target_->refcount();
}
bool expired() const { return use_count() == 0; }
void reset() { reset_(); }
};
// Creates a new T with an initial strong refcount of 1.
template <typename T, typename... Args>
intrusive_ptr<T> make_intrusive(Args&&... args) {
return intrusive_ptr<T>(new T(std::forward<Args>(args)...));
}
} // namespace c10