From: Chris Johns <chr...@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 <chr...@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 <chr...@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 <chr...@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 <chr...@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 <chr...@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 <chr...@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) _______________________________________________ devel mailing list devel@rtems.org http://lists.rtems.org/mailman/listinfo/devel