/*
* Copyright (C) 2015 Hamburg University of Applied Sciences (HAW)
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpp11-compat
* @{
*
* @file
* @brief C++11 thread drop in replacement
* @see
* std::thread, std::this_thread
*
*
* @author Raphael Hiesgen
*
* @}
*/
#ifndef RIOT_THREAD_HPP
#define RIOT_THREAD_HPP
#include "time.h"
#include "thread.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include "riot/mutex.hpp"
#include "riot/chrono.hpp"
#include "riot/condition_variable.hpp"
#include "riot/detail/thread_util.hpp"
namespace riot {
namespace {
constexpr kernel_pid_t thread_uninitialized = -1;
constexpr size_t stack_size = THREAD_STACKSIZE_MAIN;
}
struct thread_data {
thread_data() : ref_count{2}, joining_thread{thread_uninitialized} {
// nop
}
std::atomic ref_count;
kernel_pid_t joining_thread;
char stack[stack_size];
};
/**
* This deleter prevents our thread data from being destroyed if the thread
* object is destroyed before the thread had a chance to run
*/
struct thread_data_deleter {
void operator()(thread_data* ptr) {
if (--ptr->ref_count == 0) {
delete ptr;
}
}
};
/**
* @brief implementation of thread::id
* @see
* thread::id
*
*/
class thread_id {
template
friend std::basic_ostream& operator<<(std::basic_ostream
& out,
thread_id id);
friend class thread;
public:
inline thread_id() noexcept : m_handle{thread_uninitialized} {}
inline thread_id(kernel_pid_t handle) : m_handle{handle} {}
inline bool operator==(thread_id other) noexcept {
return m_handle == other.m_handle;
}
inline bool operator!=(thread_id other) noexcept {
return !(m_handle == other.m_handle);
}
inline bool operator<(thread_id other) noexcept {
return m_handle < other.m_handle;
}
inline bool operator<=(thread_id other) noexcept {
return !(m_handle > other.m_handle);
}
inline bool operator>(thread_id other) noexcept {
return m_handle > other.m_handle;
}
inline bool operator>=(thread_id other) noexcept {
return !(m_handle < other.m_handle);
}
private:
kernel_pid_t m_handle;
};
template
inline std::basic_ostream& operator<<(std::basic_ostream
& out,
thread_id id) {
return out << id.m_handle;
}
namespace this_thread {
inline thread_id get_id() noexcept { return thread_getpid(); }
inline void yield() noexcept { thread_yield(); }
void sleep_for(const std::chrono::nanoseconds& ns);
template
void sleep_for(const std::chrono::duration& sleep_duration) {
using namespace std::chrono;
if (sleep_duration > std::chrono::duration::zero()) {
constexpr std::chrono::duration max = nanoseconds::max();
nanoseconds ns;
if (sleep_duration < max) {
ns = duration_cast(sleep_duration);
if (ns < sleep_duration) {
++ns;
}
} else {
ns = nanoseconds::max();
}
sleep_for(ns);
}
}
inline void sleep_until(const riot::time_point& sleep_time) {
mutex mtx;
condition_variable cv;
unique_lock lk(mtx);
while (riot::now() < sleep_time) {
cv.wait_until(lk, sleep_time);
}
}
} // namespace this_thread
/*
* @brief C++11 compliant implementation of thread, however uses the time
* point from out chrono header instead of the specified one
* @see
* std::thread
*
*/
class thread {
public:
using id = thread_id;
using native_handle_type = kernel_pid_t;
inline thread() noexcept : m_handle{thread_uninitialized} {}
template
explicit thread(F&& f, Args&&... args);
~thread();
thread(const thread&) = delete;
inline thread(thread&& t) noexcept : m_handle{t.m_handle} {
t.m_handle = thread_uninitialized;
std::swap(m_data, t.m_data);
}
thread& operator=(const thread&) = delete;
thread& operator=(thread&&) noexcept;
void swap(thread& t) noexcept {
std::swap(m_data, t.m_data);
std::swap(m_handle, t.m_handle);
}
inline bool joinable() const noexcept {
return m_handle != thread_uninitialized;
}
void join();
void detach();
inline id get_id() const noexcept { return m_handle; }
inline native_handle_type native_handle() noexcept { return m_handle; }
static unsigned hardware_concurrency() noexcept;
kernel_pid_t m_handle;
std::unique_ptr m_data;
};
void swap(thread& lhs, thread& rhs) noexcept;
template
void* thread_proxy(void* vp) {
{ // without this scope, the objects here are not cleaned up corrctly
std::unique_ptr p(static_cast(vp));
auto tmp = std::get<0>(*p);
std::unique_ptr data{tmp};
// create indices for the arguments, 0 is thread_data and 1 is the function
auto indices = detail::get_indices::value, 2>();
try {
detail::apply_args(std::get<1>(*p), indices, *p);
}
catch (...) {
// nop
}
if (data->joining_thread != thread_uninitialized) {
thread_wakeup(data->joining_thread);
}
}
// some riot cleanup code
sched_task_exit();
return nullptr;
}
template
thread::thread(F&& f, Args&&... args)
: m_data{new thread_data} {
using namespace std;
using func_and_args = tuple
::type, typename decay::type...>;
std::unique_ptr p(
new func_and_args(m_data.get(), forward(f), forward(args)...));
m_handle = thread_create(
m_data->stack, stack_size, THREAD_PRIORITY_MAIN - 1, 0,
&thread_proxy, p.get(), "riot_cpp_thread");
if (m_handle >= 0) {
p.release();
} else {
throw std::system_error(
std::make_error_code(std::errc::resource_unavailable_try_again),
"Failed to create thread.");
}
}
inline thread& thread::operator=(thread&& other) noexcept {
if (m_handle != thread_uninitialized) {
std::terminate();
}
m_handle = other.m_handle;
other.m_handle = thread_uninitialized;
std::swap(m_data, other.m_data);
return *this;
}
inline void swap(thread& lhs, thread& rhs) noexcept { lhs.swap(rhs); }
} // namespace riot
#endif // RIOT_THREAD_HPP