1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

Merge pull request #2540 from josephnoir/topic/cpp-stl-compatibility

Add replacement headers for std thread, mutex and condition_variable
This commit is contained in:
BytesGalore 2015-05-19 07:56:54 +02:00
commit 369e7bbf43
22 changed files with 1673 additions and 3 deletions

View File

@ -254,3 +254,9 @@ endif
ifneq (,$(filter log_%,$(USEMODULE)))
USEMODULE += log
endif
ifneq (,$(filter cpp11-compat,$(USEMODULE)))
USEMODULE += vtimer
USEMODULE += timex
FEATURES_REQUIRED += cpp
endif

View File

@ -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

View File

@ -85,6 +85,7 @@
#include "tcb.h"
#include "attributes.h"
#include "kernel_types.h"
#include "native_sched.h"
#ifdef __cplusplus
extern "C" {

View File

@ -40,6 +40,11 @@ extern void board_init(void);
extern void kernel_init(void);
extern void __libc_init_array(void);
/**
* Required by g++ cross compiler
*/
void *__dso_handle;
/**
* @brief This function is the entry point after a system reset
*

View File

@ -17,8 +17,8 @@ ifneq (,$(filter nomac,$(USEMODULE)))
DIRS += net/link_layer/nomac
endif
ifneq (,$(filter transport_layer,$(USEMODULE)))
USEMODULE += udp
USEMODULE += tcp
USEMODULE += udp
USEMODULE += tcp
endif
ifneq (,$(filter socket_base,$(USEMODULE)))
DIRS += net/transport_layer/socket_base
@ -45,7 +45,7 @@ ifneq (,$(filter rpl,$(USEMODULE)))
DIRS += net/routing/rpl
endif
ifneq (,$(filter routing,$(USEMODULE)))
DIRS += net/routing
DIRS += net/routing
endif
ifneq (,$(filter aodvv2,$(USEMODULE)))
DIRS += net/routing/aodvv2
@ -146,6 +146,9 @@ endif
ifneq (,$(filter log_%,$(USEMODULE)))
DIRS += log
endif
ifneq (,$(filter cpp11-compat,$(USEMODULE)))
DIRS += cpp11-compat
endif
DIRS += $(dir $(wildcard $(addsuffix /Makefile, ${USEMODULE})))

View File

@ -78,6 +78,10 @@ ifneq (,$(filter fib,$(USEMODULE)))
USEMODULE_INCLUDES += $(RIOTBASE)/sys/include/net
endif
ifneq (,$(filter cpp11-compat,$(USEMODULE)))
USEMODULE_INCLUDES += $(RIOTBASE)/sys/cpp11-compat/include
endif
ifneq (,$(filter embunit,$(USEMODULE)))
ifeq ($(OUTPUT),XML)
CFLAGS += -DOUTPUT=OUTPUT_XML

View File

@ -0,0 +1,4 @@
# This module requires cpp 11
CXXEXFLAGS += -std=c++11
include $(RIOTBASE)/Makefile.base

View File

@ -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

13
sys/cpp11-compat/doc.txt Normal file
View File

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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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();
t4.join();
}
puts("Done\n");
puts("Wait for ...");
{
using chrono::system_clock;
constexpr unsigned timeout = 1;
mutex m;
condition_variable cv;
timex_t before, after;
unique_lock<mutex> lk(m);
vtimer_now(&before);
cv.wait_for(lk, chrono::seconds(timeout));
vtimer_now(&after);
auto diff = timex_sub(after, before);
assert(diff.seconds >= timeout);
}
puts("Done\n");
puts("Wait until ...");
{
using chrono::system_clock;
constexpr unsigned timeout = 1;
mutex m;
condition_variable cv;
timex_t before, after;
unique_lock<mutex> lk(m);
vtimer_now(&before);
auto time = riot::now() += chrono::seconds(timeout);
cv.wait_until(lk, time);
vtimer_now(&after);
auto diff = timex_sub(after, before);
assert(diff.seconds >= timeout);
}
puts("Done\n");
puts("Bye, bye. ");
puts("******************************************************\n");
return 0;
}

View File

@ -0,0 +1,28 @@
# name of your application
APPLICATION = cpp11_mutex
# 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

View File

@ -0,0 +1,87 @@
/*
* 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 mutex 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/mutex */
int main() {
puts("\n************ C++ mutex test ***********");
puts("Lock and unlock ... ");
{
mutex m;
int resource = 0;
auto f = [&m, &resource] {
for (int i = 0; i < 3; ++i) {
m.lock();
++resource;
this_thread::sleep_for(chrono::milliseconds(100));
m.unlock();
}
};
assert(resource == 0);
auto start = std::chrono::system_clock::now();
thread t1(f);
thread t2(f);
t1.join();
t2.join();
assert(resource == 6);
auto duration = std::chrono::duration_cast
<chrono::milliseconds>(std::chrono::system_clock::now() - start);
assert(duration.count() >= 600);
}
puts("Done\n");
puts("Try_lock ...");
{
mutex m;
m.lock();
thread([&m] {
auto res = m.try_lock();
assert(res == false);
}).detach();
m.unlock();
}
{
mutex m;
thread([&m] {
auto res = m.try_lock();
assert(res == true);
m.unlock();
}).detach();
}
puts("Done\n");
puts("Bye, bye.");
puts("*****************************************\n");
return 0;
}

View File

@ -0,0 +1,28 @@
# name of your application
APPLICATION = cpp11_thread
# 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

186
tests/cpp11_thread/main.cpp Normal file
View File

@ -0,0 +1,186 @@
/*
* 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 thread 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/thread */
int main() {
puts("\n************ C++ thread test ***********");
assert(sched_num_threads == 2); // main + idle
puts("Creating one thread and passing an argument ...");
{
constexpr int i = 3;
thread t([=](const int j) { assert(j == i); }, i);
try {
t.join();
}
catch (const std::system_error& e) {
assert(false);
}
}
puts("Done\n");
assert(sched_num_threads == 2);
puts("Creating detached thread ...");
{
thread t([] {
// nop
});
assert(t.joinable() == 1);
t.detach();
assert(t.joinable() == 0);
}
puts("Done\n");
assert(sched_num_threads == 2);
puts("Join on 'finished' thread ...");
{
thread t;
assert(t.joinable() == 0);
t = thread([] {
// nop
});
assert(t.joinable() == 1);
try {
t.join();
}
catch (const std::system_error& e) {
assert(false);
}
}
puts("Done\n");
assert(sched_num_threads == 2);
puts("Join on 'running' thread ...");
{
mutex m;
thread t1, t2;
condition_variable cv;
assert(t1.joinable() == 0);
assert(t2.joinable() == 0);
t1 = thread([&m, &cv] {
unique_lock<mutex> lk(m);
cv.wait(lk);
});
assert(t1.joinable() == 1);
t2 = thread([&t1] {
try {
t1.join();
}
catch (const std::system_error& e) {
assert(false);
}
});
assert(t2.joinable() == 1);
cv.notify_one();
try {
t2.join();
}
catch (const std::system_error& e) {
assert(false);
}
assert(t1.joinable() == 0);
assert(t2.joinable() == 0);
}
puts("Done\n");
assert(sched_num_threads == 2);
puts("Testing sleep_for ...");
{
timex_t before, after;
vtimer_now(&before);
this_thread::sleep_for(chrono::seconds(1));
vtimer_now(&after);
auto diff = timex_sub(after, before);
assert(diff.seconds >= 1);
}
puts("Done\n");
assert(sched_num_threads == 2);
puts("Testing sleep_until ...");
{
timex_t before, after;
vtimer_now(&before);
this_thread::sleep_until(riot::now() += chrono::seconds(1));
vtimer_now(&after);
auto diff = timex_sub(after, before);
assert(diff.seconds >= 1);
}
puts("Done\n");
assert(sched_num_threads == 2);
puts("Swapping two threads ...");
{
thread t1([] {
// nop
});
thread t2([] {
// nop
});
auto t1_old = t1.get_id();
auto t2_old = t2.get_id();
t1.swap(t2);
assert(t1_old == t2.get_id());
assert(t2_old == t1.get_id());
t1.join();
t2.join();
}
puts("Done\n");
assert(sched_num_threads == 2);
puts("Move constructor ...");
{
thread t1([] {
// nop
});
thread t2(move(t1));
assert(t1.joinable() == 0);
assert(t2.joinable() == 1);
t2.join();
}
puts("Done\n");
assert(sched_num_threads == 2);
puts("Bye, bye.");
puts("******************************************");
return 0;
}