Files
Renderive/Core/base/Task.h
T
2026-08-01 20:09:45 +08:00

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{};
};
}