diff --git a/sys/cpp11-compat/include/riot/chrono.hpp b/sys/cpp11-compat/include/riot/chrono.hpp index 4f94738714..46a89fa51e 100644 --- a/sys/cpp11-compat/include/riot/chrono.hpp +++ b/sys/cpp11-compat/include/riot/chrono.hpp @@ -14,7 +14,7 @@ * @brief C++11 chrono drop in replacement that adds the function now based on * xtimer/timex * @see - * std::thread, defined in header + * std::thread, defined in header thread * * * @author Raphael Hiesgen @@ -38,30 +38,37 @@ constexpr uint32_t microsecs_in_sec = 1000000; } // namespace anaonymous /** - * @brief time point to use for timed wait, as stdlib clocks are not available + * @brief A time point for timed wait, as clocks from the standard are not + * available on RIOT. */ class time_point { using native_handle_type = timex_t; public: /** - * @brief create a time point with seconds and microseconds set to 0 + * @brief Creates a time point with seconds and microseconds set to 0. */ inline time_point() : m_handle{0, 0} {} /** - * @brief create time point from timex_t struct + * @brief Create time point from timex_t struct. */ inline time_point(timex_t&& tp) : m_handle(tp) {} + /** + * @brief Use default copy constructor. + */ constexpr time_point(const time_point& tp) = default; + /** + * @brief Use default move constructor. + */ constexpr time_point(time_point&& tp) = default; /** - * @brief get access to the handle used to store the time information + * @brief Gives access to the native handle that stores the time information. */ inline native_handle_type native_handle() const { return m_handle; } /** - * @brief add a stdlib chrono::duration to this time point + * @brief Add a standard chrono::duration to this time point. */ template inline time_point& operator+=(const std::chrono::duration& d) { @@ -74,12 +81,12 @@ class time_point { } /** - * @brief returns seconds member as uint32_t + * @brief Returns seconds member as uint32_t. */ inline uint32_t seconds() const { return m_handle.seconds; } /** - * @brief returns microseconds member as uint32_t + * @brief Returns microseconds member as uint32_t. */ inline uint32_t microseconds() const { return m_handle.microseconds; } @@ -93,9 +100,9 @@ class time_point { }; /** - * @brief get the current time saved in a time point + * @brief Returns the current time saved in a time point. * - * @return time_point containing the current time + * @return time_point containing the current time. */ inline time_point now() { timex_t tp; @@ -104,7 +111,7 @@ inline time_point now() { } /** - * @brief compare two timepoints + * @brief Compares two timepoints. */ inline bool operator<(const time_point& lhs, const time_point& rhs) { return lhs.seconds() < rhs.seconds() @@ -113,21 +120,21 @@ inline bool operator<(const time_point& lhs, const time_point& rhs) { } /** - * @brief compare two timepoints + * @brief Compares two timepoints. */ inline bool operator>(const time_point& lhs, const time_point& rhs) { return rhs < lhs; } /** - * @brief compare two timepoints + * @brief Compares two timepoints. */ inline bool operator<=(const time_point& lhs, const time_point& rhs) { return !(rhs < lhs); } /** - * @brief compare two timepoints + * @brief Compare two timepoints. */ inline bool operator>=(const time_point& lhs, const time_point& rhs) { return !(lhs < rhs); diff --git a/sys/cpp11-compat/include/riot/condition_variable.hpp b/sys/cpp11-compat/include/riot/condition_variable.hpp index 3f0a3014ae..4b6b7ede6f 100644 --- a/sys/cpp11-compat/include/riot/condition_variable.hpp +++ b/sys/cpp11-compat/include/riot/condition_variable.hpp @@ -33,49 +33,110 @@ namespace riot { +/** + * @brief Status for timeout-based calls of the condition variable. + */ enum class cv_status { no_timeout, timeout }; /** - * @brief C++11 complient implementation of condition variable, uses the time - * point implemented in our chrono replacement instead of the - * specified one + * @brief C++11 compliant implementation of condition variable, uses the time + * point implemented in our chrono replacement instead of the + * specified one * @see * std::condition_variable * */ class condition_variable { - public: +public: + /** + * @brief The native handle type used by the condition variable. + */ using native_handle_type = priority_queue_t*; - inline condition_variable() { m_queue.first = NULL; } ~condition_variable(); + /** + * @brief Notify one thread waiting on this condition. + */ void notify_one() noexcept; + /** + * @brief Notify all threads waiting on this condition variable. + */ void notify_all() noexcept; + /** + * @brief Block until woken up through the condition variable. + * @param lock A lock that is locked by the current thread. + */ void wait(unique_lock& lock) noexcept; + /** + * @brief Block until woken up through the condition variable and a predicate + * is fulfilled. + * @param lock A lock that is locked by the current thread. + * @param pred A predicate that returns a bool to signify if the thread + * should continue to wait when woken up through the cv. + */ template void wait(unique_lock& lock, Predicate pred); + /** + * @brief Block until woken up through the condition variable or a specified + * point in time is reached. The lock is reacquired either way. + * @param lock A lock that is locked by the current thread. + * @param timeout_time Point in time when the thread is woken up + * independently of the condition variable. + * @return A status to signify if woken up due to a timeout or the cv. + */ cv_status wait_until(unique_lock& lock, const time_point& timeout_time); + /** + * @brief Block until woken up through the condition variable and a predicate + * is fulfilled or a specified point in time is reached. The lock is + * reacquired either way. + * @param lock A lock that is locked by the current thread. + * @param timeout_time Point in time when the thread is woken up + * independently of the condition variable. + * @param pred A predicate that returns a bool to signify if the + * thread should continue to wait when woken up through + * the cv. + * @return Result of the pred when the function returns. + */ template bool wait_until(unique_lock& lock, const time_point& timeout_time, Predicate pred); + /** + * @brief Blocks until woken up through the condition variable or when the + * thread has been blocked for a certain time. + * @param lock A lock that is locked by the current thread. + * @param rel_time The maximum time spent blocking. + * @return A status to signify if woken up due to a timeout or the cv. + */ template cv_status wait_for(unique_lock& lock, const std::chrono::duration& rel_time); + /** + * @brief Blocks until woken up through the condition variable and a predicate + * is fulfilled or when the thread has been blocked for a certain time. + * @param lock A lock that is locked by the current thread. + * @param rel_time The maximum time spent blocking. + * @param pred A predicate that returns a bool to signify if the thread + * should continue to wait when woken up through the cv. + * @return Result of the pred when the function returns. + */ template bool wait_for(unique_lock& lock, const std::chrono::duration& rel_time, Predicate pred); + /** + * @brief Returns the native handle of the condition variable. + */ inline native_handle_type native_handle() { return &m_queue; } - private: +private: condition_variable(const condition_variable&); condition_variable& operator=(const condition_variable&); diff --git a/sys/cpp11-compat/include/riot/detail/thread_util.hpp b/sys/cpp11-compat/include/riot/detail/thread_util.hpp index 6c1e76a2c2..1952df4bec 100644 --- a/sys/cpp11-compat/include/riot/detail/thread_util.hpp +++ b/sys/cpp11-compat/include/riot/detail/thread_util.hpp @@ -29,34 +29,49 @@ namespace riot { namespace detail { /** - * A list of integers (wraps a long... template parameter pack). + * @brief A list of integers (wraps a long... template parameter pack). */ template struct int_list {}; /** - * Creates indices for from `Pos` to `Max`. + * @brief Creates indices from `Pos` to `Max`. */ template > struct il_indices; +/** + * @brief End of recursion, `Pos` reached `Max`. + */ template struct il_indices> { + /** + * @brief Result is the list containing `Is...`. + */ using type = int_list; }; +/** + * @brief Recursion step. + */ template struct il_indices> { + /** + * @brief Append `Pos` to list and increment for the next step. + */ using type = typename il_indices>::type; }; +/** + * @brief Function to create a list of indices from `From` to `To`. + */ template typename il_indices::type get_indices() { return {}; } /** - * apply arguments to function + * @brief Apply arguments in a tuple to function. */ template inline auto apply_args(F& f, detail::int_list, Tuple&& tup) @@ -64,12 +79,20 @@ inline auto apply_args(F& f, detail::int_list, Tuple&& tup) return f(std::get(tup)...); } +/** + * @brief Prefix the argument tuple with additonal arguments. + * In this case the tuple is empty. + */ template inline auto apply_args_prefixed(F& f, detail::int_list<>, Tuple&, Ts&&... args) -> decltype(f(std::forward(args)...)) { return f(std::forward(args)...); } +/** + * @brief Prefix the argument tuple with additonal arguments. + * In this case the tuple is contains arguments. + */ template inline auto apply_args_prefixed(F& f, detail::int_list, Tuple& tup, Ts&&... args) @@ -77,6 +100,9 @@ inline auto apply_args_prefixed(F& f, detail::int_list, Tuple& tup, return f(std::forward(args)..., std::get(tup)...); } +/** + * @brief Suffix the tuple with additonal arguments. + */ template inline auto apply_args_suffxied(F& f, detail::int_list, Tuple& tup, Ts&&... args) diff --git a/sys/cpp11-compat/include/riot/mutex.hpp b/sys/cpp11-compat/include/riot/mutex.hpp index ca808cc9db..39cecad8ea 100644 --- a/sys/cpp11-compat/include/riot/mutex.hpp +++ b/sys/cpp11-compat/include/riot/mutex.hpp @@ -41,31 +41,66 @@ namespace riot { * */ class mutex { - public: +public: + /** + * The native handle type used by the mutex. + */ using native_handle_type = mutex_t*; inline constexpr mutex() noexcept : m_mtx{{0}} {} ~mutex(); + /** + * @brief Lock the mutex. + */ void lock(); + /** + * @brief Try to lock the mutex. + * @return `true` if the mutex was locked, `false` otherwise. + */ bool try_lock() noexcept; + /** + * @brief Unlock the mutex. + */ void unlock() noexcept; + /** + * @brief Provides access to the native handle. + * @return The native handle of the mutex. + */ inline native_handle_type native_handle() { return &m_mtx; } - private: +private: mutex(const mutex&); mutex& operator=(const mutex&); mutex_t m_mtx; }; +/** + * @brief Tag type for defer lock strategy. + */ struct defer_lock_t {}; +/** + * @brief Tag type for try lock strategy. + */ struct try_to_lock_t {}; +/** + * @brief Tag type for adopt lock strategy. + */ struct adopt_lock_t {}; +/** + * @brief Tag constant for defer lock strategy. + */ constexpr defer_lock_t defer_lock = defer_lock_t(); +/** + * @brief Tag constant for try lock strategy. + */ constexpr try_to_lock_t try_to_lock = try_to_lock_t(); +/** + * @brief Tag constant for adopt lock strategy. + */ constexpr adopt_lock_t adopt_lock = adopt_lock_t(); /** @@ -76,15 +111,24 @@ constexpr adopt_lock_t adopt_lock = adopt_lock_t(); */ template class lock_guard { - - public: +public: + /** + * The type of Mutex used by the lock_guard. + */ using mutex_type = Mutex; + /** + * @brief Constructs a lock_gurad from a Mutex and locks it. + */ inline explicit lock_guard(mutex_type& mtx) : m_mtx(mtx) { m_mtx.lock(); } + /** + * @brief Constructs a lock_guard from a Mutex, acquireing ownership without + * locking it. + */ inline lock_guard(mutex_type& mtx, adopt_lock_t) : m_mtx{mtx} {} inline ~lock_guard() { m_mtx.unlock(); } - private: +private: mutex_type& m_mtx; }; @@ -96,18 +140,33 @@ class lock_guard { */ template class unique_lock { - - public: +public: + /** + * The type of Mutex used by the lock. + */ using mutex_type = Mutex; inline unique_lock() noexcept : m_mtx{nullptr}, m_owns{false} {} + /** + * @brief Constructs a unique_lock from a Mutex and locks it. + */ inline explicit unique_lock(mutex_type& mtx) : m_mtx{&mtx}, m_owns{true} { m_mtx->lock(); } + /** + * @brief Constructs a unique_lock from a Mutex but does not lock it. + */ inline unique_lock(mutex_type& mtx, defer_lock_t) noexcept : m_mtx{&mtx}, m_owns{false} {} + /** + * @brief Constructs a unique_lock from a Mutex and tries to lock it. + */ inline unique_lock(mutex_type& mtx, try_to_lock_t) : m_mtx{&mtx}, m_owns{mtx.try_lock()} {} + /** + * @brief Constructs a unique_lock from a Mutex that is already owned by the + * thread. + */ inline unique_lock(mutex_type& mtx, adopt_lock_t) : m_mtx{&mtx}, m_owns{true} {} inline ~unique_lock() { @@ -115,11 +174,17 @@ class unique_lock { m_mtx->unlock(); } } + /** + * @brief Move constructor. + */ inline unique_lock(unique_lock&& lock) noexcept : m_mtx{lock.m_mtx}, m_owns{lock.m_owns} { lock.m_mtx = nullptr; lock.m_owns = false; } + /** + * @brief Move assignment operator. + */ inline unique_lock& operator=(unique_lock&& lock) noexcept { if (m_owns) { m_mtx->unlock(); @@ -131,16 +196,34 @@ class unique_lock { return *this; } + /** + * @brief Locks the associated mutex. + */ void lock(); + /** + * @brief Tries to lock the associated mutex. + * @return `true` if the mutex has been locked successfully, + * `false` otherwise. + */ bool try_lock(); - + /** + * @brief Unlocks the associated mutex. + */ void unlock(); + /** + * @brief Swap this unique_lock with another unique_lock. + */ inline void swap(unique_lock& lock) noexcept { std::swap(m_mtx, lock.m_mtx); std::swap(m_owns, lock.m_owns); } + /** + * @brief Disassociate this lock from its mutex. The caller is responsible to + * unlock the mutex if it was locked before. + * @return A pointer to the associated mutex or `nullptr` if there was none. + */ inline mutex_type* release() noexcept { mutex_type* mtx = m_mtx; m_mtx = nullptr; @@ -148,11 +231,25 @@ class unique_lock { return mtx; } + /** + * @brief Query ownership of the associate mutex. + * @return `true` if an associated mutex exists and the lock owns it, + * `false` otherwise. + */ inline bool owns_lock() const noexcept { return m_owns; } + /** + * @brief Operator to query the ownership of the associated mutex. + * @return `true` if an associated mutex exists and the lock owns it, + * `false` otherwise. + */ inline explicit operator bool() const noexcept { return m_owns; } + /** + * @brief Provides access to the associated mutex. + * @return A pointer to the associated mutex or nullptr it there was none. + */ inline mutex_type* mutex() const noexcept { return m_mtx; } - private: +private: unique_lock(unique_lock const&); unique_lock& operator=(unique_lock const&); @@ -203,6 +300,11 @@ void unique_lock::unlock() { m_owns = false; } +/** + * @brief Swaps two mutexes. + * @param[inout] lhs Reference to one mutex. + * @param[inout] rhs Reference to the other mutex. + */ template inline void swap(unique_lock& lhs, unique_lock& rhs) noexcept { lhs.swap(rhs); diff --git a/sys/cpp11-compat/include/riot/thread.hpp b/sys/cpp11-compat/include/riot/thread.hpp index 82ab32e5e7..cba69852d2 100644 --- a/sys/cpp11-compat/include/riot/thread.hpp +++ b/sys/cpp11-compat/include/riot/thread.hpp @@ -45,24 +45,39 @@ 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; char stack[stack_size]; + /** @endcond */ }; /** - * This deleter prevents our thread data from being destroyed if the thread - * object is destroyed before the thread had a chance to run + * @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; @@ -83,33 +98,60 @@ class thread_id { thread_id id); friend class thread; - public: +public: + /** + * @brief Creates a uninitialized thread id. + */ inline thread_id() noexcept : m_handle{thread_uninitialized} {} + /** + * @brief Create a thread id from a native handle. + */ 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: +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, @@ -119,9 +161,23 @@ inline std::basic_ostream& operator<<(std::basic_ostream namespace this_thread { +/** + * @brief Access the id of the currently running thread. + */ inline thread_id get_id() noexcept { return 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; @@ -139,6 +195,11 @@ void sleep_for(const std::chrono::duration& sleep_duration) { 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; @@ -149,7 +210,7 @@ inline void sleep_until(const riot::time_point& 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 @@ -157,44 +218,100 @@ inline void sleep_until(const riot::time_point& sleep_time) { * */ class thread { - public: +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); - ~thread(); - 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(); + 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 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 @@ -217,6 +334,7 @@ void* thread_proxy(void* vp) { sched_task_exit(); return nullptr; } +/** @endcond */ template thread::thread(F&& f, Args&&... args) @@ -224,7 +342,7 @@ thread::thread(F&& f, Args&&... args) using namespace std; using func_and_args = tuple ::type, typename decay::type...>; - std::unique_ptr p( + 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,