omni/core/IWeakObject.h

File members: omni/core/IWeakObject.h

// Copyright (c) 2022-2023, NVIDIA CORPORATION. All rights reserved.
//
// NVIDIA CORPORATION and its licensors retain all intellectual property
// and proprietary rights in and to this software, related documentation
// and any modifications thereto. Any use, reproduction, disclosure or
// distribution of this software and related documentation without an express
// license agreement from NVIDIA CORPORATION is strictly prohibited.
//

#pragma once

#include <carb/cpp/Atomic.h> // atomic_ref
#include <carb/detail/DeferredLoad.h>
#include <omni/core/Api.h> // OMNI_API
#include <omni/core/Assert.h>
#include <omni/core/IObject.h>

#include <limits>

namespace omni
{
namespace core
{

class IWeakObject;
class IWeakObject_abi;
class IWeakObjectControlBlock;
class IWeakObjectControlBlock_abi;

class IWeakObjectControlBlock_abi
    : public omni::core::Inherits<omni::core::IObject, OMNI_TYPE_ID("omni.core.IWeakObjectControlBlock")>
{
protected:
    virtual IObject* getObject_abi() noexcept = 0;
};

class IWeakObject_abi : public omni::core::Inherits<omni::core::IObject, OMNI_TYPE_ID("omni.core.IWeakObject")>
{
protected:
    virtual OMNI_ATTR("not_null") IWeakObjectControlBlock* getWeakObjectControlBlock_abi() noexcept = 0;
};

} // namespace core
} // namespace omni

#define OMNI_BIND_INCLUDE_INTERFACE_DECL
#include <omni/core/IWeakObject.gen.h>

namespace omni
{
namespace core
{

class IWeakObjectControlBlock : public omni::core::Generated<omni::core::IWeakObjectControlBlock_abi>
{
};

class IWeakObject : public omni::core::Generated<omni::core::IWeakObject_abi>
{
};

#ifdef CARB_DOC_BUILD
#endif

template <typename T>
class WeakPtr
{
public:
    WeakPtr(std::nullptr_t = nullptr) noexcept
    {
    }

    WeakPtr(const omni::core::ObjectPtr<T>& strong) noexcept
    {
        if (strong)
        {
            m_ref = strong->getWeakObjectControlBlock();
        }
    }

    WeakPtr(T* strong) noexcept
    {
        if (strong)
        {
            m_ref = strong->getWeakObjectControlBlock();
        }
    }

    WeakPtr(const WeakPtr& other) noexcept = default;

    WeakPtr(WeakPtr&& other) noexcept = default;

    ~WeakPtr() noexcept = default;

    WeakPtr& operator=(const WeakPtr& other) noexcept = default;

    WeakPtr& operator=(WeakPtr&& other) noexcept = default;

    omni::core::ObjectPtr<T> getObjectPtr() const noexcept
    {
        if (m_ref)
        {
            return m_ref->getObject().template as<T>();
        }

        return nullptr;
    }

    omni::core::ObjectPtr<T> lock() const noexcept
    {
        if (m_ref)
        {
            return m_ref->getObject().template as<T>();
        }

        return nullptr;
    }

private:
    ObjectPtr<IWeakObjectControlBlock> m_ref;
};

#ifndef DOXYGEN_BUILD
namespace detail
{

enum class WeakObjectControlBlockOp
{
    eIncrementStrong = 0,
    eDecrementStrong = 1,
    eDecrementWeak = 2,
    eGetStrongCount = 3, // for testing
    eGetWeakCount = 4, // for testing
    eHasControlBlock = 5, // for testing
};

} // namespace detail
#endif // DOXYGEN_BUILD

} // namespace core
} // namespace omni

// carb.dll C-ABI to hide the implementation details of a weak object's control block.  see IWeakObject's class docs for
// motivation.
#ifndef DOXYGEN_BUILD
#    if CARB_REQUIRE_LINKED
OMNI_API omni::core::IWeakObjectControlBlock* omniWeakObjectGetOrCreateControlBlock(omni::core::IObject* obj,
                                                                                    uintptr_t* refCountOrEncodedPtr);
OMNI_API uint32_t omniWeakObjectControlBlockOp(uintptr_t* refCountOrEncodedPtr, omni::core::WeakObjectControlBlockOp op);
#    else
OMNI_API omni::core::IWeakObjectControlBlock* omniWeakObjectGetOrCreateControlBlock(omni::core::IObject* obj,
                                                                                    uintptr_t* refCountOrEncodedPtr)
    CARB_ATTRIBUTE(weak);
OMNI_API uint32_t omniWeakObjectControlBlockOp(uintptr_t* refCountOrEncodedPtr,
                                               omni::core::detail::WeakObjectControlBlockOp op) CARB_ATTRIBUTE(weak);
#    endif
#endif // DOXYGEN_BUILD

