Vous êtes sur la page 1sur 4

#pragma once

#include <thread>
#include <string>
#include <optional>
#include <vector>
#include <mutex>
#include <algorithm>

namespace Dinner::Concurrency
{
extern std::mutex thread_list_mutex;

class named_thread : public std::thread


{
public://interface
const std::string& name() const noexcept;

virtual void stop();

public: //ctor
named_thread() noexcept = default;

template<typename Fn, typename... Args>


explicit named_thread(const std::string& name, Fn&& fn, Args&&... args)
: std::thread(std::forward<Fn>(fn), std::forward<Args>(args)...)
, name_(name)
{}

named_thread& operator=(named_thread&& other) noexcept;


named_thread(named_thread&& other) noexcept;

virtual ~named_thread() noexcept = default;

named_thread(const named_thread& other) = delete;


named_thread& operator=(const named_thread& other) = delete;

private:
std::string name_;
};

template<typename Fn, typename... Args>


class poll_thread : public named_thread
{
using function_info_t = std::tuple<std::decay_t<Fn>,
std::decay_t<Args>...>;

public://interface
void stop() override
{
running_ = false;
}

public: //ctor
poll_thread() noexcept = default;

template <typename Stub>


explicit poll_thread(const std::string& name, Stub&& stub, Fn&& fn,
Args&&... args)
: named_thread()
, function_info_(
std::make_unique<function_info_t>(
std::make_tuple(std::forward<Fn>(fn),
std::forward<Args>(args)...)
))
, running_(true)
{
named_thread::operator=(named_thread(name, stub, [this]() { this-
>poll_loop(); }));
}

poll_thread& operator=(poll_thread&& other) noexcept


{
function_info_ = std::move(other.function_info_);
running_ = other.running_;
named_thread::operator=(std::move(other));
return *this;
}

poll_thread(poll_thread&& other) noexcept


: named_thread(std::move(other))
, function_info_(std::move(other.function_info_))
, running_(other.running_)
{}

~poll_thread() noexcept = default;

poll_thread(const poll_thread& other) = delete;


poll_thread& operator=(const poll_thread& other) = delete;

private:
std::unique_ptr<function_info_t> function_info_;

bool running_;

void poll_loop() const


{
if (function_info_ == nullptr)
throw std::runtime_error("[poll_thread::poll_loop] No
function and/or parameters");

while (running_)
{
auto& params = *function_info_;
std::get<std::decay_t<Fn>>(params)
(std::get<std::decay_t<Args>>(params)...);
}
}
};

namespace detail { extern std::vector<std::unique_ptr<named_thread>> threads;


}

std::optional<std::reference_wrapper<named_thread>> find_thread(const
std::string& thread_name);
std::optional<std::reference_wrapper<named_thread>>
find_thread(std::thread::id thread_id);
std::optional<std::reference_wrapper<named_thread>> current_thread();

std::size_t thread_count();

namespace detail
{
template <typename ThreadType, typename Fn, typename... Args>
void launch_impl(const std::string& name, Fn&& fn, Args&&... args)
{
const auto thread_stub = [](auto&& fn, auto&&... args)
{
// Invoke the given function.
fn(std::forward<decltype(args)>(args)...);

// Blocking lock of thread list


std::lock_guard<std::mutex> lock(thread_list_mutex);
const auto end_range =
std::remove_if(detail::threads.begin(), detail::threads.end(), [](const auto&
thread)
{
if (thread && thread->get_id() ==
std::this_thread::get_id())
return true;

return false;
});

if (end_range != detail::threads.end())
{
// First detach the current thread from the thread
list so that
// the current thread isn't terminated prematurely.
(*end_range)->detach();

// Remove the current thread from the thread list.


detail::threads.erase(end_range);
}
};

detail::threads.emplace_back(std::make_unique<ThreadType>(
name,
thread_stub,
std::forward<Fn>(fn),
std::forward<Args>(args)...
));
}
}

template <typename Fn, typename... Args>


void launch(const std::string& name, Fn&& fn, Args&&... args)
{
detail::launch_impl<named_thread>(name, std::forward<Fn>(fn),
std::forward<Args>(args)...);
}

template <typename Fn, typename... Args>


void launch_polling(const std::string& name, Fn&& fn, Args&&... args)
{
detail::launch_impl<poll_thread<Fn, Args...>>(name,
std::forward<Fn>(fn), std::forward<Args>(args)...);
}
}

Vous aimerez peut-être aussi