From cf95826089f36a42a69a1b12691e453428c0b2af Mon Sep 17 00:00:00 2001 From: Chris Johns Date: Sat, 3 Oct 2020 16:13:24 +1000 Subject: cpukit/librcxx: Add a C++ thread interface with attributes --- cpukit/include/rtems/error.hpp | 68 +++++ cpukit/include/rtems/thread.hpp | 477 ++++++++++++++++++++++++++++++ cpukit/librtemscxx/error.cpp | 76 +++++ cpukit/librtemscxx/thread.cpp | 416 ++++++++++++++++++++++++++ spec/build/cpukit/grp.yml | 2 + spec/build/cpukit/librtemscxx.yml | 21 ++ spec/build/testsuites/libtests/grp.yml | 2 + spec/build/testsuites/libtests/rcxx01.yml | 22 ++ testsuites/libtests/rcxx01/init.c | 89 ++++++ testsuites/libtests/rcxx01/rcxx01.doc | 16 + testsuites/libtests/rcxx01/rcxx01.scn | 13 + testsuites/libtests/rcxx01/thread.cpp | 110 +++++++ 12 files changed, 1312 insertions(+) create mode 100644 cpukit/include/rtems/error.hpp create mode 100644 cpukit/include/rtems/thread.hpp create mode 100644 cpukit/librtemscxx/error.cpp create mode 100644 cpukit/librtemscxx/thread.cpp create mode 100644 spec/build/cpukit/librtemscxx.yml create mode 100644 spec/build/testsuites/libtests/rcxx01.yml create mode 100644 testsuites/libtests/rcxx01/init.c create mode 100644 testsuites/libtests/rcxx01/rcxx01.doc create mode 100644 testsuites/libtests/rcxx01/rcxx01.scn create mode 100644 testsuites/libtests/rcxx01/thread.cpp diff --git a/cpukit/include/rtems/error.hpp b/cpukit/include/rtems/error.hpp new file mode 100644 index 0000000000..a62ee966c6 --- /dev/null +++ b/cpukit/include/rtems/error.hpp @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/** + * @file + * + * @ingroup RTEMSC++ + * + * @brief RTEMS Error exception. + * + * Provide an error exception for RTEMS errors. + */ + +/* + * Copyright (C) 2020 Chris Johns (http://contemporary.software) + * + * 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. + */ + +#if !defined(RTEMS_ERROR_HPP) +#define RTEMS_ERROR_HPP + +#include +#include + +#include + +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/thread.hpp b/cpukit/include/rtems/thread.hpp new file mode 100644 index 0000000000..e90e664dfa --- /dev/null +++ b/cpukit/include/rtems/thread.hpp @@ -0,0 +1,477 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/** + * @file + * + * @ingroup RTEMSC++ + * + * @brief C++ standard thread support with thread attribute control. + * + * Provide a way to create a thread in C++ with attributes that let + * you control the real-time embedded parameters need to run + * threads on RTEMS. + * + * The code requires the `-std=c++17` option to access `std::invoke()`. + */ + +/* + * Copyright (C) 2020 Chris Johns (http://contemporary.software) + * + * 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. + */ + +#if !defined(RTEMS_THREAD_HPP) +#define RTEMS_THREAD_HPP + +#include +#include +#include +#include +#include + +namespace rtems +{ + namespace thread + { + /** + * @brief Manage the attributes of a thread. + */ + class attributes + { + public: + /** + * The scheduler attribute. + */ + enum sched_attr { + sched_inherit, /**< Inherit the scheduler attributes + * from the creating thread. */ + sched_explicit /**< Explicitly set the scheduler to these + * attributes. */ + }; + + /** + * The scheduler policies. + */ + enum sched_policy { + sched_other, /**< Other scheduler policy */ + sched_fifo, /**< FIFO scheduler policy */ + sched_roundrobin, /**< Round robin 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 attributes' scheduler attribute for the thread. + * + * @return sched_attr The attributes' scheduler attribute + */ + sched_attr get_scheduler_attr () const; + + /** + * Set the scheduler policy for the thread. This call sets the + * scheduler attribute to @ref sched_explicit. + * + * @param policy The scheduler policy. + */ + void set_scheduler_policy (sched_policy policy); + + /** + * 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 attribute 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 comparison 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_attr attr; /**< Scheduler's attribute */ + 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 real-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 + * the attributes supported 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; + + public: + + /** + * Template check to see if the first argument of a thread is a set of + * attributes. + */ + template ::type> + using enable_if_attributes = typename std::enable_if + ::value>::type; + + /** + * 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 + friend std::basic_ostream& + operator<< (std::basic_ostream& out, thread::id id_); + }; + + /** + * The default thread constructions. + */ + thread () noexcept = default; + + /** + * The std::thread equivalent constructor. The attributes will be the + * same as the executing thread with a default thread name and the + * configured minimum stack size. + */ + template + 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 > + 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 + struct invoker { + Parms p; + + template + static std::__tuple_element_t&& declval (); + + template + auto invoke (std::_Index_tuple) + noexcept (noexcept (std::invoke (declval()...))) + -> decltype (std::invoke (declval ()...)) { + return std::invoke (std::get (std::move (p))...); + } + + using indices = + typename std::_Build_index_tuple::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 + struct state : state_base { + const attributes attr; + Invoker i; + + state (const attributes& attr, Invoker&& i) + : attr (attr), + i (std::forward (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 + static state_ptr + make_state (const attributes& attr, Invoker&& i) { + using state_impl = state; + return state_ptr{ new state_impl (attr, std::forward (i)) }; + } + + /** + * Decay the parameters so they can be correctly packed into the + * parameter tuple. + */ + template + using decayed_tuple = std::tuple::type...>; + + /** + * Make the invoker with the parameters. + */ + template + static invoker> + make_invoker (F&& func, Args&&... args) + { + return { + decayed_tuple { + std::forward (func), std::forward (args)... + } + }; + } + + /** + * Create and start the thread. + */ + void start_thread (state_ptr s); + }; + + template + inline typename std::decay::type + decay_copy (T&& t) { + return std::forward (t); + } + + template + thread::thread (F&& func, Args&&... args) + : id_ (0) { + attributes attr; + start_thread ( + make_state (attr, + make_invoker (decay_copy (std::forward (func)), + decay_copy (std::forward (args))...)) + ); + } + + template> + thread::thread (A&& attr, F&& func, Args&&... args) + : id_ (0) { + start_thread ( + make_state (attr, + make_invoker (decay_copy (std::forward (func)), + decay_copy (std::forward (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 + inline std::basic_ostream& + operator<< (std::basic_ostream& out, thread::id id_) { + return out << std::thread::id (id_.id_); + } + }; +}; + +#endif diff --git a/cpukit/librtemscxx/error.cpp b/cpukit/librtemscxx/error.cpp new file mode 100644 index 0000000000..ba46a8c2a5 --- /dev/null +++ b/cpukit/librtemscxx/error.cpp @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/* + * Copyright (C) 2020 Chris Johns (http://contemporary.software) + * + * 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 + +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.cpp b/cpukit/librtemscxx/thread.cpp new file mode 100644 index 0000000000..11bf3df230 --- /dev/null +++ b/cpukit/librtemscxx/thread.cpp @@ -0,0 +1,416 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/* + * Copyright (C) 2020 Chris Johns (http://contemporary.software) + * + * 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. + */ + +#if !defined(_GNU_SOURCE) +#define _GNU_SOURCE +#endif + +#include + +#include +#include + +#include + +#include + +#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), + attr (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), + attr (attr.attr), + 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_attr + attributes::get_scheduler_attr () const + { + return attr; + } + + void + attributes::set_scheduler_policy (sched_policy policy_) + { + attr = 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 pattr; + system_error_check (::pthread_getattr_np (::pthread_self (), &pattr), + "getting thread attributes"); + system_error_check (::pthread_attr_getstacksize (&pattr, &stack_size), + "getting stack size"); + int inheritsched = 0; + system_error_check (::pthread_attr_getinheritsched (&pattr, &inheritsched), + "getting inherited sheduler attribute"); + switch (inheritsched) { + case PTHREAD_INHERIT_SCHED: + attr = sched_inherit; + break; + case PTHREAD_EXPLICIT_SCHED: + attr = sched_explicit; + break; + default: + system_error_check (EINVAL, "get scheduler attribute"); + 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& other) + { + name = other.name; + priority = other.priority; + stack_size = other.stack_size; + attr = other.attr; + policy = other.policy; + return *this; + } + + bool + attributes::operator== (const attributes& other) const + { + return + name == other.name && + priority == other.priority && + stack_size == other.stack_size && + attr == other.attr && + policy == other.policy; + } + + void* + thread_generic_entry (void* arg) + { + thread::state_ptr s{ static_cast (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_attr () == 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/grp.yml b/spec/build/cpukit/grp.yml index 3a285d03fc..91fa2d9625 100644 --- a/spec/build/cpukit/grp.yml +++ b/spec/build/cpukit/grp.yml @@ -27,6 +27,8 @@ links: uid: libpppd - role: build-dependency uid: librtemscpu +- role: build-dependency + uid: librtemscxx - role: build-dependency uid: librtemsdfltcfg - role: build-dependency diff --git a/spec/build/cpukit/librtemscxx.yml b/spec/build/cpukit/librtemscxx.yml new file mode 100644 index 0000000000..9a924bd789 --- /dev/null +++ b/spec/build/cpukit/librtemscxx.yml @@ -0,0 +1,21 @@ +SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause +build-type: library +cflags: [] +copyrights: +- Copyright (C) 2020 Chris Johns (http://contemporary.software) +cppflags: [] +cxxflags: [-std=c++17] +enabled-by: true +includes: [] +install: +- destination: ${BSP_INCLUDEDIR} + source: + - cpukit/include/rtems/c++/error + - cpukit/include/rtems/c++/thread +install-path: ${BSP_LIBDIR} +links: [] +source: +- cpukit/librtemscxx/error.cpp +- cpukit/librtemscxx/thread.cpp +target: rtemscxx +type: build diff --git a/spec/build/testsuites/libtests/grp.yml b/spec/build/testsuites/libtests/grp.yml index 2b1f2727cf..b9ca014b0d 100644 --- a/spec/build/testsuites/libtests/grp.yml +++ b/spec/build/testsuites/libtests/grp.yml @@ -214,6 +214,8 @@ links: uid: pwdgrp02 - role: build-dependency uid: rbheap01 +- role: build-dependency + uid: rcxx01 - role: build-dependency uid: read - role: build-dependency diff --git a/spec/build/testsuites/libtests/rcxx01.yml b/spec/build/testsuites/libtests/rcxx01.yml new file mode 100644 index 0000000000..864ad4d9d6 --- /dev/null +++ b/spec/build/testsuites/libtests/rcxx01.yml @@ -0,0 +1,22 @@ +SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause +build-type: test-program +cflags: [] +copyrights: +- Copyright (C) 2020 Chris Johns (http://contemporary.software) +cppflags: [] +cxxflags: [-std=c++17] +enabled-by: true +features: c cxx cxxprogram +includes: [] +ldflags: [] +links: [] +source: +- testsuites/libtests/rcxx01/init.c +- testsuites/libtests/rcxx01/thread.cpp +stlib: [] +target: testsuites/libtests/rcxx01.exe +type: build +use-after: [] +use-before: +- rtemsdefaultconfig +- rtemscxx diff --git a/testsuites/libtests/rcxx01/init.c b/testsuites/libtests/rcxx01/init.c new file mode 100644 index 0000000000..87d3155c7f --- /dev/null +++ b/testsuites/libtests/rcxx01/init.c @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/* + * Copyright (C) 2020 Chris Johns (http://contemporary.software) + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include +#include + +#include +#include +#include +#include +#include +#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 + +#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER +#define CONFIGURE_APPLICATION_NEEDS_SIMPLE_CONSOLE_DRIVER + +#define CONFIGURE_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 + +/* end of file */ diff --git a/testsuites/libtests/rcxx01/rcxx01.doc b/testsuites/libtests/rcxx01/rcxx01.doc new file mode 100644 index 0000000000..3fd41b029b --- /dev/null +++ b/testsuites/libtests/rcxx01/rcxx01.doc @@ -0,0 +1,16 @@ +# Copyright (c) 2019 Chris Johns +# + +This file describes the directives and concepts tested by this test set. + +test set name: rcxx01 + +directives: + + rtems::thread::thread + rtems::thread::attributes + +concepts: + ++ Create a thread using the default method, ie like std::thread. +* Create a thread with changed attributes. diff --git a/testsuites/libtests/rcxx01/rcxx01.scn b/testsuites/libtests/rcxx01/rcxx01.scn new file mode 100644 index 0000000000..37bf2d3f7a --- /dev/null +++ b/testsuites/libtests/rcxx01/rcxx01.scn @@ -0,0 +1,13 @@ +*** BEGIN OF TEST RCXX 1 *** +*** TEST VERSION: 5.0.0.7ba04a62227286dcd3da20ea7319d9c64b8f5fd1 +*** TEST STATE: EXPECTED-PASS +*** TEST BUILD: +*** TEST TOOLS: 7.5.0 20191114 (RTEMS 5, RSB ceb811fa19ddcfdd449a8da8f1107e6e592727b6, Newlib d14714c69) +Thread: start: default + 1 D +Thread: start: attr + 2 R +Thread: end: default +Thread: end: attr + +*** END OF TEST RCXX 1 *** diff --git a/testsuites/libtests/rcxx01/thread.cpp b/testsuites/libtests/rcxx01/thread.cpp new file mode 100644 index 0000000000..05a9de8c48 --- /dev/null +++ b/testsuites/libtests/rcxx01/thread.cpp @@ -0,0 +1,110 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/* + * Copyright (C) 2020 Chris Johns (http://contemporary.software) + * + * 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 +#include +#include +#include + +#include + +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 lock(mutex); + + finished = true; +} + +bool test_thread::running() +{ + std::lock_guard 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; + } +} -- cgit v1.2.3