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:
commit
369e7bbf43
@ -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
|
||||
|
57
core/include/native_sched.h
Normal file
57
core/include/native_sched.h
Normal 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
|
@ -85,6 +85,7 @@
|
||||
#include "tcb.h"
|
||||
#include "attributes.h"
|
||||
#include "kernel_types.h"
|
||||
#include "native_sched.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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})))
|
||||
|
||||
|
@ -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
|
||||
|
4
sys/cpp11-compat/Makefile
Normal file
4
sys/cpp11-compat/Makefile
Normal file
@ -0,0 +1,4 @@
|
||||
# This module requires cpp 11
|
||||
CXXEXFLAGS += -std=c++11
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
120
sys/cpp11-compat/condition_variable.cpp
Normal file
120
sys/cpp11-compat/condition_variable.cpp
Normal 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
13
sys/cpp11-compat/doc.txt
Normal 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
|
||||
*/
|
139
sys/cpp11-compat/include/riot/chrono.hpp
Normal file
139
sys/cpp11-compat/include/riot/chrono.hpp
Normal 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
|
139
sys/cpp11-compat/include/riot/condition_variable.hpp
Normal file
139
sys/cpp11-compat/include/riot/condition_variable.hpp
Normal 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
|
90
sys/cpp11-compat/include/riot/detail/thread_util.hpp
Normal file
90
sys/cpp11-compat/include/riot/detail/thread_util.hpp
Normal 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
|
213
sys/cpp11-compat/include/riot/mutex.hpp
Normal file
213
sys/cpp11-compat/include/riot/mutex.hpp
Normal 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
|
256
sys/cpp11-compat/include/riot/thread.hpp
Normal file
256
sys/cpp11-compat/include/riot/thread.hpp
Normal 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
|
35
sys/cpp11-compat/mutex.cpp
Normal file
35
sys/cpp11-compat/mutex.cpp
Normal 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
|
98
sys/cpp11-compat/thread.cpp
Normal file
98
sys/cpp11-compat/thread.cpp
Normal 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
|
28
tests/cpp11_condition_variable/Makefile
Normal file
28
tests/cpp11_condition_variable/Makefile
Normal 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
|
130
tests/cpp11_condition_variable/main.cpp
Normal file
130
tests/cpp11_condition_variable/main.cpp
Normal 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;
|
||||
}
|
28
tests/cpp11_mutex/Makefile
Normal file
28
tests/cpp11_mutex/Makefile
Normal 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
|
87
tests/cpp11_mutex/main.cpp
Normal file
87
tests/cpp11_mutex/main.cpp
Normal 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;
|
||||
}
|
28
tests/cpp11_thread/Makefile
Normal file
28
tests/cpp11_thread/Makefile
Normal 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
186
tests/cpp11_thread/main.cpp
Normal 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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user