[PATCH v2] cpukit/librcxx: Add a C++ thread interface with attributes
chrisj at rtems.org
chrisj at rtems.org
Thu Dec 12 21:33:12 UTC 2019
From: Chris Johns <chrisj at rtems.org>
---
cpukit/include/rtems/c++/error | 65 +++
cpukit/include/rtems/c++/thread | 469 ++++++++++++++++++
cpukit/librtemscxx/error.cc | 76 +++
cpukit/librtemscxx/thread.cc | 414 ++++++++++++++++
.../cpukit/RTEMS-BUILD-CPUKIT-GRPTOP.yml | 1 +
.../cpukit/RTEMS-BUILD-CPUKIT-LIBRTEMSCXX.yml | 27 +
.../libtests/RTEMS-BUILD-TEST-LIB-GRP.yml | 1 +
.../libtests/RTEMS-BUILD-TEST-LIB-RCXX01.yml | 27 +
testsuites/libtests/rcxx01/init.c | 70 +++
testsuites/libtests/rcxx01/thread.cc | 90 ++++
10 files changed, 1240 insertions(+)
create mode 100644 cpukit/include/rtems/c++/error
create mode 100644 cpukit/include/rtems/c++/thread
create mode 100644 cpukit/librtemscxx/error.cc
create mode 100644 cpukit/librtemscxx/thread.cc
create mode 100644 spec/build/cpukit/RTEMS-BUILD-CPUKIT-LIBRTEMSCXX.yml
create mode 100644 spec/build/testsuites/libtests/RTEMS-BUILD-TEST-LIB-RCXX01.yml
create mode 100644 testsuites/libtests/rcxx01/init.c
create mode 100644 testsuites/libtests/rcxx01/thread.cc
diff --git a/cpukit/include/rtems/c++/error b/cpukit/include/rtems/c++/error
new file mode 100644
index 0000000000..a001d172da
--- /dev/null
+++ b/cpukit/include/rtems/c++/error
@@ -0,0 +1,65 @@
+/* -*- C++ -*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 2019 Chris Johns <chrisj at rtems.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * @file
+ *
+ * @ingroup RTEMSC++
+ *
+ * RTEMS Error exception.
+ */
+
+#if !defined(RTEMS_CXX_ERROR)
+#define RTEMS_CXX_ERROR
+
+#include <stdexcept>
+#include <string>
+
+#include <rtems.h>
+
+namespace rtems
+{
+ class runtime_error :
+ public std::runtime_error
+ {
+ const rtems_status_code sc;
+ public:
+ runtime_error (const rtems_status_code sc);
+ runtime_error (const rtems_status_code sc, const std::string& what);
+ runtime_error (const rtems_status_code sc, const char* what);
+ ~runtime_error ();
+ };
+
+ /**
+ * Throw a rtems::runtime_error exception if the RTEMS status code is
+ * not RTEMS_SUCCESSFUL.
+ */
+ void runtime_error_check (const rtems_status_code sc);
+ void runtime_error_check (const rtems_status_code sc, const std::string& what);
+ void runtime_error_check (const rtems_status_code sc, const char* what);
+};
+
+#endif
diff --git a/cpukit/include/rtems/c++/thread b/cpukit/include/rtems/c++/thread
new file mode 100644
index 0000000000..1d283a756f
--- /dev/null
+++ b/cpukit/include/rtems/c++/thread
@@ -0,0 +1,469 @@
+/* -*- C++ -*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 2019 Chris Johns <chrisj at rtems.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * @file
+ *
+ * @ingroup RTEMSC++
+ *
+ * The C++ standard thread support with thread attribute control.
+ *
+ * The code requires the `-std=c++17` option to access `std::invoke()`.
+ */
+
+#if !defined(RTEMS_CXX_THREAD)
+#define RTEMS_CXX_THREAD
+
+#include <functional>
+#include <iostream>
+#include <string>
+#include <thread>
+#include <utility>
+
+namespace rtems
+{
+ namespace thread
+ {
+ /**
+ * @brief Manage the attributes of a thread.
+ */
+ class attributes
+ {
+ public:
+ /**
+ * The schedular modes.
+ */
+ enum sched_mode {
+ sched_inherit, /**< Inhert the scheduler */
+ sched_explicit /**< Explicitly set the scheduler */
+ };
+
+ /**
+ * The schedular policies.
+ */
+ enum sched_policy {
+ sched_other, /**< Other scheduler policy */
+ sched_fifo, /**< FIFO scheduler policy */
+ sched_roundrobin, /**< Roundrobin scheduler policy */
+ sched_sporadic /**< Sporadic scheduler policy */
+ };
+
+ /**
+ * Construct a thread attributes object with the current settings of the
+ * executing thread. The stack size is set to the configured minimum
+ * stack size.
+ */
+ attributes ();
+
+ /*
+ * Copy construct the thread attributes.
+ *
+ * @param attr The attributes to copy.
+ */
+ attributes (const attributes& attr);
+
+ /**
+ * Set the name of the thread. The thread is a classic API thread and
+ * the name is only 4 characters.
+ *
+ * @param name The name as a string.
+ */
+ void set_name (const std::string& name);
+
+ /**
+ * Set the name of the thread. The thread is a classic API thread and
+ * the name is only 4 characters.
+ *
+ * @param name The name as a string.
+ */
+ void set_name (const char* name);
+
+ /**
+ * Get the name of the thread.
+ *
+ * @retval const std::string& The name of the thread.
+ */
+ const std::string& get_name () const;
+
+ /**
+ * Set the priority of the thread.
+ *
+ * @param priority The POSIX API priority of the thread.
+ */
+ void set_priority (int priority);
+
+ /**
+ * Get the POSIX API priority of the thread.
+ *
+ * @retval int The POSIX API thread priority.
+ */
+ int get_priority () const;
+
+ /**
+ * Set the stack size. If the size is less than the configured minimum
+ * the minimum value is used.
+ *
+ * @param size The stack size in bytes.
+ */
+ void set_stack_size (size_t size);
+
+ /**
+ * Get the stack size.
+ *
+ * @retval size_t The stack size in bytes.
+ */
+ size_t get_stack_size () const;
+
+ /**
+ * Set the scheduler name. If not set no scheduler is set.
+ *
+ * @parrm scheduler The name of the scheduler.
+ */
+ void set_scheduler (const std::string& scheduler);
+
+ /**
+ * Set the scheduler name. If not set no scheduler is set.
+ */
+ void set_scheduler (const char* scheduler);
+
+ /**
+ * Get scheduler name.
+ */
+ const std::string& get_scheduler ();
+
+ /**
+ * Get the attrobutes' scheduler mode for the thread.
+ *
+ * @return sched_mode The attributes; scheduler mode
+ */
+ sched_mode get_scheduler_mode () const;
+
+ /**
+ * Set the scheduler policy for the thread. This call sets the
+ * schedular mode to @ref sched_explicit.
+ *
+ * @param policy The scheduler policy.
+ */
+ void set_scheduler_policy (sched_policy policyr);
+
+ /**
+ * Get the scheduler policy for the thread.
+ */
+ sched_policy get_scheduler_policy () const;
+
+ /**
+ * Commit any changes to the executing thread. @note only the priority
+ * and modes of a thread can be changed. The name and stack size are
+ * ignored.
+ */
+ void commit ();
+
+ /**
+ * Update the attribute values from the executing thread. The attributes
+ * are updated from the current thread when constructed and the values
+ * returned are those held since then. If another thread changes the
+ * attributes of the current thread those changes will not be seen until
+ * this method is called. Except for the name and stack size any local
+ * changes made will lost then the update call is made.
+ */
+ void update ();
+
+ /**
+ * Copy operator.
+ */
+ attributes& operator= (const attributes& attr);
+
+ /**
+ * The comparision operator does not check the name or stack size
+ * of a thread.
+ */
+ bool operator== (const attributes& attr) const;
+
+ private:
+ std::string name; /**< Name of the thread */
+ int priority; /**< POSIX API priority */
+ size_t stack_size; /**< Stack size in bytes */
+ std::string scheduler; /**< Name of the scheduler */
+ sched_mode mode; /**< Scheduler's mode */
+ sched_policy policy; /**< Scheduler's policy */
+ /* affinity, cpu set size is? */
+ };
+
+ /**
+ * @brief Create a thread with thread attributes.
+ *
+ * Create a thread optionally with thread attributes. The usage of this
+ * class follows the C++ standard for std::thread. The standard support
+ * for creating a thread does not let you control the attributes of a
+ * thread and control is important in embedded reall-time
+ * applications. This class lets you control a thread attributes and use
+ * the extensive an excellent thread support the C++ standard provides.
+ *
+ * There is no indication attribute support for threads will be added to
+ * the C++ standard and what it will look like. The support provided here
+ * is designed to make as little impact on a code base as possible. While
+ * attributes supportted are specific to RTEMS they are common to all
+ * embedded operating systems.
+ *
+ * The support provided here is specific to GCC due to the use of some
+ * non-standard interfaces to get the indices of the template argument
+ * pack in new thread's context. A standards only solution would be
+ * preferred.
+ */
+ class thread
+ {
+ friend void* thread_generic_entry (void* arg);
+
+ /**
+ * Base state class to interface to derived template of the thread
+ * state from the generic entry point for the thread.
+ */
+ struct state_base
+ {
+ virtual ~state_base ();
+ virtual const attributes get_attributes () = 0;
+ virtual void run () = 0;
+ };
+
+ /**
+ * The state is passed to the new thread context as a unique
+ * pointer. This handles the hand over and clean up.
+ */
+ using state_ptr = std::unique_ptr<state_base>;
+
+ /**
+ * Template check to see if the first argument of a thread is a set of
+ * attributes.
+ */
+ template <typename A, class DecayA = typename std::decay<A>::type>
+ using enable_if_attributes = typename std::enable_if
+ <std::is_same<DecayA, attributes>::value>::type;
+
+ public:
+
+ /**
+ * We need our own id type so the thread class can access the pthread
+ * handle to initialise it.
+ */
+ class id {
+ public:
+ id () noexcept : id_ (0) { }
+ explicit id (pthread_t id_) : id_ (id_) { }
+ private:
+ pthread_t id_;
+
+ friend class thread;
+ friend bool operator== (thread::id l, thread::id r) noexcept;
+
+ template<class CharT, class Traits>
+ friend std::basic_ostream<CharT, Traits>&
+ operator<< (std::basic_ostream<CharT, Traits>& out, thread::id id_);
+ };
+
+ /**
+ * The default thread constructions.
+ */
+ thread () noexcept = default;
+
+ /**
+ * The std::thread equivilant constructor. The attributes will be the
+ * same as the executing thread with a default thread name and the
+ * configured minimum stack size.
+ */
+ template<typename F, typename... Args>
+ explicit thread (F&& func, Args&&... args);
+
+ /**
+ * Create a thread with the provided attributes. The entry point and
+ * optional arguments are the same as std::thread.
+ */
+ template <typename A, typename F, typename ...Args,
+ class = enable_if_attributes<A>>
+ explicit thread (A&& attr, F&& func, Args&&... args);
+
+ /**
+ * Move the thread id to this instance.
+ */
+ thread& operator= (thread&& thread_);
+
+ void swap(thread& thread_) noexcept;
+
+ bool joinable() const noexcept;
+
+ /*
+ * Constrain use. These are not available.
+ */
+ thread (thread&) = delete;
+ thread (const thread&) = delete;
+ thread (const thread&&) = delete;
+ thread& operator= (const thread&) = delete;
+
+ std::thread::id get_id() const noexcept;
+
+ private:
+
+ id id_;
+
+ /**
+ * Invoke the thread's entry point with the parameter pack in the new
+ * thread's context. This object holds the parameters copied onto the
+ * new thread's stack making them available to entry point routine.
+ */
+ template<typename Parms>
+ struct invoker {
+ Parms p;
+
+ template<size_t Index>
+ static std::__tuple_element_t<Index, Parms>&& declval ();
+
+ template<size_t... Ind>
+ auto invoke (std::_Index_tuple<Ind...>)
+ noexcept (noexcept (std::invoke (declval<Ind>()...)))
+ -> decltype (std::invoke (declval<Ind> ()...)) {
+ return std::invoke (std::get<Ind> (std::move (p))...);
+ }
+
+ using indices =
+ typename std::_Build_index_tuple<std::tuple_size<Parms>::value>::__type;
+
+ void run () {
+ invoke (indices ());
+ }
+ };
+
+ /**
+ * The state holds the invoker with the parameters. The generic entry
+ * point calls the virtual methods to get the attributes and to run the
+ * new thread in the new thread's context..
+ */
+ template<typename Invoker>
+ struct state : state_base {
+ const attributes attr;
+ Invoker i;
+
+ state (const attributes& attr, Invoker&& i)
+ : attr (attr),
+ i (std::forward<Invoker> (i)) {
+ }
+
+ const attributes get_attributes () override {
+ return attr;
+ }
+
+ void run () override {
+ i.run ();
+ }
+ };
+
+ /**
+ * Make the state. This dynamic memory is managed by the unique pointer
+ * and is passed to the generic thread entry point.
+ */
+ template<typename Invoker>
+ static state_ptr
+ make_state (const attributes& attr, Invoker&& i) {
+ using state_impl = state<Invoker>;
+ return state_ptr{ new state_impl (attr, std::forward<Invoker> (i)) };
+ }
+
+ /**
+ * Decay the parameters so they can be correctly packed into the
+ * parameter tuple.
+ */
+ template<typename... T>
+ using decayed_tuple = std::tuple<typename std::decay<T>::type...>;
+
+ /**
+ * Make the invoker with the parameters.
+ */
+ template<typename F, typename... Args>
+ static invoker<decayed_tuple<F, Args...>>
+ make_invoker (F&& func, Args&&... args)
+ {
+ return {
+ decayed_tuple<F, Args...> {
+ std::forward<F> (func), std::forward<Args> (args)...
+ }
+ };
+ }
+
+ /**
+ * Create and start the thread.
+ */
+ void start_thread (state_ptr s);
+ };
+
+ template <class T>
+ inline typename std::decay<T>::type
+ decay_copy (T&& t) {
+ return std::forward<T> (t);
+ }
+
+ template<typename F, typename... Args>
+ thread::thread (F&& func, Args&&... args)
+ : id_ (0) {
+ attributes attr;
+ start_thread (
+ make_state (attr,
+ make_invoker (decay_copy (std::forward<F> (func)),
+ decay_copy (std::forward<Args> (args))...))
+ );
+ }
+
+ template<typename A, typename F, typename... Args,
+ class = thread::enable_if_attributes<A>>
+ thread::thread (A&& attr, F&& func, Args&&... args)
+ : id_ (0) {
+ start_thread (
+ make_state (attr,
+ make_invoker (decay_copy (std::forward<F> (func)),
+ decay_copy (std::forward<Args> (args))...))
+ );
+ }
+
+ inline std::thread::id thread::get_id() const noexcept {
+ return std::thread::id (id_.id_);
+ }
+
+ inline bool
+ operator== (thread::id l, thread::id r) noexcept {
+ return l.id_ == r.id_;
+ }
+
+ inline bool
+ operator!= (thread::id l, thread::id r) noexcept {
+ return !(l == r);
+ }
+
+ template<class C, class T>
+ inline std::basic_ostream<C, T>&
+ operator<< (std::basic_ostream<C, T>& out, thread::id id_) {
+ return out << std::thread::id (id_.id_);
+ }
+ };
+};
+
+#endif
diff --git a/cpukit/librtemscxx/error.cc b/cpukit/librtemscxx/error.cc
new file mode 100644
index 0000000000..5e6d090962
--- /dev/null
+++ b/cpukit/librtemscxx/error.cc
@@ -0,0 +1,76 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 2019 Chris Johns <chrisj at rtems.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <rtems/c++/error>
+
+namespace rtems
+{
+ runtime_error::runtime_error (const rtems_status_code sc)
+ : std::runtime_error (::rtems_status_text (sc)),
+ sc (sc)
+ {
+ }
+
+ runtime_error::runtime_error (const rtems_status_code sc,
+ const std::string& what)
+ : std::runtime_error (what + ": " + ::rtems_status_text (sc)),
+ sc (sc)
+ {
+ }
+
+ runtime_error::runtime_error (const rtems_status_code sc,
+ const char* what)
+ : std::runtime_error (std::string (what) + ": " + ::rtems_status_text (sc)),
+ sc (sc)
+ {
+ }
+
+ runtime_error::~runtime_error()
+ {
+ }
+
+ void
+ runtime_error_check (const rtems_status_code sc)
+ {
+ if (sc != RTEMS_SUCCESSFUL)
+ throw runtime_error (sc);
+ }
+
+ void
+ runtime_error_check (const rtems_status_code sc, const std::string& what)
+ {
+ if (sc != RTEMS_SUCCESSFUL)
+ throw runtime_error (sc, what);
+ }
+
+ void
+ runtime_error_check (const rtems_status_code sc, const char* what)
+ {
+ if (sc != RTEMS_SUCCESSFUL)
+ throw runtime_error (sc, what);
+ }
+};
diff --git a/cpukit/librtemscxx/thread.cc b/cpukit/librtemscxx/thread.cc
new file mode 100644
index 0000000000..5710973027
--- /dev/null
+++ b/cpukit/librtemscxx/thread.cc
@@ -0,0 +1,414 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 2019 Chris Johns <chrisj at rtems.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _GNU_SOURCE
+
+#include <system_error>
+
+#include <rtems/c++/error>
+#include <rtems/c++/thread>
+
+#include <pthread.h>
+
+#include <rtems.h>
+
+#if HAVE_GET_SCHEDULER_NAME
+extern "C" bool get_scheduler_name (rtems_id sid, char* name);
+#endif
+
+#if HAVE_GET_SCHEDULER_NAME
+bool get_scheduler_name (rtems_id sid, char* name)
+{
+ name[0] = 'N';
+ name[1] = 'O';
+ name[2] = 'P';
+ name[3] = '\0';
+ return true;
+}
+#endif
+
+namespace rtems
+{
+ namespace thread
+ {
+ void
+ system_error_check (int ec, const char* what)
+ {
+ if (ec != 0)
+ throw std::system_error (ec, std::system_category(), what);
+ }
+
+ attributes::attributes ()
+ : priority (-1),
+ stack_size (RTEMS_MINIMUM_STACK_SIZE),
+ mode (sched_inherit),
+ policy (sched_fifo)
+ {
+ update ();
+ }
+
+ attributes::attributes (const attributes& attr)
+ : name (attr.name),
+ priority (attr.priority),
+ stack_size (attr.stack_size),
+ scheduler (attr.scheduler),
+ mode (attr.mode),
+ policy (attr.policy)
+ {
+ }
+
+ void
+ attributes::set_name (const std::string& name_)
+ {
+ name = name_;
+ }
+
+ void
+ attributes::set_name (const char* name_)
+ {
+ name = name_;
+ }
+
+ const std::string&
+ attributes::get_name () const
+ {
+ return name;
+ }
+
+ void
+ attributes::set_priority (int priority_)
+ {
+ priority = priority_;
+ }
+
+ int
+ attributes::get_priority () const
+ {
+ return priority;
+ }
+
+ void
+ attributes::set_stack_size(size_t size)
+ {
+ stack_size = size;
+ }
+
+ size_t
+ attributes::get_stack_size () const
+ {
+ return stack_size;
+ }
+
+ void
+ attributes::set_scheduler (const std::string& scheduler_)
+ {
+ scheduler = scheduler_;
+ }
+
+ void
+ attributes::set_scheduler (const char* scheduler_)
+ {
+ scheduler = scheduler_;
+ }
+
+ const std::string&
+ attributes::get_scheduler ()
+ {
+ return scheduler;
+ }
+
+ attributes::sched_mode
+ attributes::get_scheduler_mode () const
+ {
+ return mode;
+ }
+
+ void
+ attributes::set_scheduler_policy (sched_policy policy_)
+ {
+ mode = sched_explicit;
+ policy = policy_;
+ }
+
+ attributes::sched_policy
+ attributes::get_scheduler_policy () const
+ {
+ return policy;
+ }
+
+ void
+ attributes::commit ()
+ {
+ pthread_t pid = ::pthread_self ();
+
+ system_error_check (::pthread_setname_np (pid, name.c_str ()),
+ "getting name");
+
+ int spolicy;
+ struct sched_param sched_param;
+
+ system_error_check (::pthread_getschedparam (::pthread_self (),
+ &spolicy,
+ &sched_param),
+ "getting scheduler parameters");
+
+ switch (policy) {
+ case sched_other:
+ spolicy = SCHED_OTHER;
+ break;
+ case sched_fifo:
+ spolicy = SCHED_FIFO;
+ break;
+ case sched_roundrobin:
+ spolicy = SCHED_RR;
+ break;
+ case sched_sporadic:
+ spolicy = SCHED_SPORADIC;
+ break;
+ default:
+ system_error_check (EINVAL, "get scheduler policy");
+ break;
+ }
+
+ sched_param.sched_priority = priority;
+
+ system_error_check (::pthread_setschedparam (::pthread_self (),
+ spolicy,
+ &sched_param),
+ "getting scheduler parameters");
+
+ if (!scheduler.empty ()) {
+ char sname[4] = { ' ', ' ', ' ', ' ' };
+ for (size_t c = 0; c < sizeof (sname); ++c) {
+ if (c >= scheduler.length ())
+ break;
+ sname[c] = scheduler[c];
+ }
+ rtems_name scheduler_name = rtems_build_name (sname[0],
+ sname[1],
+ sname[2],
+ sname[3]);
+ rtems_id scheduler_id;
+ runtime_error_check (::rtems_scheduler_ident (scheduler_name,
+ &scheduler_id),
+ "get scheduler id");
+ // runtime_error_check (::rtems_task_set_scheduler (RTEMS_SELF,
+ // scheduler_id,
+ // 1),
+ // "set scheduler id");
+ }
+ }
+
+ void
+ attributes::update ()
+ {
+ char buf[32];
+ system_error_check (::pthread_getname_np (::pthread_self (),
+ buf,
+ sizeof (buf)),
+ "getting name");
+ name = buf;
+
+ int spolicy;
+ struct sched_param sched_param;
+ system_error_check (::pthread_getschedparam (::pthread_self (),
+ &spolicy,
+ &sched_param),
+ "getting scheduler parameters");
+
+ switch (spolicy) {
+ case SCHED_OTHER:
+ policy = sched_other;
+ break;
+ case SCHED_FIFO:
+ policy = sched_fifo;
+ break;
+ case SCHED_RR:
+ policy = sched_roundrobin;
+ break;
+ case SCHED_SPORADIC:
+ policy = sched_sporadic;
+ break;
+ default:
+ system_error_check (EINVAL, "get scheduler policy");
+ break;
+ }
+ priority = sched_param.sched_priority;
+
+ pthread_attr_t attr;
+ system_error_check (::pthread_getattr_np (::pthread_self (), &attr),
+ "getting thread attributes");
+ system_error_check (::pthread_attr_getstacksize (&attr, &stack_size),
+ "getting stack size");
+ int inheritsched = 0;
+ system_error_check (::pthread_attr_getinheritsched (&attr, &inheritsched),
+ "getting inherited sheduler mode");
+ switch (inheritsched) {
+ case PTHREAD_INHERIT_SCHED:
+ mode = sched_inherit;
+ break;
+ case PTHREAD_EXPLICIT_SCHED:
+ mode = sched_explicit;
+ break;
+ default:
+ system_error_check (EINVAL, "get scheduler mode");
+ break;
+ }
+
+ rtems_id scheduler_id;
+ runtime_error_check (::rtems_task_get_scheduler (RTEMS_SELF, &scheduler_id));
+#if HAVE_GET_SCHEDULER_NAME
+ char name[5];
+ if (!get_scheduler_name (scheduler_id, &name[0]))
+ system_error_check (ENOENT, "get scheduler name");
+ scheduler = name;
+#endif
+ }
+
+ attributes&
+ attributes::operator= (const attributes& attr)
+ {
+ name = attr.name;
+ priority = attr.priority;
+ stack_size = attr.stack_size;
+ mode = attr.mode;
+ policy = attr.policy;
+ return *this;
+ }
+
+ bool
+ attributes::operator== (const attributes& attr) const
+ {
+ return
+ name == attr.name &&
+ priority == attr.priority &&
+ stack_size == attr.stack_size &&
+ mode == attr.mode &&
+ policy == attr.policy;
+ }
+
+ void*
+ thread_generic_entry (void* arg)
+ {
+ thread::state_ptr s{ static_cast<thread::state_base*> (arg) };
+ try {
+ s->run ();
+ } catch (...) {
+ std::terminate ();
+ }
+ return nullptr;
+ }
+
+ thread&
+ thread::operator= (thread&& thread_)
+ {
+ if (joinable ())
+ std::terminate ();
+ swap(thread_);
+ return *this;
+ }
+
+ void
+ thread::swap(thread& thread_) noexcept
+ {
+ std::swap(id_, thread_.id_);
+ }
+
+ bool
+ thread::joinable() const noexcept
+ {
+ return !(id_ == id());
+ }
+
+ thread::state_base::~state_base () = default;
+
+ void
+ thread::start_thread (thread::state_ptr s)
+ {
+ const attributes attr = s->get_attributes ();
+
+ pthread_attr_t pattr;
+
+ system_error_check (::pthread_attr_init (&pattr),
+ "attribute init");
+
+ system_error_check (::pthread_attr_setdetachstate (&pattr,
+ PTHREAD_CREATE_DETACHED),
+ "set detached state");
+
+ struct sched_param param;
+ param.sched_priority = attr.get_priority ();
+ system_error_check (::pthread_attr_setschedparam (&pattr, ¶m),
+ "set ");
+
+ int spolicy;
+ switch (attr.get_scheduler_policy ()) {
+ case attributes::sched_other:
+ spolicy = SCHED_OTHER;
+ break;
+ case attributes::sched_roundrobin:
+ spolicy = SCHED_RR;
+ break;
+ case attributes::sched_sporadic:
+ spolicy = SCHED_SPORADIC;
+ break;
+ default:
+ spolicy = SCHED_FIFO;
+ break;
+ }
+ system_error_check (::pthread_attr_setschedpolicy (&pattr, spolicy),
+ "set scheduler policy");
+
+ if (attr.get_scheduler_mode () == attributes::sched_inherit)
+ ::pthread_attr_setinheritsched (&pattr, PTHREAD_INHERIT_SCHED);
+ else
+ ::pthread_attr_setinheritsched (&pattr, PTHREAD_EXPLICIT_SCHED);
+
+ system_error_check (::pthread_attr_setstacksize(&pattr,
+ attr.get_stack_size ()),
+ "set stack size");
+
+ /*
+ * Hold the new thread in the state's run handler until the rest
+ * of the thread is set up after the create call.
+ */
+ system_error_check (::pthread_create (&id_.id_,
+ &pattr,
+ thread_generic_entry,
+ s.get ()),
+ "create thread");
+
+ system_error_check (::pthread_setname_np (id_.id_,
+ attr.get_name ().c_str ()),
+ "setting thread name");
+
+ ::pthread_attr_destroy (&pattr);
+
+ s.release ();
+ };
+ };
+};
diff --git a/spec/build/cpukit/RTEMS-BUILD-CPUKIT-GRPTOP.yml b/spec/build/cpukit/RTEMS-BUILD-CPUKIT-GRPTOP.yml
index c45e487ec2..4f42bf52bd 100644
--- a/spec/build/cpukit/RTEMS-BUILD-CPUKIT-GRPTOP.yml
+++ b/spec/build/cpukit/RTEMS-BUILD-CPUKIT-GRPTOP.yml
@@ -17,6 +17,7 @@ links:
- RTEMS-BUILD-CPUKIT-LIBMGHTTPD: H4IeA-ZHbblu3Fs1Q1bT6VBtvgphQiAAYM9EikwrNcA=
- RTEMS-BUILD-CPUKIT-LIBNFS: AQuzqktVYRNZ1y5F2X6VZjp5aB6azhEccWQGqsDzjBA=
- RTEMS-BUILD-CPUKIT-LIBPPPD: rTrsd1YhmaPjaVJ1cuk1X_JOYCoJq3bzoYpZpR_rLvs=
+- RTEMS-BUILD-CPUKIT-LIBRTEMSCXX: 6vNgwJFY3WUHtihZFPK4lJ_sb8vyoYWPuTaCJagy37E=
- RTEMS-BUILD-CPUKIT-LIBRTEMSCPU: KnDc2CfWdvqvXHIEEga_OF41wRS-bTUKFWKMsoQkj38=
- RTEMS-BUILD-CPUKIT-LIBRTEMSDFLTCFG: mMDacNoZThpYly7rJar0VekwIRIuIaKEFBHFpMP6m0s=
- RTEMS-BUILD-CPUKIT-LIBRTEMSTEST: IAMGUUUCwre_MQ5pQWb7aaS31jm42EjXWLpN7jLWfkw=
diff --git a/spec/build/cpukit/RTEMS-BUILD-CPUKIT-LIBRTEMSCXX.yml b/spec/build/cpukit/RTEMS-BUILD-CPUKIT-LIBRTEMSCXX.yml
new file mode 100644
index 0000000000..29c6f1f6b3
--- /dev/null
+++ b/spec/build/cpukit/RTEMS-BUILD-CPUKIT-LIBRTEMSCXX.yml
@@ -0,0 +1,27 @@
+active: true
+build-type: library
+cflags: []
+cppflags: []
+cxxflags: [-std=c++17]
+derived: false
+enabled-by: []
+header: ''
+includes: []
+install:
+- destination: ${BSP_INCLUDEDIR}
+ source:
+ - cpukit/include/rtems/c++/error
+ - cpukit/include/rtems/c++/thread
+install-path: ${BSP_LIBDIR}
+level: 1.111
+links: []
+normative: true
+order: 0
+ref: ''
+reviewed: 6vNgwJFY3WUHtihZFPK4lJ_sb8vyoYWPuTaCJagy37E=
+source:
+- cpukit/librtemscxx/error.cc
+- cpukit/librtemscxx/thread.cc
+target: rtemscxx
+text: ''
+type: build
diff --git a/spec/build/testsuites/libtests/RTEMS-BUILD-TEST-LIB-GRP.yml b/spec/build/testsuites/libtests/RTEMS-BUILD-TEST-LIB-GRP.yml
index bb967de64b..0cd4337bd8 100644
--- a/spec/build/testsuites/libtests/RTEMS-BUILD-TEST-LIB-GRP.yml
+++ b/spec/build/testsuites/libtests/RTEMS-BUILD-TEST-LIB-GRP.yml
@@ -117,6 +117,7 @@ links:
- RTEMS-BUILD-TEST-LIB-PWDGRP02: iJANATJr5dmPixALYSuf9lnt-pAODh_i1UKYMiMzEyU=
- RTEMS-BUILD-TEST-LIB-RBHEAP01: 8RbwWvEAHeHrzRu6wnkvzr5vBiK7BSQzX2vkthENNfE=
- RTEMS-BUILD-TEST-LIB-READ: e_Tb0VhPmbqFwMj9d5325h8DC0IpKgihLU0bcT0kUOA=
+- RTEMS-BUILD-TEST-LIB-RCXX01: null
- RTEMS-BUILD-TEST-LIB-READV: XE2dg5LvWtXlneKbH3Ogk_zPTIi035VtKw7Fw801lig=
- RTEMS-BUILD-TEST-LIB-REALLOC: qsc7l3XVbqi3VJODzCRXmfQZ7zyA_NlpjejQJfw8PX8=
- RTEMS-BUILD-TEST-LIB-RECORD01: Sbci1SZEyXjVnemxFwQbo03GoBBtUnt5IABA9Z9SoXg=
diff --git a/spec/build/testsuites/libtests/RTEMS-BUILD-TEST-LIB-RCXX01.yml b/spec/build/testsuites/libtests/RTEMS-BUILD-TEST-LIB-RCXX01.yml
new file mode 100644
index 0000000000..9e304ff631
--- /dev/null
+++ b/spec/build/testsuites/libtests/RTEMS-BUILD-TEST-LIB-RCXX01.yml
@@ -0,0 +1,27 @@
+active: true
+build-type: test-program
+cflags: []
+cppflags: []
+cxxflags: [-std=c++17]
+derived: false
+enabled-by: []
+features: c cxx cxxprogram
+header: ''
+includes: []
+ldflags: []
+level: 1.159
+links: []
+normative: true
+order: 0
+ref: ''
+reviewed: null
+source:
+- testsuites/libtests/rcxx01/init.c
+- testsuites/libtests/rcxx01/thread.cc
+stlib: []
+target: testsuites/libtests/rcxx01.exe
+text: ''
+type: build
+use-after:
+ - rtemscxx
+use-before: []
diff --git a/testsuites/libtests/rcxx01/init.c b/testsuites/libtests/rcxx01/init.c
new file mode 100644
index 0000000000..129f7ee4ba
--- /dev/null
+++ b/testsuites/libtests/rcxx01/init.c
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2019 Chris Johns <chrisj at rtems.org>
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <bsp.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include "tmacros.h"
+
+const char rtems_test_name[] = "RCXX 1";
+
+/* forward declarations to avoid warnings */
+rtems_task Init(rtems_task_argument argument);
+
+void rcxx_run_test(void);
+
+rtems_task Init(
+ rtems_task_argument ignored
+)
+{
+ TEST_BEGIN();
+
+ rcxx_run_test();
+
+ TEST_END();
+ rtems_test_exit( 0 );
+}
+
+/* configuration information */
+
+#include <rtems/serial_mouse.h>
+
+#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
+#define CONFIGURE_APPLICATION_NEEDS_SIMPLE_CONSOLE_DRIVER
+
+#define CONFIGURE_LIBIO_MAXIMUM_FILE_DESCRIPTORS 5
+
+#define CONFIGURE_MEMORY_OVERHEAD (2024)
+
+#define CONFIGURE_MAXIMUM_TASKS 1
+#define CONFIGURE_MAXIMUM_POSIX_THREADS 2
+
+#define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION
+
+#define CONFIGURE_RTEMS_INIT_TASKS_TABLE
+
+#define CONFIGURE_INIT_TASK_STACK_SIZE (10U * 1024U)
+
+#define CONFIGURE_INIT_TASK_ATTRIBUTES RTEMS_FLOATING_POINT
+
+#define CONFIGURE_INIT
+
+#include <rtems/confdefs.h>
+
+/* end of file */
diff --git a/testsuites/libtests/rcxx01/thread.cc b/testsuites/libtests/rcxx01/thread.cc
new file mode 100644
index 0000000000..49d55c5127
--- /dev/null
+++ b/testsuites/libtests/rcxx01/thread.cc
@@ -0,0 +1,90 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2015 Chris Johns <chrisj at rtems.org>
+ * All rights reserved.
+ */
+
+#include <chrono>
+#include <iostream>
+#include <thread>
+#include <mutex>
+
+#include <rtems/c++/thread>
+
+using namespace std::chrono_literals;
+
+extern "C" void rcxx_run_test(void);
+
+struct test_thread
+{
+ test_thread();
+
+ void start();
+ bool running();
+ void body(const char* title, int i, char c);
+
+ rtems::thread::thread thread_default;
+ rtems::thread::thread thread_attr;
+
+ std::mutex mutex;
+
+ bool finished;
+};
+
+test_thread::test_thread()
+ : finished(false)
+{
+}
+
+void test_thread::start()
+{
+ thread_default = rtems::thread::thread(&test_thread::body, this,
+ "default", 1, 'D');
+
+ rtems::thread::attributes attr;
+
+ attr.set_name("RTHREAD");
+ attr.set_priority(5);
+ attr.set_stack_size(32 * 1024);
+
+ thread_attr = rtems::thread::thread(attr, &test_thread::body, this,
+ "attr", 2, 'R');
+}
+
+void test_thread::body(const char* title, int i, char c)
+{
+ std::cout << "Thread: start: " << title << std::endl
+ << ' ' << i << ' ' << c << std::endl;
+
+ size_t count = 5;
+
+ while (count--) {
+ std::this_thread::sleep_for(1s);
+ }
+
+ std::cout << "Thread: end: " << title << std::endl;
+
+ std::lock_guard<std::mutex> lock(mutex);
+
+ finished = true;
+}
+
+bool test_thread::running()
+{
+ std::lock_guard<std::mutex> lock(mutex);
+ return finished == false;
+}
+
+void rcxx_run_test(void)
+{
+ try {
+ test_thread tt;
+ tt.start();
+ while (tt.running())
+ std::this_thread::sleep_for(1s);
+ } catch (...) {
+ std::cout << "Thread: ouch" << std::endl;
+ throw;
+ }
+}
--
2.21.0 (Apple Git-122.2)
More information about the devel
mailing list