#ifndef NEW_INL_H_ #error "Direct inclusion of this file is not allowed, include new.h" // For the sake of sane code completion. #include "new.h" #endif #include "ref_tracked.h" #include #include namespace NYT { //////////////////////////////////////////////////////////////////////////////// struct TRefCountedCookieHolder { #ifdef YT_ENABLE_REF_COUNTED_TRACKING TRefCountedTypeCookie Cookie = NullRefCountedTypeCookie; void InitializeTracking(TRefCountedTypeCookie cookie) { YT_ASSERT(Cookie == NullRefCountedTypeCookie); Cookie = cookie; TRefCountedTrackerFacade::AllocateInstance(Cookie); } ~TRefCountedCookieHolder() { if (Cookie != NullRefCountedTypeCookie) { TRefCountedTrackerFacade::FreeInstance(Cookie); } } #endif }; template struct TRefCountedWrapper final : public T , public TRefTracked { template explicit TRefCountedWrapper(TArgs&&... args) : T(std::forward(args)...) { } ~TRefCountedWrapper() = default; void DestroyRefCounted() override { T::DestroyRefCountedImpl(this); } }; template class TRefCountedWrapperWithDeleter final : public T , public TRefTracked { public: template explicit TRefCountedWrapperWithDeleter(TDeleter deleter, TArgs&&... args) : T(std::forward(args)...) , Deleter_(std::move(deleter)) { } ~TRefCountedWrapperWithDeleter() = default; void DestroyRefCounted() override { Deleter_(this); } private: const TDeleter Deleter_; }; template struct TRefCountedWrapperWithCookie final : public T , public TRefCountedCookieHolder { template explicit TRefCountedWrapperWithCookie(TArgs&&... args) : T(std::forward(args)...) { } ~TRefCountedWrapperWithCookie() = default; void DestroyRefCounted() override { T::DestroyRefCountedImpl(this); } }; namespace NDetail { template Y_FORCE_INLINE void CustomInitialize(Args... args) { Y_UNUSED(args...); } template Y_FORCE_INLINE auto CustomInitialize(T* ptr) -> decltype(&T::InitializeRefCounted, void()) { ptr->InitializeRefCounted(); } template Y_FORCE_INLINE T* NewEpilogue(void* ptr, As&& ... args) { try { auto* instance = static_cast(ptr); new (instance) T(std::forward(args)...); CustomInitialize(instance); return instance; } catch (...) { // Do not forget to free the memory. TFreeMemory::Do(ptr); throw; } } template > struct TConstructHelper { static constexpr size_t RefCounterSpace = (sizeof(TRefCounter) + alignof(T) - 1) & ~(alignof(T) - 1); static constexpr size_t RefCounterOffset = RefCounterSpace - sizeof(TRefCounter); static constexpr size_t Size = RefCounterSpace + sizeof(T); static constexpr size_t Alignment = alignof(T); template Y_FORCE_INLINE static T* Construct(void* ptr, As&&... args) { auto* refCounter = reinterpret_cast(static_cast(ptr) + RefCounterOffset); new (refCounter) TRefCounter(); auto* object = reinterpret_cast(refCounter + 1); if constexpr (std::is_constructible_v) { new(object) T(std::forward(args)...); } else { new(object) T{std::forward(args)...}; } CustomInitialize(object); return object; } }; template struct TConstructHelper { static constexpr size_t Size = sizeof(TRefCountedWrapper); static constexpr size_t Alignment = alignof(TRefCountedWrapper); template Y_FORCE_INLINE static TRefCountedWrapper* Construct(void* ptr, As&&... args) { using TDerived = TRefCountedWrapper; auto* object = new(static_cast(ptr)) TDerived(std::forward(args)...); CustomInitialize(object); return object; } }; template Y_FORCE_INLINE TIntrusivePtr SafeConstruct(void* ptr, As&&... args) { try { auto* instance = TConstructHelper::Construct(ptr, std::forward(args)...); return TIntrusivePtr(instance, /*addReference*/ false); } catch (...) { // Do not forget to free the memory. TFreeMemory::Do(ptr); throw; } } template void* AllocateConstSizeAligned() { #ifdef _win_ return ::aligned_malloc(Size, Alignment); #else if (Alignment <= alignof(std::max_align_t)) { return ::malloc(Size); } else { return ::aligned_malloc(Size, Alignment); } #endif } } // namespace NDetail //////////////////////////////////////////////////////////////////////////////// template Y_FORCE_INLINE TIntrusivePtr New( As&&... args) { void* ptr = NYT::NDetail::AllocateConstSizeAligned< NYT::NDetail::TConstructHelper::Size, NYT::NDetail::TConstructHelper::Alignment>(); return NYT::NDetail::SafeConstruct(ptr, std::forward(args)...); } template Y_FORCE_INLINE TIntrusivePtr New( typename T::TAllocator* allocator, As&&... args) { auto* ptr = allocator->Allocate(NYT::NDetail::TConstructHelper::Size); if (!ptr) { return nullptr; } return NYT::NDetail::SafeConstruct(ptr, std::forward(args)...); } //////////////////////////////////////////////////////////////////////////////// template Y_FORCE_INLINE TIntrusivePtr NewWithExtraSpace( size_t extraSpaceSize, As&&... args) { auto totalSize = NYT::NDetail::TConstructHelper::Size + extraSpaceSize; void* ptr = nullptr; #ifdef _win_ ptr = ::aligned_malloc(totalSize, NYT::NDetail::TConstructHelper::Alignment); #else if (NYT::NDetail::TConstructHelper::Alignment <= alignof(std::max_align_t)) { ptr = ::malloc(totalSize); } else { ptr = ::aligned_malloc(totalSize, NYT::NDetail::TConstructHelper::Alignment); } #endif return NYT::NDetail::SafeConstruct(ptr, std::forward(args)...); } template Y_FORCE_INLINE TIntrusivePtr NewWithExtraSpace( typename T::TAllocator* allocator, size_t extraSpaceSize, As&&... args) { auto totalSize = NYT::NDetail::TConstructHelper::Size + extraSpaceSize; auto* ptr = allocator->Allocate(totalSize); if (!ptr) { return nullptr; } return NYT::NDetail::SafeConstruct(ptr, std::forward(args)...); } //////////////////////////////////////////////////////////////////////////////// template Y_FORCE_INLINE TIntrusivePtr NewWithDeleter(TDeleter deleter, As&&... args) { using TWrapper = TRefCountedWrapperWithDeleter; void* ptr = NYT::NDetail::AllocateConstSizeAligned(); auto* instance = NYT::NDetail::NewEpilogue( ptr, std::move(deleter), std::forward(args)...); return TIntrusivePtr(instance, /*addReference*/ false); } //////////////////////////////////////////////////////////////////////////////// template Y_FORCE_INLINE TIntrusivePtr NewWithLocation( const TSourceLocation& location, As&&... args) { using TWrapper = TRefCountedWrapperWithCookie; void* ptr = NYT::NDetail::AllocateConstSizeAligned(); auto* instance = NYT::NDetail::NewEpilogue(ptr, std::forward(args)...); #ifdef YT_ENABLE_REF_COUNTED_TRACKING instance->InitializeTracking(GetRefCountedTypeCookieWithLocation(location)); #else Y_UNUSED(location); #endif return TIntrusivePtr(instance, /*addReference*/ false); } //////////////////////////////////////////////////////////////////////////////// template const void* TWithExtraSpace::GetExtraSpacePtr() const { return static_cast(this) + 1; } template void* TWithExtraSpace::GetExtraSpacePtr() { return static_cast(this) + 1; } template size_t TWithExtraSpace::GetUsableSpaceSize() const { #ifdef _win_ return 0; #else return malloc_usable_size(const_cast(static_cast(this))) - sizeof(T); #endif } //////////////////////////////////////////////////////////////////////////////// } // namespace NYT