/* * 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