#pragma once #include #include #include #include #include #include #include #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 requires (!std::is_same_v, Task>) Task(Callable&& callable) { using Model = Task_Model>; if constexpr (sizeof(Model) <= Inline_Size && alignof(Model) <= Inline_Align && std::is_nothrow_move_constructible_v) { object = inline_storage(); std::construct_at(static_cast(object), std::forward(callable)); inline_object = true; } else { resource = memory_resource(Memory_Domain::Scheduler_Task); std::pmr::polymorphic_allocator allocator(resource); object = allocator.allocate(1); try { std::construct_at(static_cast(object), std::forward(callable)); } catch (...) { allocator.deallocate(static_cast(object), 1); object = nullptr; resource = nullptr; throw; } } invoke_function = &invoke; destroy_function = &destroy; move_function = &move; } 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 struct Task_Model { template explicit Task_Model(Source&& callable) : callable(std::forward(callable)) {} Callable callable; }; template static void invoke(void* object) { static_cast(object)->callable(); } template static void destroy(void* object, std::pmr::memory_resource* resource) { std::destroy_at(static_cast(object)); if (resource) { std::pmr::polymorphic_allocator allocator(resource); allocator.deallocate(static_cast(object), 1); } } template static void move(void* destination, void* source) { std::construct_at(static_cast(destination), std::move(*static_cast(source))); std::destroy_at(static_cast(source)); } void* inline_storage() noexcept { return static_cast(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{}; }; }