Skip to content
Snippets Groups Projects
Commit bddaee75 authored by Joseph Noir's avatar Joseph Noir
Browse files

Add replacement headers for thread, mutex and cond

These headers do not provide full stl functionality,
but a small subset:
* thread and this_thread
* condition_variable (some timed functions are missing)
* mutex, lock_guard and unique_lock
parent e53cdbb1
No related branches found
No related tags found
No related merge requests found
Showing
with 1459 additions and 3 deletions
...@@ -254,3 +254,9 @@ endif ...@@ -254,3 +254,9 @@ endif
ifneq (,$(filter log_%,$(USEMODULE))) ifneq (,$(filter log_%,$(USEMODULE)))
USEMODULE += log USEMODULE += log
endif endif
ifneq (,$(filter cpp11-compat,$(USEMODULE)))
USEMODULE += vtimer
USEMODULE += timex
FEATURES_REQUIRED += cpp
endif
/*
* 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 @@ ...@@ -85,6 +85,7 @@
#include "tcb.h" #include "tcb.h"
#include "attributes.h" #include "attributes.h"
#include "kernel_types.h" #include "kernel_types.h"
#include "native_sched.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
......
...@@ -40,6 +40,11 @@ extern void board_init(void); ...@@ -40,6 +40,11 @@ extern void board_init(void);
extern void kernel_init(void); extern void kernel_init(void);
extern void __libc_init_array(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 * @brief This function is the entry point after a system reset
* *
......
...@@ -17,8 +17,8 @@ ifneq (,$(filter nomac,$(USEMODULE))) ...@@ -17,8 +17,8 @@ ifneq (,$(filter nomac,$(USEMODULE)))
DIRS += net/link_layer/nomac DIRS += net/link_layer/nomac
endif endif
ifneq (,$(filter transport_layer,$(USEMODULE))) ifneq (,$(filter transport_layer,$(USEMODULE)))
USEMODULE += udp USEMODULE += udp
USEMODULE += tcp USEMODULE += tcp
endif endif
ifneq (,$(filter socket_base,$(USEMODULE))) ifneq (,$(filter socket_base,$(USEMODULE)))
DIRS += net/transport_layer/socket_base DIRS += net/transport_layer/socket_base
...@@ -45,7 +45,7 @@ ifneq (,$(filter rpl,$(USEMODULE))) ...@@ -45,7 +45,7 @@ ifneq (,$(filter rpl,$(USEMODULE)))
DIRS += net/routing/rpl DIRS += net/routing/rpl
endif endif
ifneq (,$(filter routing,$(USEMODULE))) ifneq (,$(filter routing,$(USEMODULE)))
DIRS += net/routing DIRS += net/routing
endif endif
ifneq (,$(filter aodvv2,$(USEMODULE))) ifneq (,$(filter aodvv2,$(USEMODULE)))
DIRS += net/routing/aodvv2 DIRS += net/routing/aodvv2
...@@ -146,6 +146,9 @@ endif ...@@ -146,6 +146,9 @@ endif
ifneq (,$(filter log_%,$(USEMODULE))) ifneq (,$(filter log_%,$(USEMODULE)))
DIRS += log DIRS += log
endif endif
ifneq (,$(filter cpp11-compat,$(USEMODULE)))
DIRS += cpp11-compat
endif
DIRS += $(dir $(wildcard $(addsuffix /Makefile, ${USEMODULE}))) DIRS += $(dir $(wildcard $(addsuffix /Makefile, ${USEMODULE})))
......
...@@ -81,6 +81,10 @@ ifneq (,$(filter fib,$(USEMODULE))) ...@@ -81,6 +81,10 @@ ifneq (,$(filter fib,$(USEMODULE)))
USEMODULE_INCLUDES += $(RIOTBASE)/sys/include/net USEMODULE_INCLUDES += $(RIOTBASE)/sys/include/net
endif endif
ifneq (,$(filter cpp11-compat,$(USEMODULE)))
USEMODULE_INCLUDES += $(RIOTBASE)/sys/cpp11-compat/include
endif
ifneq (,$(filter embunit,$(USEMODULE))) ifneq (,$(filter embunit,$(USEMODULE)))
ifeq ($(OUTPUT),XML) ifeq ($(OUTPUT),XML)
CFLAGS += -DOUTPUT=OUTPUT_XML CFLAGS += -DOUTPUT=OUTPUT_XML
......
# This module requires cpp 11
CXXEXFLAGS += -std=c++11
include $(RIOTBASE)/Makefile.base
/*
* 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
/*
* 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
*/
/*
* 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
/*
* 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
/*
* 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
/*
* 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
/*
* 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
/*
* 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
/*
* 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
# 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
/*
* 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;
}
# 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
/*
* 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;
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment