carb/tasking/Delegate.h

File members: carb/tasking/Delegate.h

// Copyright (c) 2021-2024, 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 "../delegate/detail/DelegateBase.h"

#include "TaskingUtils.h"

namespace carb
{

namespace tasking
{

namespace detail
{

struct TaskingExec
{
    using Id = TaskContext;

    Id operator()() const noexcept
    {
        auto tasking = carb::getCachedInterface<ITasking>();

        TaskContext taskContext = tasking ? tasking->getTaskContext() : kInvalidTaskContext;
        if (taskContext == kInvalidTaskContext)
        {
            // There won't be overlap here because TaskContext has a bit set in the upper word, and thread IDs are only
            // 32-bit
            static_assert(sizeof(carb::this_thread::getId()) == sizeof(uint32_t), "Invalid assumption");
            taskContext = TaskContext(carb::this_thread::getId());
        }

        return taskContext;
    }
};

} // namespace detail

template <class T>
class Delegate;

template <class T>
class DelegateRef;

template <class... Args>
class Delegate<void(Args...)>
    : public delegate::detail::DelegateBase<::carb::tasking::MutexWrapper, detail::TaskingExec, void(Args...)>
{
    using Base = delegate::detail::DelegateBase<::carb::tasking::MutexWrapper, detail::TaskingExec, void(Args...)>;

public:
    Delegate() = default;

    Delegate(Delegate&& other) : Base(std::move(other))
    {
    }

    Delegate& operator=(Delegate&& other)
    {
        Base::swap(other);
        return *this;
    }

private:
    template <class U>
    friend class DelegateRef;

    // Null constructor, only for DelegateRef use.
    constexpr Delegate(std::nullptr_t) : Base(nullptr)
    {
    }

    // Copy constructor, only for DelegateRef use.
    Delegate(const Delegate& other) : Base(other)
    {
    }

    // Copy-assign operator, only for DelegateRef use.
    Delegate& operator=(const Delegate& other)
    {
        Base::copy(other);
        return *this;
    }
};

template <class... Args>
class DelegateRef<void(Args...)>
{
public:
    using DelegateType = Delegate<void(Args...)>;

    constexpr DelegateRef() noexcept : m_delegate{ nullptr }
    {
    }

    explicit DelegateRef(DelegateType& delegate) : m_delegate(delegate)
    {
    }

    DelegateRef(const DelegateRef& other) : m_delegate(other.m_delegate)
    {
    }

    DelegateRef(DelegateRef&& other) = default;

    ~DelegateRef()
    {
        // The Delegate destructor calls UnbindAll(), which we definitely don't want. So just reset our reference.
        m_delegate.reset();
    }

    DelegateRef& operator=(const DelegateRef& other)
    {
        m_delegate = other.m_delegate;
        return *this;
    }

    DelegateRef& operator=(DelegateRef&& other) = default;

    explicit operator bool() const noexcept
    {
        return m_delegate.isValid();
    }

    void reset()
    {
        m_delegate.reset();
    }

    void reset(DelegateType& delegate)
    {
        m_delegate = delegate;
    }

    void swap(DelegateRef& other)
    {
        m_delegate.swap(other);
    }

    DelegateType* get() const noexcept
    {
        return m_delegate.isValid() ? const_cast<DelegateType*>(&m_delegate) : nullptr;
    }

    DelegateType& operator*() const noexcept
    {
        CARB_ASSERT(*this);
        return const_cast<DelegateType&>(m_delegate);
    }

    DelegateType* operator->() const noexcept
    {
        CARB_ASSERT(*this);
        return const_cast<DelegateType*>(&m_delegate);
    }

private:
    DelegateType m_delegate;
};

template <class Del>
struct RefFromDelegate
{
    using type = DelegateRef<typename Del::FunctionType>;
};

template <class Del>
using RefFromDelegate_t = typename RefFromDelegate<Del>::type;

} // namespace tasking

} // namespace carb