[PATCH v4] cpukit/librcxx: Add a C++ thread interface with attributes

Chris Johns chrisj at rtems.org
Sun Oct 4 08:49:01 UTC 2020


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 <chrisj at rtems.org <mailto:chrisj at rtems.org>> wrote:
> 
>     From: Chris Johns <chrisj at rtems.org <mailto:chrisj at rtems.org>>
> 
>     ---
>      cpukit/include/rtems/c++/error            |  65 +++
>      cpukit/include/rtems/c++/thread           | 469 ++++++++++++++++++++++
>      cpukit/librtemscxx/error.cc               |  76 ++++
>      cpukit/librtemscxx/thread.cc              | 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, &param),
>     +                          "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 <chrisj at rtems.org <mailto:chrisj at 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 at rtems.org <mailto:devel at rtems.org>
>     http://lists.rtems.org/mailman/listinfo/devel
> 


More information about the devel mailing list