<div dir="auto"><div>Will this show up in documentation somewhere? <div dir="auto"><br></div><div dir="auto">It does seem like we should say something about C++ threads and this. </div><div dir="auto"><br></div><div dir="auto">What about C11 threads?</div><div dir="auto"><br></div><div dir="auto">Test doesn't appear to cover much. </div><div dir="auto"><br></div><div dir="auto">More interspersed</div><br><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Sat, Oct 3, 2020, 1:23 AM  <<a href="mailto:chrisj@rtems.org">chrisj@rtems.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">From: Chris Johns <<a href="mailto:chrisj@rtems.org" target="_blank" rel="noreferrer">chrisj@rtems.org</a>><br>
<br>
---<br>
 cpukit/include/rtems/c++/error            |  65 +++<br>
 cpukit/include/rtems/c++/thread           | 469 ++++++++++++++++++++++<br>
 cpukit/librtemscxx/error.cc               |  76 ++++<br>
 cpukit/librtemscxx/thread.cc              | 416 +++++++++++++++++++<br>
 spec/build/cpukit/grp.yml                 |   2 +<br>
 spec/build/cpukit/librtemscxx.yml         |  21 +<br>
 spec/build/testsuites/libtests/grp.yml    |   2 +<br>
 spec/build/testsuites/libtests/rcxx01.yml |  22 +<br>
 testsuites/libtests/rcxx01/init.c         |  69 ++++<br>
 testsuites/libtests/rcxx01/rcxx01.doc     |  16 +<br>
 testsuites/libtests/rcxx01/rcxx01.scn     |  13 +<br>
 testsuites/libtests/rcxx01/thread.cc      |  90 +++++<br>
 12 files changed, 1261 insertions(+)<br>
 create mode 100644 cpukit/include/rtems/c++/error<br>
 create mode 100644 cpukit/include/rtems/c++/thread<br>
 create mode 100644 cpukit/librtemscxx/error.cc<br>
 create mode 100644 cpukit/librtemscxx/thread.cc<br>
 create mode 100644 spec/build/cpukit/librtemscxx.yml<br>
 create mode 100644 spec/build/testsuites/libtests/rcxx01.yml<br>
 create mode 100644 testsuites/libtests/rcxx01/init.c<br>
 create mode 100644 testsuites/libtests/rcxx01/rcxx01.doc<br>
 create mode 100644 testsuites/libtests/rcxx01/rcxx01.scn<br>
 create mode 100644 testsuites/libtests/rcxx01/thread.cc<br>
