On 3/10/20 10:21 pm, Joel Sherrill wrote: > Will this show up in documentation somewhere?
Yes this is what I am planing. I was thinking of adding a Languages section to the User manual and this would be part of the C++ section. We would need C and Ada added. > It does seem like we should say something about C++ threads and this. Yes I agree. > What about C11 threads? That is C? This is about c++17 support. > Test doesn't appear to cover much. It tests: - The standard C++ thread equivalent interface works: thread_default = rtems::thread::thread(&test_thread::body, this, "default", 1, 'D'); This checks the right constructor is selected. This thread should have the same thread attributes and the standard library call. - Inherit the current thread attributes with this single line: rtems::thread::attributes attr; - Modify the attributes: attr.set_name("RTHREAD"); attr.set_priority(5); attr.set_stack_size(32 * 1024); - Create a thread with these new attributes: thread_attr = rtems::thread::thread(attr, &test_thread::body, this, "attr", 2, 'R'); Th test makes sure the hidden complexity needed to handle the variable template arguments, move those to a new thread context and then have them made available to thread body all work. - Some more of the attributes could be tested. > More interspersed > Great, comments with them. Thanks for the review. Chris > > On Sat, Oct 3, 2020, 1:23 AM <chr...@rtems.org <mailto:chr...@rtems.org>> > wrote: > > From: Chris Johns <chr...@rtems.org <mailto:chr...@rtems.org>> > > --- > cpukit/include/rtems/c++/error | 65 +++ > cpukit/include/rtems/c++/thread | 469 ++++++++++++++++++++++ > cpukit/librtemscxx/error.cc | 76 ++++ > cpukit/librtemscxx/thread.cc | 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 | 69 ++++ > testsuites/libtests/rcxx01/rcxx01.doc | 16 + > testsuites/libtests/rcxx01/rcxx01.scn | 13 + > testsuites/libtests/rcxx01/thread.cc | 90 +++++ > 12 files changed, 1261 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/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.cc > > diff --git a/cpukit/include/rtems/c++/error > b/cpukit/include/rtems/c++/error > new file mode 100644 > index 0000000000..8b9d875e0f > --- /dev/null > +++ b/cpukit/include/rtems/c++/error > @@ -0,0 +1,65 @@ > +/* -*- C++ -*- > + * 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. > + */ > +/** > + * @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..c3f18ab3cf > --- /dev/null > +++ b/cpukit/include/rtems/c++/thread > @@ -0,0 +1,469 @@ > +/* -*- C++ -*- > + * 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. > + */ > +/** > + * @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. > > > The thread appears to be a POSIX thread. > It uses `pthread_setname_np` to set the name. > + * > + * @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 changedTheeeeee name and stack > size are > > + * ignored. > > Is there a line break missing before the note? I will fix this. > + */ > + 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 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. > + */ > > > Since we have clang on some targets, any idea what it does and if this will > work? Should it disable itself for not GCC? Are there clang tools available? I would have tested clang if I could but I have no idea how to build clang for RTEMS and which BSPs are supported? I would prefer we support clang be supported. It should be hard to make this happen if you are able to follow how these templates work. It does get involved. > + 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>; > + > + public: > + > + /** > + * 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; > + > + /** > + * 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 > > > Equivalent I have spell checked the patch and fixed the mistakes. > + * 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..c7b749803f > --- /dev/null > +++ b/cpukit/librtemscxx/error.cc > @@ -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 <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..79375a713c > --- /dev/null > +++ b/cpukit/librtemscxx/thread.cc > @@ -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 <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 <http://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 <http://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 <http://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/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..769e5a7498 > --- /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.cc > +- cpukit/librtemscxx/thread.cc > +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..6d69feeb83 > --- /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.cc > +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..b3dfa9f0c1 > --- /dev/null > +++ b/testsuites/libtests/rcxx01/init.c > @@ -0,0 +1,69 @@ > +/* > + * SPDX-License-Identifier: BSD-2-Clause > + * > + * Copyright (c) 2020 Chris Johns (http://contemporary.software) > + * All rights reserved. > + */ > + > +#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/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 <chr...@rtems.org > <mailto:chr...@rtems.org>> > +# > + > +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.cc > b/testsuites/libtests/rcxx01/thread.cc > new file mode 100644 > index 0000000000..7cb64cea5a > --- /dev/null > +++ b/testsuites/libtests/rcxx01/thread.cc > @@ -0,0 +1,90 @@ > +/* > + * SPDX-License-Identifier: BSD-2-Clause > + * > + * Copyright (c) 2020 Chris Johns (http://contemporary.software) > + * 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.24.1 > > _______________________________________________ > devel mailing list > devel@rtems.org <mailto:devel@rtems.org> > http://lists.rtems.org/mailman/listinfo/devel > _______________________________________________ devel mailing list devel@rtems.org http://lists.rtems.org/mailman/listinfo/devel