namespace omni
{
namespace core
{

#ifndef DOXYGEN_BUILD
namespace detail
{

CARB_DETAIL_DEFINE_DEFERRED_LOAD(loadWeakObjectGetOrCreateControlBlock,
                                 omniWeakObjectGetOrCreateControlBlock,
                                 (omni::core::IWeakObjectControlBlock * (*)(omni::core::IObject*, uintptr_t*)));

inline omni::core::IWeakObjectControlBlock* getOrCreateWeakObjectControlBlock(omni::core::IObject* obj,
                                                                              uintptr_t* refCountOrEncodedPtr)
{
    auto impl = detail::loadWeakObjectGetOrCreateControlBlock();
    OMNI_ASSERT(impl);
    return impl(obj, refCountOrEncodedPtr);
}

CARB_DETAIL_DEFINE_DEFERRED_LOAD(loadWeakObjectControlBlockOp,
                                 omniWeakObjectControlBlockOp,
                                 (uint32_t(*)(uintptr_t*, WeakObjectControlBlockOp)));

inline uint32_t incrementWeakObjectStrongCount(uintptr_t* refCountOrEncodedPtr)
{
    auto impl = detail::loadWeakObjectControlBlockOp();
    OMNI_ASSERT(impl);
    return impl(refCountOrEncodedPtr, WeakObjectControlBlockOp::eIncrementStrong);
}

inline uint32_t decrementWeakObjectStrongCount(uintptr_t* refCountOrEncodedPtr)
{
    auto impl = detail::loadWeakObjectControlBlockOp();
    OMNI_ASSERT(impl);
    return impl(refCountOrEncodedPtr, WeakObjectControlBlockOp::eDecrementStrong);
}

inline void decrementWeakObjectWeakCount(uintptr_t* refCountOrEncodedPtr)
{
    auto impl = detail::loadWeakObjectControlBlockOp();
    OMNI_ASSERT(impl);
    (void)impl(refCountOrEncodedPtr, WeakObjectControlBlockOp::eDecrementWeak);
}

inline uint32_t getWeakObjectStrongCount(uintptr_t* refCountOrEncodedPtr)
{
    auto impl = detail::loadWeakObjectControlBlockOp();
    OMNI_ASSERT(impl);
    return impl(refCountOrEncodedPtr, WeakObjectControlBlockOp::eGetStrongCount);
}

inline uint32_t getWeakObjectWeakCount(uintptr_t* refCountOrEncodedPtr)
{
    auto impl = detail::loadWeakObjectControlBlockOp();
    OMNI_ASSERT(impl);
    return impl(refCountOrEncodedPtr, WeakObjectControlBlockOp::eGetWeakCount);
}

inline bool hasWeakObjectControlBlock(uintptr_t* refCountOrEncodedPtr)
{
    auto impl = detail::loadWeakObjectControlBlockOp();
    OMNI_ASSERT(impl);
    return (0 != impl(refCountOrEncodedPtr, WeakObjectControlBlockOp::eHasControlBlock));
}

} // namespace detail
#endif // DOXYGEN_BUILD

#ifdef CARB_DOC_BUILD
#endif
#ifdef CARB_DOC_BUILD
#endif
template <typename T, typename... Rest>
struct ImplementsWeak : public ImplementsCast<T, Rest...>
{
public:
    inline void acquire() noexcept
    {
        // note: this implementation is needed to disambiguate which `cast` to call when using multiple inheritance. it
        // has zero-overhead.
        static_cast<T*>(this)->acquire();
    }

    inline void release() noexcept
    {
        // note: this implementation is needed to disambiguate which `cast` to call when using multiple inheritance. it
        // has zero-overhead.
        static_cast<T*>(this)->release();
    }

    inline ObjectPtr<IWeakObjectControlBlock> getWeakObjectControlBlock() noexcept
    {
        // note: this implementation is needed to disambiguate which `getWeakObjectControlBlock` to call when using
        // multiple inheritance. it has zero-overhead.
        return static_cast<T*>(this)->getWeakObjectControlBlock();
    }

protected:
    virtual ~ImplementsWeak() noexcept
    {
        // decrementWeakObjectWeakCount() will no-op if a control block has not been created
        omni::core::detail::decrementWeakObjectWeakCount(&m_refCountOrPtr);
    }

    virtual void acquire_abi() noexcept override
    {
        omni::core::detail::incrementWeakObjectStrongCount(&m_refCountOrPtr);
    }

    virtual void release_abi() noexcept override
    {
        if (0 == omni::core::detail::decrementWeakObjectStrongCount(&m_refCountOrPtr))
        {
            delete this;
        }
    }

    virtual IWeakObjectControlBlock* getWeakObjectControlBlock_abi() noexcept override
    {
        return omni::core::detail::getOrCreateWeakObjectControlBlock(static_cast<T*>(this), &m_refCountOrPtr);
    }

#ifndef DOXYGEN_BUILD
    uint32_t _getStrongCount() noexcept
    {
        return omni::core::detail::getWeakObjectStrongCount(&m_refCountOrPtr);
    }

    bool _hasWeakObjectControlBlock() noexcept
    {
        return omni::core::detail::hasWeakObjectControlBlock(&m_refCountOrPtr);
    }
#endif

private:
    // by default, this value stores the reference count of the object.
    //
    // however, when getWeakObjectControlBlock_abi() is called, this memory is repurposed to store a pointer to an
    // IWeakObjectControlBlock. it's the IWeakObjectControlBlock that will store both a strong and weak reference count
    // for this object.
    //
    // the pointer to the IWeakObjectControlBlock count is "encoded" so that we can easily determine if this memory is a
    // reference count or a pointer to an IWeakObjectControlBlock.
    //
    // the encoding of this pointer is an implementation detail and not exposed to the user.
    //
    // this value should be treated as opaque.
    uintptr_t m_refCountOrPtr{ 1 };
};

} // namespace core
} // namespace omni

#define OMNI_BIND_INCLUDE_INTERFACE_IMPL
#include <omni/core/IWeakObject.gen.h>