132 lines
4.4 KiB
C++
132 lines
4.4 KiB
C++
#pragma once
|
|
#include <atomic>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <memory_resource>
|
|
#include <new>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
#include "Memory.h"
|
|
#include "Object_Semantics.h"
|
|
namespace renderive {
|
|
class Task : Move_Only {
|
|
public:
|
|
static constexpr std::size_t Inline_Size = 64;
|
|
static constexpr std::size_t Inline_Align = alignof(std::max_align_t);
|
|
Task() noexcept = default;
|
|
Task(std::nullptr_t) noexcept {}
|
|
template <typename Callable>
|
|
requires (!std::is_same_v<std::decay_t<Callable>, Task>)
|
|
Task(Callable&& callable) {
|
|
using Model = Task_Model<std::decay_t<Callable>>;
|
|
if constexpr (sizeof(Model) <= Inline_Size && alignof(Model) <= Inline_Align && std::is_nothrow_move_constructible_v<Model>) {
|
|
object = inline_storage();
|
|
std::construct_at(static_cast<Model*>(object), std::forward<Callable>(callable));
|
|
inline_object = true;
|
|
}
|
|
else {
|
|
resource = memory_resource(Memory_Domain::Scheduler_Task);
|
|
std::pmr::polymorphic_allocator<Model> allocator(resource);
|
|
object = allocator.allocate(1);
|
|
try {
|
|
std::construct_at(static_cast<Model*>(object), std::forward<Callable>(callable));
|
|
}
|
|
catch (...) {
|
|
allocator.deallocate(static_cast<Model*>(object), 1);
|
|
object = nullptr;
|
|
resource = nullptr;
|
|
throw;
|
|
}
|
|
}
|
|
invoke_function = &invoke<Model>;
|
|
destroy_function = &destroy<Model>;
|
|
move_function = &move<Model>;
|
|
}
|
|
Task(Task&& other) noexcept {
|
|
move_from(std::move(other));
|
|
}
|
|
Task& operator=(Task&& other) noexcept {
|
|
if (this == &other)
|
|
return *this;
|
|
reset();
|
|
move_from(std::move(other));
|
|
return *this;
|
|
}
|
|
~Task() {
|
|
reset();
|
|
}
|
|
void operator()() {
|
|
if (invoke_function)
|
|
invoke_function(object);
|
|
}
|
|
explicit operator bool() const noexcept {
|
|
return object != nullptr;
|
|
}
|
|
private:
|
|
template <typename Callable>
|
|
struct Task_Model {
|
|
template <typename Source>
|
|
explicit Task_Model(Source&& callable) : callable(std::forward<Source>(callable)) {}
|
|
Callable callable;
|
|
};
|
|
template <typename Model>
|
|
static void invoke(void* object) {
|
|
static_cast<Model*>(object)->callable();
|
|
}
|
|
template <typename Model>
|
|
static void destroy(void* object, std::pmr::memory_resource* resource) {
|
|
std::destroy_at(static_cast<Model*>(object));
|
|
if (resource) {
|
|
std::pmr::polymorphic_allocator<Model> allocator(resource);
|
|
allocator.deallocate(static_cast<Model*>(object), 1);
|
|
}
|
|
}
|
|
template <typename Model>
|
|
static void move(void* destination, void* source) {
|
|
std::construct_at(static_cast<Model*>(destination), std::move(*static_cast<Model*>(source)));
|
|
std::destroy_at(static_cast<Model*>(source));
|
|
}
|
|
void* inline_storage() noexcept {
|
|
return static_cast<void*>(storage);
|
|
}
|
|
void move_from(Task&& other) noexcept {
|
|
invoke_function = std::exchange(other.invoke_function, nullptr);
|
|
destroy_function = std::exchange(other.destroy_function, nullptr);
|
|
move_function = std::exchange(other.move_function, nullptr);
|
|
inline_object = std::exchange(other.inline_object, false);
|
|
if (!other.object) {
|
|
object = nullptr;
|
|
resource = nullptr;
|
|
return;
|
|
}
|
|
if (inline_object) {
|
|
object = inline_storage();
|
|
resource = nullptr;
|
|
move_function(object, other.object);
|
|
other.object = nullptr;
|
|
return;
|
|
}
|
|
object = std::exchange(other.object, nullptr);
|
|
resource = std::exchange(other.resource, nullptr);
|
|
}
|
|
void reset() noexcept {
|
|
if (!object)
|
|
return;
|
|
destroy_function(object, resource);
|
|
object = nullptr;
|
|
resource = nullptr;
|
|
invoke_function = nullptr;
|
|
destroy_function = nullptr;
|
|
move_function = nullptr;
|
|
inline_object = false;
|
|
}
|
|
alignas(std::max_align_t) std::byte storage[Inline_Size]{};
|
|
void* object{};
|
|
std::pmr::memory_resource* resource{};
|
|
void (*invoke_function)(void*){};
|
|
void (*destroy_function)(void*, std::pmr::memory_resource*){};
|
|
void (*move_function)(void*, void*){};
|
|
bool inline_object{};
|
|
};
|
|
}
|