<br>
diff --git a/cpukit/include/rtems/c++/error b/cpukit/include/rtems/c++/error<br>
new file mode 100644<br>
index 0000000000..8b9d875e0f<br>
--- /dev/null<br>
+++ b/cpukit/include/rtems/c++/error<br>
@@ -0,0 +1,65 @@<br>
+/* -*- C++ -*-<br>
+ * SPDX-License-Identifier: BSD-2-Clause<br>
+ *<br>
+ * Copyright (C) 2020 Chris Johns (<a href="http://contemporary.software" rel="noreferrer noreferrer" target="_blank">http://contemporary.software</a>)<br>
+ *<br>
+ * Redistribution and use in source and binary forms, with or without<br>
+ * modification, are permitted provided that the following conditions<br>
+ * are met:<br>
+ * 1. Redistributions of source code must retain the above copyright<br>
+ *    notice, this list of conditions and the following disclaimer.<br>
+ * 2. Redistributions in binary form must reproduce the above copyright<br>
+ *    notice, this list of conditions and the following disclaimer in the<br>
+ *    documentation and/or other materials provided with the distribution.<br>
+ *<br>
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"<br>
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE<br>
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE<br>
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE<br>
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR<br>
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF<br>
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS<br>
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN<br>
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)<br>
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE<br>
+ * POSSIBILITY OF SUCH DAMAGE.<br>
+ */<br>
+/**<br>
+ * @file<br>
+ *<br>
+ * @ingroup RTEMSC++<br>
+ *<br>
+ * RTEMS Error exception.<br>
+ */<br>
+<br>
+#if !defined(RTEMS_CXX_ERROR)<br>
+#define RTEMS_CXX_ERROR</blockquote></div></div><div dir="auto"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+<br>
+#include <stdexcept><br>
+#include <string><br>
+<br>
+#include <rtems.h><br>
+<br>
+namespace rtems<br>
+{<br>
+  class runtime_error :<br>
+    public std::runtime_error<br>
+  {<br>
+    const rtems_status_code sc;<br>
+  public:<br>
+    runtime_error (const rtems_status_code sc);<br>
+    runtime_error (const rtems_status_code sc, const std::string& what);<br>
+    runtime_error (const rtems_status_code sc, const char* what);<br>
+    ~runtime_error ();<br>
+  };<br>
+<br>
+  /**<br>
+   * Throw a rtems::runtime_error exception if the RTEMS status code is<br>
+   * not RTEMS_SUCCESSFUL.<br>
+   */<br>
+  void runtime_error_check (const rtems_status_code sc);<br>
+  void runtime_error_check (const rtems_status_code sc, const std::string& what);<br>
+  void runtime_error_check (const rtems_status_code sc, const char* what);<br>
+};<br>
+<br>
+#endif<br>
diff --git a/cpukit/include/rtems/c++/thread b/cpukit/include/rtems/c++/thread<br>
new file mode 100644<br>
index 0000000000..c3f18ab3cf<br>
--- /dev/null<br>
+++ b/cpukit/include/rtems/c++/thread<br>
@@ -0,0 +1,469 @@<br>
+/* -*- C++ -*-<br>
+ * SPDX-License-Identifier: BSD-2-Clause<br>
+ *<br>
+ * Copyright (C) 2020 Chris Johns (<a href="http://contemporary.software" rel="noreferrer noreferrer" target="_blank">http://contemporary.software</a>)<br>
+ *<br>
+ * Redistribution and use in source and binary forms, with or without<br>
+ * modification, are permitted provided that the following conditions<br>
+ * are met:<br>
+ * 1. Redistributions of source code must retain the above copyright<br>
+ *    notice, this list of conditions and the following disclaimer.<br>
+ * 2. Redistributions in binary form must reproduce the above copyright<br>
+ *    notice, this list of conditions and the following disclaimer in the<br>
+ *    documentation and/or other materials provided with the distribution.<br>
+ *<br>
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"<br>
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE<br>
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE<br>
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE<br>
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR<br>
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF<br>
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS<br>
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN<br>
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)<br>
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE<br>
+ * POSSIBILITY OF SUCH DAMAGE.<br>
+ */<br>
+/**<br>
+ * @file<br>
+ *<br>
+ * @ingroup RTEMSC++<br>
+ *<br>
+ * The C++ standard thread support with thread attribute control.<br>
+ *<br>
+ * The code requires the `-std=c++17` option to access `std::invoke()`.<br>
+ */<br>
+<br>
+#if !defined(RTEMS_CXX_THREAD)<br>
+#define RTEMS_CXX_THREAD<br>
+<br>
+#include <functional><br>
+#include <iostream><br>
+#include <string><br>
+#include <thread><br>
+#include <utility><br>
+<br>
+namespace rtems<br>
+{<br>
+  namespace thread<br>
+  {<br>
+    /**<br>
+     * @brief Manage the attributes of a thread.<br>
+     */<br>
+    class attributes<br>
+    {<br>
+    public:<br>
+      /**<br>
+       * The schedular modes.<br>
+       */<br>
+      enum sched_mode {<br>
+        sched_inherit,    /**< Inhert the scheduler */<br>
+        sched_explicit    /**< Explicitly set the scheduler */<br>
+      };<br>
+<br>
+      /**<br>
+       * The schedular policies.<br>
+       */<br>
+      enum sched_policy {<br>
+        sched_other,      /**< Other scheduler policy */<br>
+        sched_fifo,       /**< FIFO scheduler policy */<br>
+        sched_roundrobin, /**< Roundrobin scheduler policy */<br>
+        sched_sporadic    /**< Sporadic scheduler policy */<br>
+      };<br>
+<br>
+      /**<br>
+       * Construct a thread attributes object with the current settings of the<br>
+       * executing thread. The stack size is set to the configured minimum<br>
+       * stack size.<br>
+       */<br>
+      attributes ();<br>
+<br>
+      /*<br>
+       * Copy construct the thread attributes.<br>
+       *<br>
+       * @param attr The attributes to copy.<br>
+       */<br>
+      attributes (const attributes& attr);<br>
+<br>
+      /**<br>
+       * Set the name of the thread. The thread is a classic API thread and<br>
+       * the name is only 4 characters.</blockquote></div></div><div dir="auto"><br></div><div dir="auto">The thread appears to be a POSIX thread. </div><div dir="auto"><br></div><div dir="auto"></div><div dir="auto"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+       *<br>
+       * @param name The name as a string.<br>
+       */<br>
+      void set_name (const std::string& name);<br>
+<br>
+      /**<br>
+       * Set the name of the thread. The thread is a classic API thread and<br>
+       * the name is only 4 characters.<br>
+       *<br>
+       * @param name The name as a string.<br>
+       */<br>
+      void set_name (const char* name);<br>
+<br>
+      /**<br>
+       * Get the name of the thread.<br>
+       *<br>
+       * @retval const std::string& The name of the thread.<br>
+       */<br>
+      const std::string& get_name () const;<br>
+<br>
+      /**<br>
+       * Set the priority of the thread.<br>
+       *<br>
+       * @param priority The POSIX API priority of the thread.<br>
+       */<br>
+      void set_priority (int priority);<br>
+<br>
+      /**<br>
+       * Get the POSIX API priority of the thread.<br>
+       *<br>
+       * @retval int The POSIX API thread priority.<br>
+       */<br>
+      int get_priority () const;<br>
+<br>
+      /**<br>
+       * Set the stack size. If the size is less than the configured minimum<br>
+       * the minimum value is used.<br>
+       *<br>
+       * @param size The stack size in bytes.<br>
+       */<br>
+      void set_stack_size (size_t size);<br>
+<br>
+      /**<br>
+       * Get the stack size.<br>
+       *<br>
+       * @retval size_t The stack size in bytes.<br>
+       */<br>
+      size_t get_stack_size () const;<br>
+<br>
+      /**<br>
+       * Set the scheduler name. If not set no scheduler is set.<br>
+       *<br>
+       * @parrm scheduler The name of the scheduler.<br>
+       */<br>
+      void set_scheduler (const std::string& scheduler);<br>
+<br>
+      /**<br>
+       * Set the scheduler name. If not set no scheduler is set.<br>
+       */<br>
+      void set_scheduler (const char* scheduler);<br>
+<br>
+      /**<br>
+       * Get scheduler name.<br>
+       */<br>
+      const std::string& get_scheduler ();<br>
+<br>
+      /**<br>
+       * Get the attrobutes' scheduler mode for the thread.<br>
+       *<br>
+       * @return sched_mode The attributes; scheduler mode<br>
+       */<br>
+      sched_mode get_scheduler_mode () const;<br>
+<br>
+      /**<br>
+       * Set the scheduler policy for the thread. This call sets the<br>
+       * schedular mode to @ref sched_explicit.<br>
+       *<br>
+       * @param policy The scheduler policy.<br>
+       */<br>
+      void set_scheduler_policy (sched_policy policyr);<br>
+<br>
+      /**<br>
+       * Get the scheduler policy for the thread.<br>
+       */<br>
+      sched_policy get_scheduler_policy () const;<br>
+<br>
+      /**<br>
+       * Commit any changes to the executing thread. @note only the priority<br>
+       * and modes of a thread can be changedTheeeeee name and stack size are</blockquote></div></div><div dir="auto"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+       * ignored.<br></blockquote></div></div><div dir="auto"><br></div><div dir="auto"><br></div><div dir="auto">Is there a line break missing before the note?</div><div dir="auto"><br></div><div dir="auto"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+       */<br>
+      void commit ();<br>
+<br>
+      /**<br>
+       * Update the attribute values from the executing thread. The attributes<br>
+       * are updated from the current thread when constructed and the values<br>
+       * returned are those held since then. If another thread changes the<br>
+       * attributes of the current thread those changes will not be seen until<br>
+       * this method is called. Except for the name and stack size any local<br>
+       * changes made will lost then the update call is made.<br>
+       */<br>
+      void update ();<br>
+<br>
+      /**<br>
+       * Copy operator.<br>
+       */<br>
+      attributes& operator= (const attributes& attr);<br>
+<br>
+      /**<br>
+       * The comparision operator does not check the name or stack size<br>
+       * of a thread.<br>
+       */<br>
+      bool operator== (const attributes& attr) const;<br>
+<br>
+    private:<br>
+      std::string  name;        /**< Name of the thread */<br>
+      int          priority;    /**< POSIX API priority */<br>
+      size_t       stack_size;  /**< Stack size in bytes */<br>
+      std::string  scheduler;   /**< Name of the scheduler */<br>
+      sched_mode   mode;        /**< Scheduler's mode */<br>
+      sched_policy policy;      /**< Scheduler's policy */<br>
+      /* affinity, cpu set size is? */<br>
+    };<br>
+<br>
+    /**<br>
+     * @brief Create a thread with thread attributes.<br>
+     *<br>
+     * Create a thread optionally with thread attributes. The usage of this<br>
+     * class follows the C++ standard for std::thread. The standard support<br>
+     * for creating a thread does not let you control the attributes of a<br>
+     * thread and control is important in embedded real-time<br>
+     * applications. This class lets you control a thread attributes and use<br>
+     * the extensive an excellent thread support the C++ standard provides.<br>
+     *<br>
+     * There is no indication attribute support for threads will be added to<br>
+     * the C++ standard and what it will look like. The support provided here<br>
+     * is designed to make as little impact on a code base as possible. While<br>
+     * the attributes supported are specific to RTEMS they are common to all<br>
+     * embedded operating systems.<br>
+     *<br>
+     * The support provided here is specific to GCC due to the use of some<br>
+     * non-standard interfaces to get the indices of the template argument<br>
+     * pack in new thread's context. A standards only solution would be<br>
+     * preferred.<br>
+     */<br></blockquote></div></div><div dir="auto"><br></div><div dir="auto">Since we have clang on some targets, any idea what it does and if this will work? Should it disable itself for not GCC?</div><div dir="auto"><br></div><div dir="auto"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+    class thread<br>
+    {<br>
+      friend void* thread_generic_entry (void* arg);<br>
+<br>
+      /**<br>
+       * Base state class to interface to derived template of the thread<br>
+       * state from the generic entry point for the thread.<br>
+       */<br>
+      struct state_base<br>
+      {<br>
+        virtual ~state_base ();<br>
+        virtual const attributes get_attributes () = 0;<br>
+        virtual void run () = 0;<br>
+      };<br>
+<br>
+      /**<br>
+       * The state is passed to the new thread context as a unique<br>
+       * pointer. This handles the hand over and clean up.<br>
+       */<br>
+      using state_ptr = std::unique_ptr<state_base>;<br>
+<br>
+    public:<br>
+<br>
+      /**<br>
+       * Template check to see if the first argument of a thread is a set of<br>
+       * attributes.<br>
+       */<br>
+      template <typename A, class DecayA = typename std::decay<A>::type><br>
+      using enable_if_attributes = typename std::enable_if<br>
+        <std::is_same<DecayA, attributes>::value>::type;<br>
+<br>
+      /**<br>
+       * We need our own id type so the thread class can access the pthread<br>
+       * handle to initialise it.<br>
+       */<br>
+      class id {<br>
+      public:<br>
+        id () noexcept : id_ (0) { }<br>
+        explicit id (pthread_t id_) : id_ (id_) { }<br>
+      private:<br>
+        pthread_t id_;<br>
+<br>
+        friend class thread;<br>
+        friend bool operator== (thread::id l, thread::id r) noexcept;<br>
+<br>
+        template<class CharT, class Traits><br>
+       friend std::basic_ostream<CharT, Traits>&<br>
+       operator<< (std::basic_ostream<CharT, Traits>& out, thread::id id_);<br>
+      };<br>
+<br>
+      /**<br>
+       * The default thread constructions.<br>
+       */<br>
+      thread () noexcept = default;<br>
+<br>
+      /**<br>
+       * The std::thread equivilant constructor. The attributes will be the<br></blockquote></div></div><div dir="auto"><br></div><div dir="auto">Equivalent</div><div dir="auto"><br></div><div dir="auto"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+       * same as the executing thread with a default thread name and the<br>
+       * configured minimum stack size.<br>
+       */<br>
+      template<typename F, typename... Args><br>
+      explicit thread (F&& func, Args&&... args);<br>
+<br>
+      /**<br>
+       * Create a thread with the provided attributes. The entry point and<br>
+       * optional arguments are the same as std::thread.<br>
+       */<br>
+      template <typename A, typename F, typename ...Args,<br>
+                class = enable_if_attributes<A>><br>
+      explicit thread (A&& attr, F&& func, Args&&... args);<br>
+<br>
+      /**<br>
+       * Move the thread id to this instance.<br>
+       */<br>
+      thread& operator= (thread&& thread_);<br>
+<br>
+      void swap(thread& thread_) noexcept;<br>
+<br>
+      bool joinable() const noexcept;<br>
+<br>
+      /*<br>
+       * Constrain use. These are not available.<br>
+       */<br>
+      thread (thread&) = delete;<br>
+      thread (const thread&) = delete;<br>
+      thread (const thread&&) = delete;<br>
+      thread& operator= (const thread&) = delete;<br>
+<br>
+      std::thread::id get_id() const noexcept;<br>
+<br>
+    private:<br>
+<br>
+      id id_;<br>
+<br>
+      /**<br>
+       * Invoke the thread's entry point with the parameter pack in the new<br>
+       * thread's context. This object holds the parameters copied onto the<br>
+       * new thread's stack making them available to entry point routine.<br>
+       */<br>
+      template<typename Parms><br>
+      struct invoker {<br>
+        Parms p;<br>
+<br>
+        template<size_t Index><br>
+        static std::__tuple_element_t<Index, Parms>&& declval ();<br>
+<br>
+        template<size_t... Ind><br>
+        auto invoke (std::_Index_tuple<Ind...>)<br>
+          noexcept (noexcept (std::invoke (declval<Ind>()...)))<br>
+          -> decltype (std::invoke (declval<Ind> ()...)) {<br>
+          return std::invoke (std::get<Ind> (std::move (p))...);<br>
+        }<br>
+<br>
+        using indices =<br>
+          typename std::_Build_index_tuple<std::tuple_size<Parms>::value>::__type;<br>
+<br>
+        void run () {<br>
+          invoke (indices ());<br>
+        }<br>
+      };<br>
+<br>
+      /**<br>
+       * The state holds the invoker with the parameters. The generic entry<br>
+       * point calls the virtual methods to get the attributes and to run the<br>
+       * new thread in the new thread's context..<br>
+       */<br>
+      template<typename Invoker><br>
+      struct state : state_base {<br>
+        const attributes attr;<br>
+        Invoker          i;<br>
+<br>
+        state (const attributes& attr, Invoker&& i)<br>
+          : attr (attr),<br>
+            i (std::forward<Invoker> (i)) {<br>
+        }<br>
+<br>
+        const attributes get_attributes () override {<br>
+          return attr;<br>
+        }<br>
+<br>
+        void run () override {<br>
+          i.run ();<br>
+        }<br>
+      };<br>
+<br>
+      /**<br>
+       * Make the state. This dynamic memory is managed by the unique pointer<br>
+       * and is passed to the generic thread entry point.<br>
+       */<br>
+      template<typename Invoker><br>
+      static state_ptr<br>
+      make_state (const attributes& attr, Invoker&& i) {<br>
+        using state_impl = state<Invoker>;<br>
+        return state_ptr{ new state_impl (attr, std::forward<Invoker> (i)) };<br>
+      }<br>
+<br>
+      /**<br>
+       * Decay the parameters so they can be correctly packed into the<br>
+       * parameter tuple.<br>
+       */<br>
+      template<typename... T><br>
+      using decayed_tuple = std::tuple<typename std::decay<T>::type...>;<br>
+<br>
+      /**<br>
+       * Make the invoker with the parameters.<br>
+       */<br>
+      template<typename F, typename... Args><br>
+      static invoker<decayed_tuple<F, Args...>><br>
+      make_invoker (F&& func, Args&&... args)<br>
+      {<br>
+        return {<br>
+          decayed_tuple<F, Args...> {<br>
+            std::forward<F> (func), std::forward<Args> (args)...<br>
+          }<br>
+        };<br>
+      }<br>
+<br>
+      /**<br>
+       * Create and start the thread.<br>
+       */<br>
+      void start_thread (state_ptr s);<br>
+    };<br>
+<br>
+    template <class T><br>
+    inline typename std::decay<T>::type<br>
+    decay_copy (T&& t) {<br>
+      return std::forward<T> (t);<br>
+    }<br>
+<br>
+    template<typename F, typename... Args><br>
+    thread::thread (F&& func, Args&&... args)<br>
+      : id_ (0)  {<br>
+      attributes attr;<br>
+      start_thread (<br>
+        make_state (attr,<br>
+                    make_invoker (decay_copy (std::forward<F> (func)),<br>
+                                  decay_copy (std::forward<Args> (args))...))<br>
+      );<br>
+    }<br>
+<br>
+    template<typename A, typename F, typename... Args,<br>
+             class = thread::enable_if_attributes<A>><br>
+    thread::thread (A&& attr, F&& func, Args&&... args)<br>
+      : id_ (0) {<br>
+      start_thread (<br>
+        make_state (attr,<br>
+                    make_invoker (decay_copy (std::forward<F> (func)),<br>
+                                  decay_copy (std::forward<Args> (args))...))<br>
+      );<br>
+    }<br>
+<br>
+    inline std::thread::id thread::get_id() const noexcept {<br>
+      return std::thread::id (id_.id_);<br>
+    }<br>
+<br>
+    inline bool<br>
+    operator== (thread::id l, thread::id r) noexcept {<br>
+      return l.id_ == r.id_;<br>
+    }<br>
+<br>
+    inline bool<br>
+    operator!= (thread::id l, thread::id r) noexcept {<br>
+      return !(l == r);<br>
+    }<br>
+<br>
+    template<class C, class T><br>
+    inline std::basic_ostream<C, T>&<br>
+    operator<< (std::basic_ostream<C, T>& out, thread::id id_) {<br>
+      return out << std::thread::id (id_.id_);<br>
+    }<br>
+  };<br>
+};<br>
+<br>
+#endif<br>
diff --git a/cpukit/librtemscxx/error.cc b/cpukit/librtemscxx/error.cc<br>
new file mode 100644<br>
index 0000000000..c7b749803f<br>
--- /dev/null<br>
+++ b/cpukit/librtemscxx/error.cc<br>
@@ -0,0 +1,76 @@<br>
+/*<br>
+ * SPDX-License-Identifier: BSD-2-Clause<br>
+ *<br>
+ * Copyright (C) 2020 Chris Johns (<a href="http://contemporary.software" rel="noreferrer noreferrer" target="_blank">http://contemporary.software</a>)<br>
+ *<br>
+ * Redistribution and use in source and binary forms, with or without<br>
+ * modification, are permitted provided that the following conditions<br>
+ * are met:<br>
+ * 1. Redistributions of source code must retain the above copyright<br>
+ *    notice, this list of conditions and the following disclaimer.<br>
+ * 2. Redistributions in binary form must reproduce the above copyright<br>
+ *    notice, this list of conditions and the following disclaimer in the<br>
+ *    documentation and/or other materials provided with the distribution.<br>
+ *<br>
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"<br>
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE<br>
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE<br>
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE<br>
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR<br>
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF<br>
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS<br>
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN<br>
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)<br>
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE<br>
+ * POSSIBILITY OF SUCH DAMAGE.<br>
+ */<br>
+<br>
+#include <rtems/c++/error><br>
+<br>
+namespace rtems<br>
+{<br>
+  runtime_error::runtime_error (const rtems_status_code sc)<br>
+    : std::runtime_error (::rtems_status_text (sc)),<br>
+      sc (sc)<br>
+  {<br>
+  }<br>
+<br>
+  runtime_error::runtime_error (const rtems_status_code sc,<br>
+                                const std::string&      what)<br>
+    : std::runtime_error (what + ": " + ::rtems_status_text (sc)),<br>
+      sc (sc)<br>
+  {<br>
+  }<br>
+<br>
+  runtime_error::runtime_error (const rtems_status_code sc,<br>
+                                const char*             what)<br>
+    : std::runtime_error (std::string (what) + ": " + ::rtems_status_text (sc)),<br>
+      sc (sc)<br>
+  {<br>
+  }<br>
+<br>
+  runtime_error::~runtime_error()<br>
+  {<br>
+  }<br>
+<br>
+  void<br>
+  runtime_error_check (const rtems_status_code sc)<br>
+  {<br>
+    if (sc != RTEMS_SUCCESSFUL)<br>
+      throw runtime_error (sc);<br>
+  }<br>
+<br>
+  void<br>
+  runtime_error_check (const rtems_status_code sc, const std::string& what)<br>
+  {<br>
+    if (sc != RTEMS_SUCCESSFUL)<br>
+      throw runtime_error (sc, what);<br>
+  }<br>
+<br>
+  void<br>
+  runtime_error_check (const rtems_status_code sc, const char* what)<br>
+  {<br>
+    if (sc != RTEMS_SUCCESSFUL)<br>
+      throw runtime_error (sc, what);<br>
+  }<br>
+};<br>
diff --git a/cpukit/librtemscxx/thread.cc b/cpukit/librtemscxx/thread.cc<br>
new file mode 100644<br>
index 0000000000..79375a713c<br>
--- /dev/null<br>
+++ b/cpukit/librtemscxx/thread.cc<br>
@@ -0,0 +1,416 @@<br>
+/*<br>
+ * SPDX-License-Identifier: BSD-2-Clause<br>
+ *<br>
+ * Copyright (C) 2020 Chris Johns (<a href="http://contemporary.software" rel="noreferrer noreferrer" target="_blank">http://contemporary.software</a>)<br>
+ *<br>
+ * Redistribution and use in source and binary forms, with or without<br>
+ * modification, are permitted provided that the following conditions<br>
+ * are met:<br>
+ * 1. Redistributions of source code must retain the above copyright<br>
+ *    notice, this list of conditions and the following disclaimer.<br>
+ * 2. Redistributions in binary form must reproduce the above copyright<br>
+ *    notice, this list of conditions and the following disclaimer in the<br>
+ *    documentation and/or other materials provided with the distribution.<br>
+ *<br>
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"<br>
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE<br>
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE<br>
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE<br>
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR<br>
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF<br>
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS<br>
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN<br>
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)<br>
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE<br>
+ * POSSIBILITY OF SUCH DAMAGE.<br>
+ */<br>
+<br>
+#if !defined(_GNU_SOURCE)<br>
+#define _GNU_SOURCE<br>
+#endif<br>
+<br>
+#include <system_error><br>
+<br>
+#include <rtems/c++/error><br>
+#include <rtems/c++/thread><br>
+<br>
+#include <pthread.h><br>
+<br>
+#include <rtems.h><br>
+<br>
+#if HAVE_GET_SCHEDULER_NAME<br>
+extern "C" bool get_scheduler_name (rtems_id sid, char* name);<br>
+#endif<br>
+<br>
+#if HAVE_GET_SCHEDULER_NAME<br>
+bool get_scheduler_name (rtems_id sid, char* name)<br>
+{<br>
+  name[0] = 'N';<br>
+  name[1] = 'O';<br>
+  name[2] = 'P';<br>
+  name[3] = '\0';<br>
+  return true;<br>
+}<br>
+#endif<br>
+<br>
+namespace rtems<br>
+{<br>
+  namespace thread<br>
+  {<br>
+    void<br>
+    system_error_check (int ec, const char* what)<br>
+    {<br>
+      if (ec != 0)<br>
+        throw std::system_error (ec, std::system_category(), what);<br>
+    }<br>
+<br>
+    attributes::attributes ()<br>
+      : priority (-1),<br>
+        stack_size (RTEMS_MINIMUM_STACK_SIZE),<br>
+        mode (sched_inherit),<br>
+        policy (sched_fifo)<br>
+    {<br>
+      update ();<br>
+    }<br>
+<br>
+    attributes::attributes (const attributes& attr)<br>
+      : name (<a href="http://attr.name" rel="noreferrer noreferrer" target="_blank">attr.name</a>),<br>
+        priority (attr.priority),<br>
+        stack_size (attr.stack_size),<br>
+        scheduler (attr.scheduler),<br>
+        mode (attr.mode),<br>
+        policy (attr.policy)<br>
+    {<br>
+    }<br>
+<br>
+    void<br>
+    attributes::set_name (const std::string& name_)<br>
+    {<br>
+      name = name_;<br>
+    }<br>
+<br>
+    void<br>
+    attributes::set_name (const char* name_)<br>
+    {<br>
+      name = name_;<br>
+    }<br>
+<br>
+    const std::string&<br>
+    attributes::get_name () const<br>
+    {<br>
+      return name;<br>
+    }<br>
+<br>
+    void<br>
+    attributes::set_priority (int priority_)<br>
+    {<br>
+      priority = priority_;<br>
+    }<br>
+<br>
+    int<br>
+    attributes::get_priority () const<br>
+    {<br>
+      return priority;<br>
+    }<br>
+<br>
+    void<br>
+    attributes::set_stack_size(size_t size)<br>
+    {<br>
+      stack_size = size;<br>
+    }<br>
+<br>
+    size_t<br>
+    attributes::get_stack_size () const<br>
+    {<br>
+      return stack_size;<br>
+    }<br>
+<br>
+    void<br>
+    attributes::set_scheduler (const std::string& scheduler_)<br>
+    {<br>
+      scheduler = scheduler_;<br>
+    }<br>
+<br>
+    void<br>
+    attributes::set_scheduler (const char* scheduler_)<br>
+    {<br>
+      scheduler = scheduler_;<br>
+    }<br>
+<br>
+    const std::string&<br>
+    attributes::get_scheduler ()<br>
+    {<br>
+      return scheduler;<br>
+    }<br>
+<br>
+    attributes::sched_mode<br>
+    attributes::get_scheduler_mode () const<br>
+    {<br>
+      return mode;<br>
+    }<br>
+<br>
+    void<br>
+    attributes::set_scheduler_policy (sched_policy policy_)<br>
+    {<br>
+      mode = sched_explicit;<br>
+      policy = policy_;<br>
+    }<br>
+<br>
+    attributes::sched_policy<br>
+    attributes::get_scheduler_policy () const<br>
+    {<br>
+      return policy;<br>
+    }<br>
+<br>
+    void<br>
+    attributes::commit ()<br>
+    {<br>
+      pthread_t pid = ::pthread_self ();<br>
+<br>
+      system_error_check (::pthread_setname_np (pid, name.c_str ()),<br>
+                          "getting name");<br>
+<br>
+      int                spolicy;<br>
+      struct sched_param sched_param;<br>
+<br>
+      system_error_check (::pthread_getschedparam (::pthread_self (),<br>
+                                                   &spolicy,<br>
+                                                   &sched_param),<br>
+                          "getting scheduler parameters");<br>
+<br>
+      switch (policy) {<br>
+      case sched_other:<br>
+        spolicy = SCHED_OTHER;<br>
+        break;<br>
+      case sched_fifo:<br>
+        spolicy = SCHED_FIFO;<br>
+        break;<br>
+      case sched_roundrobin:<br>
+        spolicy = SCHED_RR;<br>
+        break;<br>
+      case sched_sporadic:<br>
+        spolicy = SCHED_SPORADIC;<br>
+        break;<br>
+      default:<br>
+        system_error_check (EINVAL, "get scheduler policy");<br>
+        break;<br>
+      }<br>
+<br>
+      sched_param.sched_priority = priority;<br>
+<br>
+      system_error_check (::pthread_setschedparam (::pthread_self (),<br>
+                                                   spolicy,<br>
+                                                   &sched_param),<br>
+                          "getting scheduler parameters");<br>
+<br>
+      if (!scheduler.empty ()) {<br>
+        char sname[4] = { ' ', ' ', ' ', ' ' };<br>
+        for (size_t c = 0; c < sizeof (sname); ++c) {<br>
+          if (c >= scheduler.length ())<br>
+            break;<br>
+          sname[c] = scheduler[c];<br>
+        }<br>
+        rtems_name scheduler_name = rtems_build_name (sname[0],<br>
+                                                      sname[1],<br>
+                                                      sname[2],<br>
+                                                      sname[3]);<br>
+        rtems_id scheduler_id;<br>
+        runtime_error_check (::rtems_scheduler_ident (scheduler_name,<br>
+                                                      &scheduler_id),<br>
+                             "get scheduler id");<br>
+        // runtime_error_check (::rtems_task_set_scheduler (RTEMS_SELF,<br>
+        //                                                  scheduler_id,<br>
+        //                                                  1),<br>
+        //                      "set scheduler id");<br>
+      }<br>
+    }<br>
+<br>
+    void<br>
+    attributes::update ()<br>
+    {<br>
+      char buf[32];<br>
+      system_error_check (::pthread_getname_np (::pthread_self (),<br>
+                                                buf,<br>
+                                                sizeof (buf)),<br>
+                          "getting name");<br>
+      name = buf;<br>
+<br>
+      int                spolicy;<br>
+      struct sched_param sched_param;<br>
+      system_error_check (::pthread_getschedparam (::pthread_self (),<br>
+                                                   &spolicy,<br>
+                                                   &sched_param),<br>
+                          "getting scheduler parameters");<br>
+<br>
+      switch (spolicy) {<br>
+      case SCHED_OTHER:<br>
+        policy = sched_other;<br>
+        break;<br>
+      case SCHED_FIFO:<br>
+        policy = sched_fifo;<br>
+        break;<br>
+      case SCHED_RR:<br>
+        policy = sched_roundrobin;<br>
+        break;<br>
+      case SCHED_SPORADIC:<br>
+        policy = sched_sporadic;<br>
+        break;<br>
+      default:<br>
+        system_error_check (EINVAL, "get scheduler policy");<br>
+        break;<br>
+      }<br>
+      priority = sched_param.sched_priority;<br>
+<br>
+      pthread_attr_t attr;<br>
+      system_error_check (::pthread_getattr_np (::pthread_self (), &attr),<br>
+                          "getting thread attributes");<br>
+      system_error_check (::pthread_attr_getstacksize (&attr, &stack_size),<br>
+                          "getting stack size");<br>
+      int inheritsched = 0;<br>
+      system_error_check (::pthread_attr_getinheritsched (&attr, &inheritsched),<br>
+                          "getting inherited sheduler mode");<br>
+      switch (inheritsched) {<br>
+      case PTHREAD_INHERIT_SCHED:<br>
+        mode = sched_inherit;<br>
+        break;<br>
+      case PTHREAD_EXPLICIT_SCHED:<br>
+        mode = sched_explicit;<br>
+        break;<br>
+      default:<br>
+        system_error_check (EINVAL, "get scheduler mode");<br>
+        break;<br>
+      }<br>
+<br>
+      rtems_id scheduler_id;<br>
+      runtime_error_check (::rtems_task_get_scheduler (RTEMS_SELF, &scheduler_id));<br>
+#if HAVE_GET_SCHEDULER_NAME<br>
+      char name[5];<br>
+      if (!get_scheduler_name (scheduler_id, &name[0]))<br>
+        system_error_check (ENOENT, "get scheduler name");<br>
+      scheduler = name;<br>
+#endif<br>
+    }<br>
+<br>
+    attributes&<br>
+    attributes::operator= (const attributes& attr)<br>
+    {<br>
+      name = <a href="http://attr.name" rel="noreferrer noreferrer" target="_blank">attr.name</a>;<br>
+      priority = attr.priority;<br>
+      stack_size = attr.stack_size;<br>
+      mode = attr.mode;<br>
+      policy = attr.policy;<br>
+      return *this;<br>
+    }<br>
+<br>
+    bool<br>
+    attributes::operator== (const attributes& attr) const<br>
+    {<br>
+      return<br>
+        name == <a href="http://attr.name" rel="noreferrer noreferrer" target="_blank">attr.name</a> &&<br>
+        priority == attr.priority &&<br>
+        stack_size == attr.stack_size &&<br>
+        mode == attr.mode &&<br>
+        policy == attr.policy;<br>
+    }<br>
+<br>
+    void*<br>
+    thread_generic_entry (void* arg)<br>
+    {<br>
+      thread::state_ptr s{ static_cast<thread::state_base*> (arg) };<br>
+      try {<br>
+        s->run ();<br>
+      } catch (...) {<br>
+        std::terminate ();<br>
+      }<br>
+      return nullptr;<br>
+    }<br>
+<br>
+    thread&<br>
+    thread::operator= (thread&& thread_)<br>
+    {<br>
+      if (joinable ())<br>
+        std::terminate ();<br>
+      swap(thread_);<br>
+      return *this;<br>
+    }<br>
+<br>
+    void<br>
+    thread::swap(thread& thread_) noexcept<br>
+    {<br>
+      std::swap(id_, thread_.id_);<br>
+    }<br>
+<br>
+    bool<br>
+    thread::joinable() const noexcept<br>
+    {<br>
+      return !(id_ == id());<br>
+    }<br>
+<br>
+    thread::state_base::~state_base () = default;<br>
+<br>
+    void<br>
+    thread::start_thread (thread::state_ptr s)<br>
+    {<br>
+      const attributes attr = s->get_attributes ();<br>
+<br>
+      pthread_attr_t pattr;<br>
+<br>
+      system_error_check (::pthread_attr_init (&pattr),<br>
+                          "attribute init");<br>
+<br>
+      system_error_check (::pthread_attr_setdetachstate (&pattr,<br>
+                                                         PTHREAD_CREATE_DETACHED),<br>
+                          "set detached state");<br>
+<br>
+      struct sched_param param;<br>
+      param.sched_priority = attr.get_priority ();<br>
+      system_error_check (::pthread_attr_setschedparam (&pattr, &param),<br>
+                          "set ");<br>
+<br>
+      int spolicy;<br>
+      switch (attr.get_scheduler_policy ()) {<br>
+      case attributes::sched_other:<br>
+        spolicy = SCHED_OTHER;<br>
+        break;<br>
+      case attributes::sched_roundrobin:<br>
+        spolicy = SCHED_RR;<br>
+        break;<br>
+      case attributes::sched_sporadic:<br>
+        spolicy = SCHED_SPORADIC;<br>
+        break;<br>
+      default:<br>
+        spolicy = SCHED_FIFO;<br>
+        break;<br>
+      }<br>
+      system_error_check (::pthread_attr_setschedpolicy (&pattr, spolicy),<br>
+                          "set scheduler policy");<br>
+<br>
+      if (attr.get_scheduler_mode () == attributes::sched_inherit)<br>
+        ::pthread_attr_setinheritsched (&pattr, PTHREAD_INHERIT_SCHED);<br>
+      else<br>
+        ::pthread_attr_setinheritsched (&pattr, PTHREAD_EXPLICIT_SCHED);<br>
+<br>
+      system_error_check (::pthread_attr_setstacksize(&pattr,<br>
+                                                      attr.get_stack_size ()),<br>
+                          "set stack size");<br>
+<br>
+      /*<br>
+       * Hold the new thread in the state's run handler until the rest<br>
+       * of the thread is set up after the create call.<br>
+       */<br>
+      system_error_check (::pthread_create (&id_.id_,<br>
+                                            &pattr,<br>
+                                            thread_generic_entry,<br>
+                                            s.get ()),<br>
+                          "create thread");<br>
+<br>
+      system_error_check (::pthread_setname_np (id_.id_,<br>
+                                                attr.get_name ().c_str ()),<br>
+                          "setting thread name");<br>
+<br>
+      ::pthread_attr_destroy (&pattr);<br>
+<br>
+      s.release ();<br>
+    };<br>
+  };<br>
+};<br>
diff --git a/spec/build/cpukit/grp.yml b/spec/build/cpukit/grp.yml<br>
index 3a285d03fc..91fa2d9625 100644<br>
--- a/spec/build/cpukit/grp.yml<br>
+++ b/spec/build/cpukit/grp.yml<br>
@@ -27,6 +27,8 @@ links:<br>
   uid: libpppd<br>
 - role: build-dependency<br>
   uid: librtemscpu<br>
