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