
22 changed files with 1673 additions and 3 deletions
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2015 HAW Hamburg |
||||
* |
||||
* 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. |
||||
*/ |
||||
|
||||
/**
|
||||
* @defgroup core_sched Scheduler |
||||
* @ingroup core |
||||
* @brief The RIOT scheduler |
||||
* @details |
||||
* |
||||
* @{ |
||||
* |
||||
* @file native_sched.h |
||||
* @brief Add definitions required on the native board |
||||
* |
||||
* @author Raphael Hiesgen <raphael.hiesgen@haw-hamburg.de> |
||||
* @} |
||||
*/ |
||||
|
||||
#ifndef _NATIVE_SCHEDULER_H |
||||
#define _NATIVE_SCHEDULER_H |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
#ifdef BOARD_NATIVE |
||||
#include <stdio.h> |
||||
/*
|
||||
* Required to use some C++11 headers with g++ on the native board. |
||||
*/ |
||||
#define __CPU_SETSIZE 1024 |
||||
#define __NCPUBITS (8* sizeof(__cpu_mask)) |
||||
typedef unsigned long int __cpu_mask; |
||||
typedef struct { |
||||
__cpu_mask __bits[__CPU_SETSIZE / __NCPUBITS]; |
||||
} cpu_set_t; |
||||
|
||||
/**
|
||||
* @brief In all test the function has never been called, hence it is empty for now. |
||||
*/ |
||||
inline int sched_yield(void) |
||||
{ |
||||
puts("[ERROR] sched_yield called (defined in sched.h)\n"); |
||||
return 0; |
||||
} |
||||
#endif // BOARD_NATIVE
|
||||
|
||||
#ifdef __cplusplus |
||||
} |
||||
#endif |
||||
|
||||
#endif // _NATIVE_SCHEDULER_H
|
@ -0,0 +1,4 @@
|
||||
# This module requires cpp 11
|
||||
CXXEXFLAGS += -std=c++11
|
||||
|
||||
include $(RIOTBASE)/Makefile.base |
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 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 condition_variable.cpp |
||||
* @brief C++11 condition variable drop in replacement |
||||
* |
||||
* @author Raphael Hiesgen <raphael.hiesgen (at) haw-hamburg.de> |
||||
* |
||||
* @} |
||||
*/ |
||||
|
||||
#include <cstdio> |
||||
#include <stdexcept> |
||||
#include <system_error> |
||||
|
||||
#include "irq.h" |
||||
#include "sched.h" |
||||
#include "vtimer.h" |
||||
#include "priority_queue.h" |
||||
|
||||
#include "riot/condition_variable.hpp" |
||||
|
||||
using namespace std::chrono; |
||||
|
||||
namespace riot { |
||||
|
||||
condition_variable::~condition_variable() { m_queue.first = NULL; } |
||||
|
||||
void condition_variable::notify_one() noexcept { |
||||
unsigned old_state = disableIRQ(); |
||||
priority_queue_node_t* head = priority_queue_remove_head(&m_queue); |
||||
int other_prio = -1; |
||||
if (head != NULL) { |
||||
tcb_t* other_thread = (tcb_t*)sched_threads[head->data]; |
||||
if (other_thread) { |
||||
other_prio = other_thread->priority; |
||||
sched_set_status(other_thread, STATUS_PENDING); |
||||
} |
||||
head->data = -1u; |
||||
} |
||||
restoreIRQ(old_state); |
||||
if (other_prio >= 0) { |
||||
sched_switch(other_prio); |
||||
} |
||||
} |
||||
|
||||
void condition_variable::notify_all() noexcept { |
||||
unsigned old_state = disableIRQ(); |
||||
int other_prio = -1; |
||||
while (true) { |
||||
priority_queue_node_t* head = priority_queue_remove_head(&m_queue); |
||||
if (head == NULL) { |
||||
break; |
||||
} |
||||
tcb_t* other_thread = (tcb_t*)sched_threads[head->data]; |
||||
if (other_thread) { |
||||
auto max_prio |
||||
= [](int a, int b) { return (a < 0) ? b : ((a < b) ? a : b); }; |
||||
other_prio = max_prio(other_prio, other_thread->priority); |
||||
sched_set_status(other_thread, STATUS_PENDING); |
||||
} |
||||
head->data = -1u; |
||||
} |
||||
restoreIRQ(old_state); |
||||
if (other_prio >= 0) { |
||||
sched_switch(other_prio); |
||||
} |
||||
} |
||||
|
||||
void condition_variable::wait(unique_lock<mutex>& lock) noexcept { |
||||
if (!lock.owns_lock()) { |
||||
throw std::system_error( |
||||
std::make_error_code(std::errc::operation_not_permitted), |
||||
"Mutex not locked."); |
||||
} |
||||
priority_queue_node_t n; |
||||
n.priority = sched_active_thread->priority; |
||||
n.data = sched_active_pid; |
||||
n.next = NULL; |
||||
// the signaling thread may not hold the mutex, the queue is not thread safe
|
||||
unsigned old_state = disableIRQ(); |
||||
priority_queue_add(&m_queue, &n); |
||||
restoreIRQ(old_state); |
||||
mutex_unlock_and_sleep(lock.mutex()->native_handle()); |
||||
if (n.data != -1u) { |
||||
// on signaling n.data is set to -1u
|
||||
// if it isn't set, then the wakeup is either spurious or a timer wakeup
|
||||
old_state = disableIRQ(); |
||||
priority_queue_remove(&m_queue, &n); |
||||
restoreIRQ(old_state); |
||||
} |
||||
mutex_lock(lock.mutex()->native_handle()); |
||||
} |
||||
|
||||
cv_status condition_variable::wait_until(unique_lock<mutex>& lock, |
||||
const time_point& timeout_time) { |
||||
vtimer_t timer; |
||||
// todo: use function to wait for absolute timepoint once available
|
||||
timex_t before; |
||||
vtimer_now(&before); |
||||
auto diff = timex_sub(timeout_time.native_handle(), before); |
||||
vtimer_set_wakeup(&timer, diff, sched_active_pid); |
||||
wait(lock); |
||||
timex_t after; |
||||
vtimer_now(&after); |
||||
vtimer_remove(&timer); |
||||
auto cmp = timex_cmp(after, timeout_time.native_handle()); |
||||
return cmp < 1 ? cv_status::no_timeout : cv_status::timeout; |
||||
} |
||||
|
||||
} // namespace riot
|
@ -0,0 +1,13 @@
|
||||
/* |
||||
* 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. |
||||
*/ |
||||
|
||||
/** |
||||
* @defgroup cpp11-compat C++11 wrapper for RIOT |
||||
* @brief drop in replacement to enable C++11-like thread, mutex and condition_variable |
||||
* @ingroup sys |
||||
*/ |
@ -0,0 +1,139 @@
|
||||
/*
|
||||
* 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 chrono.hpp |
||||
* @brief C++11 chrono drop in replacement that adds the function now based on |
||||
* vtimer/timex |
||||
* @see <a href="http://en.cppreference.com/w/cpp/thread/thread"> |
||||
* std::thread, defined in header <thread> |
||||
* </a> |
||||
* |
||||
* @author Raphael Hiesgen <raphael.hiesgen (at) haw-hamburg.de> |
||||
* |
||||
* @} |
||||
*/ |
||||
|
||||
#ifndef RIOT_CHRONO_HPP |
||||
#define RIOT_CHRONO_HPP |
||||
|
||||
#include <chrono> |
||||
#include <cstdio> |
||||
#include <algorithm> |
||||
|
||||
#include "time.h" |
||||
#include "vtimer.h" |
||||
|
||||
namespace riot { |
||||
|
||||
namespace { |
||||
constexpr uint32_t microsecs_in_sec = 1000000; |
||||
} // namespace anaonymous
|
||||
|
||||
/**
|
||||
* @brief time point to use for timed wait, as stdlib clocks are not available |
||||
*/ |
||||
class time_point { |
||||
using native_handle_type = timex_t; |
||||
|
||||
public: |
||||
/**
|
||||
* @brief create 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
|
||||
*/ |
||||
inline time_point(timex_t&& tp) : m_handle(tp) {} |
||||
constexpr time_point(const time_point& tp) = default; |
||||
constexpr time_point(time_point&& tp) = default; |
||||
|
||||
/**
|
||||
* @brief get access to the handle used to store the time information |
||||
*/ |
||||
inline native_handle_type native_handle() const { return m_handle; } |
||||
|
||||
/**
|
||||
* @brief add a stdlib chrono::duration to this time point |
||||
*/ |
||||
template <class Rep, class Period> |
||||
inline time_point& operator+=(const std::chrono::duration<Rep, Period>& d) { |
||||
auto s = std::chrono::duration_cast<std::chrono::seconds>(d); |
||||
auto m = (std::chrono::duration_cast<std::chrono::microseconds>(d) - s); |
||||
m_handle.seconds += s.count(); |
||||
m_handle.microseconds += m.count(); |
||||
adjust_overhead(); |
||||
return *this; |
||||
} |
||||
|
||||
/**
|
||||
* @brief returns seconds member as uint32_t |
||||
*/ |
||||
inline uint32_t seconds() const { return m_handle.seconds; } |
||||
|
||||
/**
|
||||
* @brief returns microseconds member as uint32_t |
||||
*/ |
||||
inline uint32_t microseconds() const { return m_handle.microseconds; } |
||||
|
||||
private: |
||||
timex_t m_handle; |
||||
void inline adjust_overhead() { |
||||
auto secs = m_handle.microseconds / microsecs_in_sec; |
||||
m_handle.seconds += secs; |
||||
m_handle.microseconds -= (secs * microsecs_in_sec); |
||||
} |
||||
}; |
||||
|
||||
/**
|
||||
* @brief get the current time saved in a time point |
||||
* |
||||
* @return time_point containing the current time |
||||
*/ |
||||
inline time_point now() { |
||||
timex_t tp; |
||||
vtimer_now(&tp); |
||||
return time_point(std::move(tp)); |
||||
} |
||||
|
||||
/**
|
||||
* @brief compare two timepoints |
||||
*/ |
||||
inline bool operator<(const time_point& lhs, const time_point& rhs) { |
||||
return lhs.seconds() < rhs.seconds() |
||||
|| (lhs.seconds() == rhs.seconds() && lhs.microseconds() |
||||
< rhs.microseconds()); |
||||
} |
||||
|
||||
/**
|
||||
* @brief compare two timepoints |
||||
*/ |
||||
inline bool operator>(const time_point& lhs, const time_point& rhs) { |
||||
return rhs < lhs; |
||||
} |
||||
|
||||
/**
|
||||
* @brief compare two timepoints |
||||
*/ |
||||
inline bool operator<=(const time_point& lhs, const time_point& rhs) { |
||||
return !(rhs < lhs); |
||||
} |
||||
|
||||
/**
|
||||
* @brief compare two timepoints |
||||
*/ |
||||
inline bool operator>=(const time_point& lhs, const time_point& rhs) { |
||||
return !(lhs < rhs); |
||||
} |
||||
|
||||
} // namespace riot
|
||||
|
||||
#endif // RIOT_CHRONO_HPP
|
@ -0,0 +1,139 @@
|
||||
/*
|
||||
* 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 condition_variable.hpp |
||||
* @brief C++11 condition variable drop in replacement |
||||
* @see <a href="http://en.cppreference.com/w/cpp/thread/condition_variable"> |
||||
* std::condition_variable |
||||
* </a> |
||||
* |
||||
* @author Raphael Hiesgen <raphael.hiesgen (at) haw-hamburg.de> |
||||
* |
||||
* @} |
||||
*/ |
||||
|
||||
#ifndef RIOT_CONDITION_VARIABLE_HPP |
||||
#define RIOT_CONDITION_VARIABLE_HPP |
||||
|
||||
#include "sched.h" |
||||
#include "vtimer.h" |
||||
|
||||
#include "riot/mutex.hpp" |
||||
#include "riot/chrono.hpp" |
||||
|
||||
namespace riot { |
||||
|
||||
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 |
||||
* @see <a href="http://en.cppreference.com/w/cpp/thread/condition_variable"> |
||||
* std::condition_variable |
||||
* </a> |
||||
*/ |
||||
class condition_variable { |
||||
public: |
||||
using native_handle_type = priority_queue_t*; |
||||
|
||||
inline condition_variable() { m_queue.first = NULL; } |
||||
~condition_variable(); |
||||
|
||||
void notify_one() noexcept; |
||||
void notify_all() noexcept; |
||||
|
||||
void wait(unique_lock<mutex>& lock) noexcept; |
||||
template <class Predicate> |
||||
void wait(unique_lock<mutex>& lock, Predicate pred); |
||||
cv_status wait_until(unique_lock<mutex>& lock, |
||||
const time_point& timeout_time); |
||||
template <class Predicate> |
||||
bool wait_until(unique_lock<mutex>& lock, const time_point& timeout_time, |
||||
Predicate pred); |
||||
|
||||
template <class Rep, class Period> |
||||
cv_status wait_for(unique_lock<mutex>& lock, |
||||
const std::chrono::duration<Rep, Period>& rel_time); |
||||
template <class Rep, class Period, class Predicate> |
||||
bool wait_for(unique_lock<mutex>& lock, |
||||
const std::chrono::duration<Rep, Period>& rel_time, |
||||
Predicate pred); |
||||
|
||||
inline native_handle_type native_handle() { return &m_queue; } |
||||
|
||||
private: |
||||
condition_variable(const condition_variable&); |
||||
condition_variable& operator=(const condition_variable&); |
||||
|
||||
priority_queue_t m_queue; |
||||
}; |
||||
|
||||
template <class Predicate> |
||||
void condition_variable::wait(unique_lock<mutex>& lock, Predicate pred) { |
||||
while (!pred()) { |
||||
wait(lock); |
||||
} |
||||
} |
||||
|
||||
template <class Predicate> |
||||
bool condition_variable::wait_until(unique_lock<mutex>& lock, |
||||
const time_point& timeout_time, |
||||
Predicate pred) { |
||||
while (!pred()) { |
||||
if (wait_until(lock, timeout_time) == cv_status::timeout) { |
||||
return pred(); |
||||
} |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
template <class Rep, class Period> |
||||
cv_status condition_variable::wait_for(unique_lock<mutex>& lock, |
||||
const std::chrono::duration |
||||
<Rep, Period>& timeout_duration) { |
||||
using namespace std::chrono; |
||||
using std::chrono::duration; |
||||
if (timeout_duration <= timeout_duration.zero()) { |
||||
return cv_status::timeout; |
||||
} |
||||
timex_t timeout, before, after; |
||||
auto s = duration_cast<seconds>(timeout_duration); |
||||
timeout.seconds = s.count(); |
||||
timeout.microseconds |
||||
= (duration_cast<microseconds>(timeout_duration - s)).count(); |
||||
vtimer_now(&before); |
||||
vtimer_t timer; |
||||
vtimer_set_wakeup(&timer, timeout, sched_active_pid); |
||||
wait(lock); |
||||
vtimer_now(&after); |
||||
vtimer_remove(&timer); |
||||
auto passed = timex_sub(after, before); |
||||
auto cmp = timex_cmp(passed, timeout); |
||||
return cmp < 1 ? cv_status::no_timeout : cv_status::timeout; |
||||
} |
||||
|
||||
template <class Rep, class Period, class Predicate> |
||||
inline bool condition_variable::wait_for(unique_lock<mutex>& lock, |
||||
const std::chrono::duration |
||||
<Rep, Period>& timeout_duration, |
||||
Predicate pred) { |
||||
return wait_until(lock, std::chrono::steady_clock::now() + timeout_duration, |
||||
std::move(pred)); |
||||
} |
||||
|
||||
} // namespace riot
|
||||
|
||||
#endif // RIOT_CONDITION_VARIABLE_HPP
|
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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 thread_util.hpp |
||||
* @brief utility functions |
||||
* |
||||
* @author Dominik Charousset <dominik.charousset (at) haw-hamburg.de> |
||||
* @author Raphael Hiesgen <raphael.hiesgen (at) haw-hamburg.de> |
||||
* |
||||
* @} |
||||
*/ |
||||
|
||||
#ifndef RIOT_THREAD_UTILS_HPP |
||||
#define RIOT_THREAD_UTILS_HPP |
||||
|
||||
#include <tuple> |
||||
#include <utility> |
||||
|
||||
namespace riot { |
||||
namespace detail { |
||||
|
||||
/**
|
||||
* A list of integers (wraps a long... template parameter pack). |
||||
*/ |
||||
template <long... Is> |
||||
struct int_list {}; |
||||
|
||||
/**
|
||||
* Creates indices for from `Pos` to `Max`. |
||||
*/ |
||||
template <long Max, long Pos = 0, typename Indices = int_list<>> |
||||
struct il_indices; |
||||
|
||||
template <long Pos, long... Is> |
||||
struct il_indices<Pos, Pos, int_list<Is...>> { |
||||
using type = int_list<Is...>; |
||||
}; |
||||
|
||||
template <long Max, long Pos, long... Is> |
||||
struct il_indices<Max, Pos, int_list<Is...>> { |
||||
using type = typename il_indices<Max, Pos + 1, int_list<Is..., Pos>>::type; |
||||
}; |
||||
|
||||
template <long To, long From = 0> |
||||
typename il_indices<To, From>::type get_indices() { |
||||
return {}; |
||||
} |
||||
|
||||
/**
|
||||
* apply arguments to function |
||||
*/ |
||||
template <class F, long... Is, class Tuple> |
||||
inline auto apply_args(F& f, detail::int_list<Is...>, Tuple&& tup) |
||||
-> decltype(f(std::get<Is>(tup)...)) { |
||||
return f(std::get<Is>(tup)...); |
||||
} |
||||
|
||||
template <class F, class Tuple, class... Ts> |
||||
inline auto apply_args_prefixed(F& f, detail::int_list<>, Tuple&, Ts&&... args) |
||||
-> decltype(f(std::forward<Ts>(args)...)) { |
||||
return f(std::forward<Ts>(args)...); |
||||
} |
||||
|
||||
template <class F, long... Is, class Tuple, class... Ts> |
||||
inline auto apply_args_prefixed(F& f, detail::int_list<Is...>, Tuple& tup, |
||||
Ts&&... args) |
||||
-> decltype(f(std::forward<Ts>(args)..., std::get<Is>(tup)...)) { |
||||
return f(std::forward<Ts>(args)..., std::get<Is>(tup)...); |
||||
} |
||||
|
||||
template <class F, long... Is, class Tuple, class... Ts> |
||||
inline auto apply_args_suffxied(F& f, detail::int_list<Is...>, Tuple& tup, |
||||
Ts&&... args) |
||||
-> decltype(f(std::get<Is>(tup)..., std::forward<Ts>(args)...)) { |
||||
return f(std::get<Is>(tup)..., std::forward<Ts>(args)...); |
||||
} |
||||
|
||||
} // namespace detail
|
||||
} // namespace riot
|
||||
|
||||
#endif // RIOT_THREAD_UTILS_HPP
|
@ -0,0 +1,213 @@
|
||||
/*
|
||||
* 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 mutex.hpp |
||||
* @brief C++11 mutex drop in replacement |
||||
* @see <a href="http://en.cppreference.com/w/cpp/thread/mutex"> |
||||
* std::mutex, std::lock_guard and std::unique_lock |
||||
* </a> |
||||
* |
||||
* @author Raphael Hiesgen <raphael.hiesgen (at) haw-hamburg.de> |
||||
* |
||||
* @} |
||||
*/ |
||||
|
||||
#ifndef RIOT_MUTEX_HPP |
||||
#define RIOT_MUTEX_HPP |
||||
|
||||
#include "mutex.h" |
||||
|
||||
#include <utility> |
||||
#include <stdexcept> |
||||
#include <system_error> |
||||
|
||||
namespace riot { |
||||
|
||||
/**
|
||||
* @brief C++11 complient implementation of mutex, uses the time point |
||||
* implemented in our chrono replacement instead of the specified |
||||
* one |
||||
* @see <a href="http://en.cppreference.com/w/cpp/thread/mutex"> |
||||
* std::mutex |
||||
* </a> |
||||
*/ |
||||
class mutex { |
||||
public: |
||||
using native_handle_type = mutex_t*; |
||||
|
||||
inline constexpr mutex() noexcept : m_mtx{0, PRIORITY_QUEUE_INIT} {} |
||||
~mutex(); |
||||
|
||||
void lock(); |
||||
bool try_lock() noexcept; |
||||
void unlock() noexcept; |
||||
|
||||
inline native_handle_type native_handle() { return &m_mtx; } |
||||
|
||||
private: |
||||
mutex(const mutex&); |
||||
mutex& operator=(const mutex&); |
||||
|
||||
mutex_t m_mtx; |
||||
}; |
||||
|
||||
struct defer_lock_t {}; |
||||
struct try_to_lock_t {}; |
||||
struct adopt_lock_t {}; |
||||
|
||||
constexpr defer_lock_t defer_lock = defer_lock_t(); |
||||
constexpr try_to_lock_t try_to_lock = try_to_lock_t(); |
||||
constexpr adopt_lock_t adopt_lock = adopt_lock_t(); |
||||
|
||||
/**
|
||||
* @brief C++11 complient implementation of unique lock |
||||
* @see <a href="http://en.cppreference.com/w/cpp/thread/lock_guard"> |
||||
* std::lock_guard |
||||
* </a> |
||||
*/ |
||||
template <class Mutex> |
||||
class lock_guard { |
||||
|
||||
public: |
||||
using mutex_type = Mutex; |
||||
|
||||
inline explicit lock_guard(mutex_type& mtx) : m_mtx(mtx) { m_mtx.lock(); } |
||||
inline lock_guard(mutex_type& mtx, adopt_lock_t) : m_mtx{mtx} {} |
||||
inline ~lock_guard() { m_mtx.unlock(); } |
||||
|
||||
private: |
||||
mutex_type& m_mtx; |
||||
}; |
||||
|
||||
/**
|
||||
* @brief C++11 complient implementation of unique lock |
||||
* @see <a href="http://en.cppreference.com/w/cpp/thread/unique_lock"> |
||||
* std::unique_lock |
||||
* </a> |
||||
*/ |
||||
template <class Mutex> |
||||
class unique_lock { |
||||
|
||||
public: |
||||
using mutex_type = Mutex; |
||||
|
||||
inline unique_lock() noexcept : m_mtx{nullptr}, m_owns{false} {} |
||||
inline explicit unique_lock(mutex_type& mtx) : m_mtx{&mtx}, m_owns{true} { |
||||
m_mtx->lock(); |
||||
} |
||||
inline unique_lock(mutex_type& mtx, defer_lock_t) noexcept : m_mtx{&mtx}, |
||||
m_owns{false} {} |
||||
inline unique_lock(mutex_type& mtx, try_to_lock_t) |
||||
: m_mtx{&mtx}, m_owns{mtx.try_lock()} {} |
||||
inline unique_lock(mutex_type& mtx, adopt_lock_t) |
||||
: m_mtx{&mtx}, m_owns{true} {} |
||||
inline ~unique_lock() { |
||||
if (m_owns) { |
||||
m_mtx->unlock(); |
||||
} |
||||
} |
||||
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; |
||||
} |
||||
inline unique_lock& operator=(unique_lock&& lock) noexcept { |
||||
if (m_owns) { |
||||
m_mtx->unlock(); |
||||
} |
||||
m_mtx = lock.m_mtx; |
||||
m_owns = lock.m_owns; |
||||
lock.m_mtx = nullptr; |
||||
lock.m_owns = false; |
||||
return *this; |
||||
} |
||||
|
||||
void lock(); |
||||
bool try_lock(); |
||||
|
||||
void unlock(); |
||||
|
||||
inline void swap(unique_lock& lock) noexcept { |
||||
std::swap(m_mtx, lock.m_mtx); |
||||
std::swap(m_owns, lock.m_owns); |
||||
} |
||||
|
||||
inline mutex_type* release() noexcept { |
||||
mutex_type* mtx = m_mtx; |
||||
m_mtx = nullptr; |
||||
m_owns = false; |
||||
return mtx; |
||||
} |
||||
|
||||
inline bool owns_lock() const noexcept { return m_owns; } |
||||
inline explicit operator bool() const noexcept { return m_owns; } |
||||
inline mutex_type* mutex() const noexcept { return m_mtx; } |
||||
|
||||
private: |
||||
unique_lock(unique_lock const&); |
||||
unique_lock& operator=(unique_lock const&); |
||||
|
||||
mutex_type* m_mtx; |
||||
bool m_owns; |
||||
}; |
||||
|
||||
template <class Mutex> |
||||
void unique_lock<Mutex>::lock() { |
||||
if (m_mtx == nullptr) { |
||||
throw std::system_error( |
||||
std::make_error_code(std::errc::operation_not_permitted), |
||||
"References null mutex."); |
||||
} |
||||
if (m_owns) { |
||||
throw std::system_error( |
||||
std::make_error_code(std::errc::resource_deadlock_would_occur), |
||||
"Already locked."); |
||||
} |
||||
m_mtx->lock(); |
||||
m_owns = true; |
||||
} |
||||
|
||||
template <class Mutex> |
||||
bool unique_lock<Mutex>::try_lock() { |
||||
if (m_mtx == nullptr) { |
||||
throw std::system_error( |
||||
std::make_error_code(std::errc::operation_not_permitted), |
||||
"References null mutex."); |
||||
} |
||||
if (m_owns) { |
||||
throw std::system_error( |
||||
std::make_error_code(std::errc::resource_deadlock_would_occur), |
||||
"Already locked."); |
||||
} |
||||
m_owns = m_mtx->try_lock(); |
||||
return m_owns; |
||||
} |
||||
|
||||
template <class Mutex> |
||||
void unique_lock<Mutex>::unlock() { |
||||
if (!m_owns) { |
||||
throw std::system_error( |
||||
std::make_error_code(std::errc::operation_not_permitted), |
||||
"Mutex not locked."); |
||||
} |
||||
m_mtx->unlock(); |
||||
m_owns = false; |
||||
} |
||||
|
||||
template <class Mutex> |
||||
inline void swap(unique_lock<Mutex>& lhs, unique_lock<Mutex>& rhs) noexcept { |
||||
lhs.swap(rhs); |
||||
} |
||||
|
||||
} // namespace riot
|
||||
|
||||
#endif // RIOT_MUTEX_HPP
|
@ -0,0 +1,256 @@
|
||||
/*
|
||||
* 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 thread.hpp |
||||
* @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 "kernel_internal.h" |
||||
|
||||
#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 { |
||||
|
||||
namespace { |
||||
constexpr kernel_pid_t thread_uninitialized = -1; |
||||
constexpr size_t stack_size = KERNEL_CONF_STACKSIZE_MAIN; |
||||
} |
||||
|
||||
struct thread_data { |
||||
thread_data() : ref_count{2}, joining_thread{thread_uninitialized} { |
||||
// nop
|
||||
} |
||||
std::atomic<unsigned> 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 <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: |
||||
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 <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 { |
||||
|
||||
inline thread_id get_id() noexcept { return thread_getpid(); } |
||||
inline void yield() noexcept { thread_yield(); } |
||||
void sleep_for(const std::chrono::nanoseconds& ns); |
||||
template <class Rep, class Period> |
||||
void sleep_for(const std::chrono::duration<Rep, Period>& sleep_duration) { |
||||
using namespace std::chrono; |
||||
if (sleep_duration > std::chrono::duration<Rep, Period>::zero()) { |
||||
constexpr std::chrono::duration<long double> max = nanoseconds::max(); |
||||
nanoseconds ns; |
||||
if (sleep_duration < max) { |
||||
ns = duration_cast<nanoseconds>(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<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: |
||||
using id = thread_id; |
||||
using native_handle_type = kernel_pid_t; |
||||
|
||||
inline thread() noexcept : m_handle{thread_uninitialized} {} |
||||
template <class F, class... Args> |
||||
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<thread_data, thread_data_deleter> m_data; |
||||
}; |
||||
|
||||
void swap(thread& lhs, thread& rhs) noexcept; |
||||
|
||||
template <class Tuple> |
||||
void* thread_proxy(void* vp) { |
||||
{ // without this scope, the objects here are not cleaned up corrctly
|
||||
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 != thread_uninitialized) { |
||||
thread_wakeup(data->joining_thread); |
||||
} |
||||
} |
||||
// some riot cleanup code
|
||||
sched_task_exit(); |
||||
return nullptr; |
||||
} |
||||
|
||||
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...>; |
||||
std::unique_ptr<func_and_args> p( |
||||
new func_and_args(m_data.get(), forward<F>(f), forward<Args>(args)...)); |
||||
m_handle = thread_create( |
||||
m_data->stack, stack_size, PRIORITY_MAIN - 1, 0, // CREATE_WOUT_YIELD
|
||||
&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 != 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
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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 mutex.cpp |
||||
* @brief C++11 mutex drop in replacement |
||||
* |
||||
* @author Raphael Hiesgen <raphael.hiesgen (at) haw-hamburg.de> |
||||
* |
||||
* @} |
||||
*/ |
||||
|
||||
#include "riot/mutex.hpp" |
||||
|
||||
namespace riot { |
||||
|
||||
mutex::~mutex() { |
||||
// nop
|
||||
} |
||||
|
||||
void mutex::lock() { mutex_lock(&m_mtx); } |
||||
|
||||
bool mutex::try_lock() noexcept { return (1 == mutex_trylock(&m_mtx)); } |
||||
|
||||
void mutex::unlock() noexcept { mutex_unlock(&m_mtx); } |
||||
|
||||
} // namespace riot
|
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* 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 thread.cpp |
||||
* @brief C++11 thread drop in replacement |
||||
* |
||||
* @author Raphael Hiesgen <raphael.hiesgen (at) haw-hamburg.de> |
||||
* |
||||
* @} |
||||
*/ |
||||
|
||||
#include "vtimer.h" |
||||
|
||||
#include <cerrno> |
||||
#include <system_error> |
||||
|
||||
#include "riot/thread.hpp" |
||||
|
||||
using namespace std; |
||||
|
||||
namespace riot { |
||||
|
||||
thread::~thread() { |
||||
if (joinable()) { |
||||
terminate(); |
||||
} |
||||
} |
||||
|
||||
void thread::join() { |
||||
if (this->get_id() == this_thread::get_id()) { |
||||
throw system_error(make_error_code(errc::resource_deadlock_would_occur), |
||||
"Joining this leads to a deadlock."); |
||||
} |
||||
if (joinable()) { |
||||
auto status = thread_getstatus(m_handle); |
||||
if (status != STATUS_NOT_FOUND && status != STATUS_STOPPED) { |
||||
m_data->joining_thread = sched_active_pid; |
||||
thread_sleep(); |
||||
} |
||||
m_handle = thread_uninitialized; |
||||
} else { |
||||
throw system_error(make_error_code(errc::invalid_argument), |
||||
"Can not join an unjoinable thread."); |
||||
} |
||||
// missing: no_such_process system error
|
||||
} |
||||
|
||||
void thread::detach() { |
||||
if (joinable()) { |
||||
m_handle = thread_uninitialized; |
||||
} else { |
||||
throw system_error(make_error_code(errc::invalid_argument), |
||||
"Can not detach an unjoinable thread."); |
||||
} |
||||
} |
||||
|
||||
unsigned thread::hardware_concurrency() noexcept { |
||||
// there is currently no API for this
|
||||
return 1; |
||||
} |
||||
|
||||
namespace this_thread { |
||||
|
||||
void sleep_for(const chrono::nanoseconds& ns) { |
||||
using namespace chrono; |
||||
if (ns > nanoseconds::zero()) { |
||||
seconds s = duration_cast<seconds>(ns); |
||||
timespec ts; |
||||
using ts_sec = decltype(ts.tv_sec); |
||||
constexpr ts_sec ts_sec_max = numeric_limits<ts_sec>::max(); |
||||
if (s.count() < ts_sec_max) { |
||||
ts.tv_sec = static_cast<ts_sec>(s.count()); |
||||
ts.tv_nsec = static_cast<decltype(ts.tv_nsec)>((ns - s).count()); |
||||
} else { |
||||
ts.tv_sec = ts_sec_max; |
||||
ts.tv_nsec = giga::num - 1; |
||||
} |
||||
timex_t reltime; |
||||
reltime.seconds = ts.tv_sec; |
||||
reltime.microseconds = ts.tv_nsec / 1000u; |
||||
vtimer_t timer; |
||||
vtimer_set_wakeup(&timer, reltime, sched_active_pid); |
||||
thread_sleep(); |
||||
} |
||||
} |
||||
|
||||
} // namespace this_thread
|
||||
|
||||
} // namespace riot
|
@ -0,0 +1,28 @@
|
||||
# name of your application
|
||||
APPLICATION = cpp11_condition_variable
|
||||
|
||||
# If no BOARD is found in the environment, use this default:
|
||||
BOARD ?= native
|
||||
|
||||
# This has to be the absolute path to the RIOT base directory:
|
||||
RIOTBASE ?= $(CURDIR)/../..
|
||||
|
||||
# Comment this out to disable code in RIOT that does safety checking
|
||||
# which is not needed in a production environment but helps in the
|
||||
# development process:
|
||||
CFLAGS += -DDEVELHELP -Wno-deprecated
|
||||
|
||||
# Change this to 0 show compiler invocation lines by default:
|
||||
QUIET ?= 1
|
||||
|
||||
BOARD_WHITELIST := stm32f4discovery native
|
||||
|
||||
# If you want to add some extra flags when compile c++ files, add these flags
|
||||
# to CXXEXFLAGS variable
|
||||
CXXEXFLAGS += -std=c++11 -g -O0 -Wno-deprecated
|
||||
|
||||
USEMODULE += cpp11-compat
|
||||
USEMODULE += vtimer
|
||||
USEMODULE += timex
|
||||
|
||||
include $(RIOTBASE)/Makefile.include |
@ -0,0 +1,130 @@
|
||||
/*
|
||||
* 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 tests |
||||
* @{ |
||||
* |
||||
* @file |
||||
* @brief test condition variable replacement header |
||||
* |
||||
* @author Raphael Hiesgen <raphael.hiesgen@haw-hamburg.de> |
||||
* |
||||
* @} |
||||
*/ |
||||
|
||||
#include <string> |
||||
#include <cstdio> |
||||
#include <cassert> |
||||
#include <system_error> |
||||
|
||||
#include "riot/mutex.hpp" |
||||
#include "riot/chrono.hpp" |
||||
#include "riot/thread.hpp" |
||||
#include "riot/condition_variable.hpp" |
||||
|
||||
using namespace std; |
||||
using namespace riot; |
||||
|
||||
/* http://en.cppreference.com/w/cpp/thread/condition_variable */ |
||||
int main() { |
||||
puts("\n************ C++ condition_variable test ***********"); |
||||
|
||||
puts("Wait with predicate and notify one ... "); |
||||
{ |
||||
mutex m; |
||||
condition_variable cv; |
||||
string data; |
||||
bool ready = false; |
||||
bool processed = false; |
||||
thread worker([&] { |
||||
unique_lock<mutex> lk(m); |
||||
cv.wait(lk, [&ready] { return ready; }); |
||||
data += " after processing"; |
||||
processed = true; |
||||
cv.notify_one(); |
||||
}); |
||||
data = "Example data"; |
||||
{ |
||||
lock_guard<mutex> lk(m); |
||||
// reason: variable is read in the thread created above
|
||||
/* cppcheck-suppress unreadVariable */ |
||||
ready = true; |
||||
cv.notify_one(); |
||||
} |
||||
{ |
||||
unique_lock<mutex> lk(m); |
||||
cv.wait(lk, [&processed] { return processed; }); |
||||
} |
||||
string expected = "Example data after processing"; |
||||
assert(data == expected); |
||||
worker.join(); |
||||
} |
||||
puts("Done\n"); |
||||
|
||||
puts("Wait and notify all ..."); |
||||
{ |
||||
mutex m; |
||||
condition_variable cv; |
||||
auto waits = [&m, &cv] { |
||||
unique_lock<mutex> lk(m); |
||||
cv.wait(lk); |
||||
}; |
||||
thread t1(waits); |
||||
thread t2(waits); |
||||
thread t3(waits); |
||||
thread t4(waits); |
||||
thread([&m, &cv] { |
||||
unique_lock<mutex> lk(m); |
||||
cv.notify_all(); |
||||
}).detach(); |
||||
t1.join(); |
||||
t2.join(); |
||||
t3.join(); |
||||