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)))
|
ifneq (,$(filter log_%,$(USEMODULE)))
|
||||||
USEMODULE += log
|
USEMODULE += log
|
||||||
endif
|
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 "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);
|
|||||||
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)))
|
|||||||
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)))
|
|||||||
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
|
|||||||
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})))
|
||||||
|
|
||||||
|
|||||||
@ -78,6 +78,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
|
||||||
|
|||||||
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…
x
Reference in New Issue
Block a user