/*
* 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
#include "riot/mutex.hpp"
#include "riot/chrono.hpp"
#include "riot/condition_variable.hpp"
#include "riot/detail/thread_util.hpp"
namespace riot {
namespace {
/**
* @brief Identify uninitialized threads.
*/
constexpr kernel_pid_t thread_uninitialized = -1;
/**
* @brief The stack size for new threads.
*/
constexpr size_t stack_size = THREAD_STACKSIZE_MAIN;
}
/**
* @brief Holds context data for the thread.
*/
struct thread_data {
thread_data() : ref_count{2}, joining_thread{thread_uninitialized} {
// nop
}
/** @cond INTERNAL */
std::atomic ref_count;
kernel_pid_t joining_thread;
std::array stack;
/** @endcond */
};
/**
* @brief 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 {
/**
* @brief Called by the deleter of a thread object to manage the lifetime of
* the thread internal management data.
*/
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:
/**
* @brief Creates a uninitialized thread id.
*/
inline thread_id() noexcept : m_handle{thread_uninitialized} {}
/**
* @brief Create a thread id from a native handle.
*/
explicit inline thread_id(kernel_pid_t handle) : m_handle{handle} {}
/**
* @brief Comparison operator for thread ids.
*/
inline bool operator==(thread_id other) noexcept {
return m_handle == other.m_handle;
}
/**
* @brief Comparison operator for thread ids.
*/
inline bool operator!=(thread_id other) noexcept {
return !(m_handle == other.m_handle);
}
/**
* @brief Comparison operator for thread ids.
*/
inline bool operator<(thread_id other) noexcept {
return m_handle < other.m_handle;
}
/**
* @brief Comparison operator for thread ids.
*/
inline bool operator<=(thread_id other) noexcept {
return !(m_handle > other.m_handle);
}
/**
* @brief Comparison operator for thread ids.
*/
inline bool operator>(thread_id other) noexcept {
return m_handle > other.m_handle;
}
/**
* @brief Comparison operator for thread ids.
*/
inline bool operator>=(thread_id other) noexcept {
return !(m_handle < other.m_handle);
}
private:
kernel_pid_t m_handle;
};
/**
* @brief Enable printing of thread ids using output streams.
*/
template
inline std::basic_ostream& operator<<(std::basic_ostream
& out,
thread_id id) {
return out << id.m_handle;
}
namespace this_thread {
/**
* @brief Access the id of the currently running thread.
*/
inline thread_id get_id() noexcept { return thread_id{thread_getpid()}; }
/**
* @brief Yield the currently running thread.
*/
inline void yield() noexcept { thread_yield(); }
/**
* @brief Puts the current thread to sleep.
* @param[in] ns Duration to sleep in nanoseconds.
*/
void sleep_for(const std::chrono::nanoseconds& ns);
/**
* @brief Puts the current thread to sleep.
* @param[in] sleep_duration The duration to sleep.
*/
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);
}
}
/**
* @brief Puts the current thread to sleep.
* @param[in] sleep_time A point in time that specifies when the thread
* should wake up.
*/
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:
/**
* @brief The id is of type `thread_id`-
*/
using id = thread_id;
/**
* @brief The native handle type is the `kernel_pid_t` of RIOT.
*/
using native_handle_type = kernel_pid_t;
/**
* @brief Per default, an uninitialized thread is created.
*/
inline thread() noexcept : m_handle{thread_uninitialized} {}
/**
* @brief Create a thread from a functor and arguments for it.
* @param[in] f Functor to run as a thread.
* @param[in] args Arguments passed to the functor.
*/
template
explicit thread(F&& f, Args&&... args);
/**
* @brief Disallow copy constructor.
*/
thread(const thread&) = delete;
/**
* @brief Move constructor.
*/
inline thread(thread&& t) noexcept : m_handle{t.m_handle} {
t.m_handle = thread_uninitialized;
std::swap(m_data, t.m_data);
}
~thread();
/**
* @brief Disallow copy assignment operator.
*/
thread& operator=(const thread&) = delete;
/**
* @brief Move assignment operator.
*/
thread& operator=(thread&&) noexcept;
/**
* @brief Swap threads.
* @param[inout] t Thread to swap data with.
*/
void swap(thread& t) noexcept {
std::swap(m_data, t.m_data);
std::swap(m_handle, t.m_handle);
}
/**
* @brief Query if the thread is joinable.
* @return `true` if the thread is joinable, `false` otherwise.
*/
inline bool joinable() const noexcept {
return m_handle != thread_uninitialized;
}
/**
* @brief Block until the thread finishes. Leads to an error if the thread is
* not joinable or a thread joins itself.
*/
void join();
/**
* @brief Detaches a thread from its handle and allows it to execute
* independently. The thread cleans up its resources when it
* finishes.
*/
void detach();
/**
* @brief Returns the id of a thread.
*/
inline id get_id() const noexcept { return thread_id{m_handle}; }
/**
* @brief Returns the native handle to a thread.
*/
inline native_handle_type native_handle() noexcept { return m_handle; }
/**
* @brief Returns the number of concurrent threads supported by the
* underlying hardware. Since there is no RIOT API to query this
* information, the function always returns 1;
*/
static unsigned hardware_concurrency() noexcept;
private:
kernel_pid_t m_handle;
std::unique_ptr m_data;
};
/**
* @brief Swaps two threads.
* @param[inout] lhs Reference to one thread.
* @param[inout] rhs Reference to the other thread.
*/
void swap(thread& lhs, thread& rhs) noexcept;
/** @cond INTERNAL */
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;
}
/** @endcond */
template
thread::thread(F&& f, Args&&... args)
: m_data{new thread_data} {
using namespace std;
using func_and_args = tuple
::type, typename decay::type...>;
unique_ptr p(
new func_and_args(m_data.get(), forward(f), forward(args)...));
m_handle = thread_create(
m_data->stack.data(), 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