+- role: build-dependency<br>
+  uid: librtemscxx<br>
 - role: build-dependency<br>
   uid: librtemsdfltcfg<br>
 - role: build-dependency<br>
diff --git a/spec/build/cpukit/librtemscxx.yml b/spec/build/cpukit/librtemscxx.yml<br>
new file mode 100644<br>
index 0000000000..769e5a7498<br>
--- /dev/null<br>
+++ b/spec/build/cpukit/librtemscxx.yml<br>
@@ -0,0 +1,21 @@<br>
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause<br>
+build-type: library<br>
+cflags: []<br>
+copyrights:<br>
+- Copyright (C) 2020 Chris Johns (<a href="http://contemporary.software" rel="noreferrer noreferrer" target="_blank">http://contemporary.software</a>)<br>
+cppflags: []<br>
+cxxflags: [-std=c++17]<br>
+enabled-by: true<br>
+includes: []<br>
+install:<br>
+- destination: ${BSP_INCLUDEDIR}<br>
+  source:<br>
+  - cpukit/include/rtems/c++/error<br>
+  - cpukit/include/rtems/c++/thread<br>
+install-path: ${BSP_LIBDIR}<br>
+links: []<br>
+source:<br>
+- cpukit/librtemscxx/error.cc<br>
+- cpukit/librtemscxx/thread.cc<br>
+target: rtemscxx<br>
+type: build<br>
diff --git a/spec/build/testsuites/libtests/grp.yml b/spec/build/testsuites/libtests/grp.yml<br>
index 2b1f2727cf..b9ca014b0d 100644<br>
--- a/spec/build/testsuites/libtests/grp.yml<br>
+++ b/spec/build/testsuites/libtests/grp.yml<br>
@@ -214,6 +214,8 @@ links:<br>
   uid: pwdgrp02<br>
 - role: build-dependency<br>
   uid: rbheap01<br>
+- role: build-dependency<br>
+  uid: rcxx01<br>
 - role: build-dependency<br>
   uid: read<br>
 - role: build-dependency<br>
diff --git a/spec/build/testsuites/libtests/rcxx01.yml b/spec/build/testsuites/libtests/rcxx01.yml<br>
new file mode 100644<br>
index 0000000000..6d69feeb83<br>
--- /dev/null<br>
+++ b/spec/build/testsuites/libtests/rcxx01.yml<br>
@@ -0,0 +1,22 @@<br>
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause<br>
+build-type: test-program<br>
+cflags: []<br>
+copyrights:<br>
+- Copyright (C) 2020 Chris Johns (<a href="http://contemporary.software" rel="noreferrer noreferrer" target="_blank">http://contemporary.software</a>)<br>
+cppflags: []<br>
+cxxflags: [-std=c++17]<br>
+enabled-by: true<br>
+features: c cxx cxxprogram<br>
+includes: []<br>
+ldflags: []<br>
+links: []<br>
+source:<br>
+- testsuites/libtests/rcxx01/init.c<br>
+- testsuites/libtests/rcxx01/thread.cc<br>
+stlib: []<br>
+target: testsuites/libtests/rcxx01.exe<br>
+type: build<br>
+use-after: []<br>
+use-before:<br>
+- rtemsdefaultconfig<br>
+- rtemscxx<br>
diff --git a/testsuites/libtests/rcxx01/init.c b/testsuites/libtests/rcxx01/init.c<br>
new file mode 100644<br>
index 0000000000..b3dfa9f0c1<br>
--- /dev/null<br>
+++ b/testsuites/libtests/rcxx01/init.c<br>
@@ -0,0 +1,69 @@<br>
+/*<br>
+ * SPDX-License-Identifier: BSD-2-Clause<br>
+ *<br>
+ * Copyright (c) 2020 Chris Johns (<a href="http://contemporary.software" rel="noreferrer noreferrer" target="_blank">http://contemporary.software</a>)<br>
+ * All rights reserved.<br>
+ */<br>
+<br>
+#ifdef HAVE_CONFIG_H<br>
+#include "config.h"<br>
+#endif<br>
+<br>
+#include <bsp.h><br>
+<br>
+#include <stdlib.h><br>
+#include <stdio.h><br>
+<br>
+#include <sys/types.h><br>
+#include <sys/stat.h><br>
+#include <fcntl.h><br>
+#include <sys/ioctl.h><br>
+#include <unistd.h><br>
+#include "tmacros.h"<br>
+<br>
+const char rtems_test_name[] = "RCXX 1";<br>
+<br>
+/* forward declarations to avoid warnings */<br>
+rtems_task Init(rtems_task_argument argument);<br>
+<br>
+void rcxx_run_test(void);<br>
+<br>
+rtems_task Init(<br>
+  rtems_task_argument ignored<br>
+)<br>
+{<br>
+  TEST_BEGIN();<br>
+<br>
+  rcxx_run_test();<br>
+<br>
+  TEST_END();<br>
+  rtems_test_exit( 0 );<br>
+}<br>
+<br>
+/* configuration information */<br>
+<br>
+#include <rtems/serial_mouse.h><br>
+<br>
+#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER<br>
+#define CONFIGURE_APPLICATION_NEEDS_SIMPLE_CONSOLE_DRIVER<br>
+<br>
+#define CONFIGURE_LIBIO_MAXIMUM_FILE_DESCRIPTORS 5<br>
+<br>
+#define CONFIGURE_MEMORY_OVERHEAD           (2024)<br>
+<br>
+#define CONFIGURE_MAXIMUM_TASKS  1<br>
+#define CONFIGURE_MAXIMUM_POSIX_THREADS 2<br>
+<br>
+#define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION<br>
+<br>
+#define CONFIGURE_RTEMS_INIT_TASKS_TABLE<br>
+<br>
+#define CONFIGURE_INIT_TASK_STACK_SIZE (10U * 1024U)<br>
+<br>
+#define CONFIGURE_INIT_TASK_ATTRIBUTES RTEMS_FLOATING_POINT<br>
+<br>
+#define CONFIGURE_INIT<br>
+<br>
+#include <rtems/confdefs.h><br>
+<br>
+/* end of file */<br>
diff --git a/testsuites/libtests/rcxx01/rcxx01.doc b/testsuites/libtests/rcxx01/rcxx01.doc<br>
new file mode 100644<br>
index 0000000000..3fd41b029b<br>
--- /dev/null<br>
+++ b/testsuites/libtests/rcxx01/rcxx01.doc<br>
@@ -0,0 +1,16 @@<br>
+# Copyright (c) 2019 Chris Johns <<a href="mailto:chrisj@rtems.org" target="_blank" rel="noreferrer">chrisj@rtems.org</a>><br>
+#<br>
+<br>
+This file describes the directives and concepts tested by this test set.<br>
+<br>
+test set name: rcxx01<br>
+<br>
+directives:<br>
+<br>
+  rtems::thread::thread<br>
+  rtems::thread::attributes<br>
+<br>
+concepts:<br>
+<br>
++ Create a thread using the default method, ie like std::thread.<br>
+* Create a thread with changed attributes.<br>
diff --git a/testsuites/libtests/rcxx01/rcxx01.scn b/testsuites/libtests/rcxx01/rcxx01.scn<br>
new file mode 100644<br>
index 0000000000..37bf2d3f7a<br>
--- /dev/null<br>
+++ b/testsuites/libtests/rcxx01/rcxx01.scn<br>
@@ -0,0 +1,13 @@<br>
+*** BEGIN OF TEST RCXX 1 ***<br>
+*** TEST VERSION: 5.0.0.7ba04a62227286dcd3da20ea7319d9c64b8f5fd1<br>
+*** TEST STATE: EXPECTED-PASS<br>
+*** TEST BUILD:<br>
+*** TEST TOOLS: 7.5.0 20191114 (RTEMS 5, RSB ceb811fa19ddcfdd449a8da8f1107e6e592727b6, Newlib d14714c69)<br>
+Thread: start: default<br>
+ 1 D<br>
+Thread: start: attr<br>
+ 2 R<br>
+Thread: end: default<br>
+Thread: end: attr<br>
+<br>
+*** END OF TEST RCXX 1 ***<br>
diff --git a/testsuites/libtests/rcxx01/thread.cc b/testsuites/libtests/rcxx01/thread.cc<br>
new file mode 100644<br>
index 0000000000..7cb64cea5a<br>
--- /dev/null<br>
+++ b/testsuites/libtests/rcxx01/thread.cc<br>
@@ -0,0 +1,90 @@<br>
+/*<br>
+ * SPDX-License-Identifier: BSD-2-Clause<br>
+ *<br>
+ * Copyright (c) 2020 Chris Johns (<a href="http://contemporary.software" rel="noreferrer noreferrer" target="_blank">http://contemporary.software</a>)<br>
+ * All rights reserved.<br>
+ */<br>
+<br>
+#include <chrono><br>
+#include <iostream><br>
+#include <thread><br>
+#include <mutex><br>
+<br>
+#include <rtems/c++/thread><br>
+<br>
+using namespace std::chrono_literals;<br>
+<br>
+extern "C" void rcxx_run_test(void);<br>
+<br>
+struct test_thread<br>
+{<br>
+  test_thread();<br>
+<br>
+  void start();<br>
+  bool running();<br>
+  void body(const char* title, int i, char c);<br>
+<br>
+  rtems::thread::thread thread_default;<br>
+  rtems::thread::thread thread_attr;<br>
+<br>
+  std::mutex mutex;<br>
+<br>
+  bool finished;<br>
+};<br>
+<br>
+test_thread::test_thread()<br>
+  : finished(false)<br>
+{<br>
+}<br>
+<br>
+void test_thread::start()<br>
+{<br>
+  thread_default = rtems::thread::thread(&test_thread::body, this,<br>
+                                         "default",  1, 'D');<br>
+<br>
+  rtems::thread::attributes attr;<br>
+<br>
+  attr.set_name("RTHREAD");<br>
+  attr.set_priority(5);<br>
+  attr.set_stack_size(32 * 1024);<br>
+<br>
+  thread_attr = rtems::thread::thread(attr, &test_thread::body, this,<br>
+                                      "attr", 2, 'R');<br>
+}<br>
+<br>
+void test_thread::body(const char* title, int i, char c)<br>
+{<br>
+  std::cout << "Thread: start: " << title << std::endl<br>
+            << ' ' << i << ' ' << c << std::endl;<br>
+<br>
+  size_t count = 5;<br>
+<br>
+  while (count--) {<br>
+    std::this_thread::sleep_for(1s);<br>
+  }<br>
+<br>
+  std::cout << "Thread: end: " << title << std::endl;<br>
+<br>
+  std::lock_guard<std::mutex> lock(mutex);<br>
+<br>
+  finished = true;<br>
+}<br>
+<br>
+bool test_thread::running()<br>
+{<br>
+  std::lock_guard<std::mutex> lock(mutex);<br>
+  return finished == false;<br>
+}<br>
+<br>
+void rcxx_run_test(void)<br>
+{<br>
+  try {<br>
+    test_thread tt;<br>
+    tt.start();<br>
+    while (tt.running())<br>
+      std::this_thread::sleep_for(1s);<br>
+  } catch (...) {<br>
+    std::cout << "Thread: ouch" << std::endl;<br>
+    throw;<br>
+  }<br>
+}<br>
-- <br>
2.24.1<br>
<br>
_______________________________________________<br>
devel mailing list<br>
<a href="mailto:devel@rtems.org" target="_blank" rel="noreferrer">devel@rtems.org</a><br>
<a href="http://lists.rtems.org/mailman/listinfo/devel" rel="noreferrer noreferrer" target="_blank">http://lists.rtems.org/mailman/listinfo/devel</a><br>
</blockquote></div></div></div>