mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
edc43201db
This should not change any generated binary
376 lines
9.8 KiB
C++
376 lines
9.8 KiB
C++
/*
|
|
* 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 <a href="http://en.cppreference.com/w/cpp/thread/thread">
|
|
* std::thread, std::this_thread
|
|
* </a>
|
|
*
|
|
* @author Raphael Hiesgen <raphael.hiesgen (at) haw-hamburg.de>
|
|
*
|
|
* @}
|
|
*/
|
|
|
|
#ifndef RIOT_THREAD_HPP
|
|
#define RIOT_THREAD_HPP
|
|
|
|
#include "time.h"
|
|
#include "thread.h"
|
|
|
|
#include <array>
|
|
#include <tuple>
|
|
#include <atomic>
|
|
#include <memory>
|
|
#include <utility>
|
|
#include <exception>
|
|
#include <stdexcept>
|
|
#include <functional>
|
|
#include <type_traits>
|
|
|
|
#include "riot/mutex.hpp"
|
|
#include "riot/chrono.hpp"
|
|
#include "riot/condition_variable.hpp"
|
|
|
|
#include "riot/detail/thread_util.hpp"
|
|
|
|
namespace riot {
|
|
|
|
/**
|
|
* @brief Holds context data for the thread.
|
|
*/
|
|
struct thread_data {
|
|
thread_data() : ref_count{2}, joining_thread{KERNEL_PID_UNDEF} {
|
|
// nop
|
|
}
|
|
/** @cond INTERNAL */
|
|
std::atomic<unsigned> ref_count;
|
|
kernel_pid_t joining_thread;
|
|
std::array<char, THREAD_STACKSIZE_MAIN> 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 <a href="http://en.cppreference.com/w/cpp/thread/thread/id">
|
|
* thread::id
|
|
* </a>
|
|
*/
|
|
class thread_id {
|
|
template <class T, class Traits>
|
|
friend std::basic_ostream<T, Traits>& operator<<(std::basic_ostream
|
|
<T, Traits>& out,
|
|
thread_id id);
|
|
friend class thread;
|
|
|
|
public:
|
|
/**
|
|
* @brief Creates a uninitialized thread id.
|
|
*/
|
|
inline thread_id() noexcept : m_handle{KERNEL_PID_UNDEF} {}
|
|
/**
|
|
* @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 <class T, class Traits>
|
|
inline std::basic_ostream<T, Traits>& operator<<(std::basic_ostream
|
|
<T, Traits>& 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] us Duration to sleep in microseconds.
|
|
*/
|
|
void sleep_for(const std::chrono::microseconds& us);
|
|
/**
|
|
* @brief Puts the current thread to sleep.
|
|
* @param[in] sleep_duration The duration to sleep.
|
|
*/
|
|
template <class Rep, class Period>
|
|
void sleep_for(const std::chrono::duration<Rep, Period>& sleep_duration) {
|
|
using namespace std::chrono;
|
|
if (sleep_duration > sleep_duration.zero()) {
|
|
constexpr duration<long double> max = microseconds::max();
|
|
microseconds us;
|
|
if (sleep_duration < max) {
|
|
us = duration_cast<microseconds>(sleep_duration);
|
|
if (us.count() == 0) {
|
|
// wait at least 1
|
|
us = microseconds(1);
|
|
}
|
|
if (us < sleep_duration) {
|
|
++us;
|
|
}
|
|
} else {
|
|
us = microseconds::max();
|
|
}
|
|
sleep_for(us);
|
|
}
|
|
}
|
|
/**
|
|
* @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<mutex> 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 <a href="http://en.cppreference.com/w/cpp/thread/thread">
|
|
* std::thread
|
|
* </a>
|
|
*/
|
|
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{KERNEL_PID_UNDEF} {}
|
|
/**
|
|
* @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 <class F, class... Args>
|
|
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 = KERNEL_PID_UNDEF;
|
|
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 != KERNEL_PID_UNDEF;
|
|
}
|
|
/**
|
|
* @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<thread_data, thread_data_deleter> 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 <class Tuple>
|
|
void* thread_proxy(void* vp) {
|
|
{ // without this scope, the objects here are not cleaned up correctly
|
|
std::unique_ptr<Tuple> p(static_cast<Tuple*>(vp));
|
|
auto tmp = std::get<0>(*p);
|
|
std::unique_ptr<thread_data, thread_data_deleter> data{tmp};
|
|
// create indices for the arguments, 0 is thread_data and 1 is the function
|
|
auto indices = detail::get_indices<std::tuple_size<Tuple>::value, 2>();
|
|
try {
|
|
detail::apply_args(std::get<1>(*p), indices, *p);
|
|
}
|
|
catch (...) {
|
|
// nop
|
|
}
|
|
if (data->joining_thread != KERNEL_PID_UNDEF) {
|
|
thread_wakeup(data->joining_thread);
|
|
}
|
|
}
|
|
// some riot cleanup code
|
|
sched_task_exit();
|
|
return nullptr;
|
|
}
|
|
/** @endcond */
|
|
|
|
template <class F, class... Args>
|
|
thread::thread(F&& f, Args&&... args) : m_data{new thread_data} {
|
|
using namespace std;
|
|
using func_and_args = tuple
|
|
<thread_data*, typename decay<F>::type, typename decay<Args>::type...>;
|
|
unique_ptr<func_and_args> p(
|
|
new func_and_args(m_data.get(), std::forward<F>(f), std::forward<Args>(args)...));
|
|
m_handle = thread_create(
|
|
m_data->stack.data(), m_data->stack.size(), THREAD_PRIORITY_MAIN - 1, 0,
|
|
&thread_proxy<func_and_args>, 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 != KERNEL_PID_UNDEF) {
|
|
std::terminate();
|
|
}
|
|
m_handle = other.m_handle;
|
|
other.m_handle = KERNEL_PID_UNDEF;
|
|
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
|