threading is silly

dev-func-drop
Brett 2025-01-14 11:56:35 -05:00
parent fca412ea29
commit 1e9442bbd4
3 changed files with 192 additions and 25 deletions

View File

@ -27,7 +27,7 @@ macro(compile_options target_name)
sanitizers(${target_name})
endmacro()
project(blt-gp VERSION 0.3.7)
project(blt-gp VERSION 0.3.8)
include(CTest)

View File

@ -24,61 +24,193 @@
#include <thread>
#include <functional>
#include <atomic>
#include <type_traits>
namespace blt::gp
{
template <typename Parallel, typename Single = void>
namespace detail
{
struct empty_callable
{
void operator()() const
{
}
};
}
template <typename EnumId>
class task_builder_t;
template <typename EnumId, typename Parallel, typename Single = detail::empty_callable>
class task_t
{
static_assert(std::is_enum_v<EnumId>, "Enum ID must be of enum type!");
static_assert(std::is_invocable_v<Parallel, int>, "Parallel must be invocable with exactly one argument (thread index)");
static_assert(std::is_invocable_v<Single>, "Single must be invocable with no arguments");
friend task_builder_t<EnumId>;
public:
task_t(Parallel parallel, Single single): parallel(parallel), single(single), requires_single_sync(true)
task_t(const EnumId task_id, const Parallel& parallel, const Single& single): parallel(std::forward<Parallel>(parallel)),
single(std::forward<Single>(single)),
requires_single_sync(true), task_id(task_id)
{
}
explicit task_t(Parallel parallel): parallel(parallel), requires_single_sync(false)
explicit task_t(const EnumId task_id, const Parallel& parallel): parallel(std::forward<Parallel>(parallel)), single(detail::empty_callable{}),
task_id(task_id)
{
}
void call_parallel(size_t thread_index)
void call_parallel(size_t thread_index) const
{
parallel(thread_index);
}
void call_single()
void call_single() const
{
single();
}
[[nodiscard]] EnumId get_task_id() const
{
return task_id;
}
private:
Parallel parallel;
Single single;
bool explicit_sync_begin : 1 = true;
bool explicit_sync_end : 1 = true;
bool requires_single_sync : 1 = false;
const Parallel& parallel;
const Single& single;
bool requires_single_sync = false;
EnumId task_id;
};
template <typename... Tasks>
class task_storage_t
template <typename EnumId, typename Parallel, typename Single = detail::empty_callable>
task_t(EnumId, Parallel, Single) -> task_t<EnumId, Parallel, Single>;
template <typename EnumId>
class task_builder_t
{
static_assert(std::is_enum_v<EnumId>, "Enum ID must be of enum type!");
using EnumInt = std::underlying_type_t<EnumId>;
public:
task_builder_t() = default;
template <typename... Tasks>
static std::function<void(barrier&, EnumId, size_t)> make_callable(Tasks&&... tasks)
{
return [&tasks...](barrier& sync_barrier, EnumId task, size_t thread_index)
{
call_jmp_table(sync_barrier, task, thread_index, tasks...);
};
}
private:
template <typename Task>
static void execute(barrier& sync_barrier, const size_t thread_index, Task&& task)
{
// sync_barrier.wait();
if (task.requires_single_sync)
{
if (thread_index == 0)
task.call_single();
sync_barrier.wait();
}
task.call_parallel(thread_index);
// sync_barrier.wait();
}
template <typename Task>
static bool call(barrier& sync_barrier, const EnumId current_task, const size_t thread_index, Task&& task)
{
if (static_cast<EnumInt>(current_task) == static_cast<EnumInt>(task.get_task_id()))
{
execute(sync_barrier, thread_index, std::forward<Task>(task));
return false;
}
return true;
}
template <typename... Tasks>
static void call_jmp_table(barrier& sync_barrier, const EnumId current_task, const size_t thread_index, Tasks&&... tasks)
{
if (static_cast<EnumInt>(current_task) >= sizeof...(tasks))
BLT_UNREACHABLE;
(call(sync_barrier, current_task, thread_index, std::forward<Tasks>(tasks)) && ...);
}
};
template <typename EnumId>
class thread_manager_t
{
static_assert(std::is_enum_v<EnumId>, "Enum ID must be of enum type!");
public:
explicit thread_manager_t(const size_t thread_count, const bool will_main_block = true): barrier(thread_count),
will_main_block(will_main_block)
explicit thread_manager_t(const size_t thread_count, std::function<void(barrier&, EnumId, size_t)> task_func,
const bool will_main_block = true): barrier(thread_count), will_main_block(will_main_block)
{
for (size_t i = 0; i < will_main_block ? thread_count - 1 : thread_count; ++i)
thread_callable = [this, task_func = std::move(task_func)](const size_t thread_index)
{
threads.emplace_back([i, this]()
while (should_run)
{
while (should_run)
barrier.wait();
if (tasks_remaining > 0)
task_func(barrier, tasks.back(), thread_index);
barrier.wait();
if (thread_index == 0)
{
if (this->will_main_block)
{
tasks.pop_back();
--tasks_remaining;
}
else
{
std::scoped_lock lock{task_lock};
tasks.pop_back();
--tasks_remaining;
}
}
});
}
};
for (size_t i = 0; i < will_main_block ? thread_count - 1 : thread_count; ++i)
threads.emplace_back(thread_callable, will_main_block ? i + 1 : i);
}
void execute() const
{
BLT_ASSERT(will_main_block &&
"You attempted to call this function without specifying that "
"you want an external blocking thread (try passing will_main_block = true)");
thread_callable(0);
}
void add_task(EnumId task)
{
if (will_main_block)
{
tasks.push_back(task);
++tasks_remaining;
}
else
{
std::scoped_lock lock(task_lock);
tasks.push_back(task);
++tasks_remaining;
}
}
~thread_manager()
bool has_tasks_left()
{
if (will_main_block)
{
return !tasks.empty();
}
std::scoped_lock lock{task_lock};
return tasks.empty();
}
~thread_manager_t()
{
should_run = false;
for (auto& thread : threads)
@ -94,11 +226,15 @@ namespace blt::gp
return will_main_block ? threads.size() + 1 : threads.size();
}
barrier barrier;
blt::barrier barrier;
std::atomic_bool should_run = true;
bool will_main_block;
std::vector<size_t> tasks;
std::vector<EnumId> tasks;
std::atomic_uint64_t tasks_remaining = 0;
std::vector<std::thread> threads;
std::mutex task_lock;
std::function<void(size_t)> thread_callable;
};
}

View File

@ -165,15 +165,46 @@ void do_run()
std::cout << std::endl;
}
template<typename What, typename What2>
template <typename What, typename What2>
auto what(What addr, What2 addr2) -> decltype(addr + addr2)
{
return addr + addr2;
}
enum class test
{
hello,
there
};
inline void hello(blt::size_t)
{
BLT_TRACE("I did some parallel work!");
}
inline void there(blt::size_t)
{
BLT_TRACE("Wow there");
}
int main()
{
for (int i = 0; i < 1; i++)
do_run();
blt::gp::thread_manager_t threads{
std::thread::hardware_concurrency(), blt::gp::task_builder_t<test>::make_callable(
blt::gp::task_t{test::hello, hello},
blt::gp::task_t{test::there, there}
)
};
threads.add_task(test::hello);
threads.add_task(test::hello);
threads.add_task(test::hello);
threads.add_task(test::there);
while (threads.has_tasks_left())
threads.execute();
// for (int i = 0; i < 1; i++)
// do_run();
return 0;
}