[rtems commit] cpukit/librcxx: Add a C++ thread interface with attributes

Chris Johns chrisj at rtems.org
Thu Oct 8 00:10:45 UTC 2020


Module:    rtems
Branch:    master
Commit:    cf95826089f36a42a69a1b12691e453428c0b2af
Changeset: http://git.rtems.org/rtems/commit/?id=cf95826089f36a42a69a1b12691e453428c0b2af

Author:    Chris Johns <chrisj at rtems.org>
Date:      Sat Oct  3 16:13:24 2020 +1000

cpukit/librcxx: Add a C++ thread interface with attributes

---

 cpukit/include/rtems/error.hpp            |  68 +++++
 cpukit/include/rtems/thread.hpp           | 477 ++++++++++++++++++++++++++++++
 cpukit/librtemscxx/error.cpp              |  76 +++++
 cpukit/librtemscxx/thread.cpp             | 416 ++++++++++++++++++++++++++
 spec/build/cpukit/grp.yml                 |   2 +
 spec/build/cpukit/librtemscxx.yml         |  21 ++
 spec/build/testsuites/libtests/grp.yml    |   2 +
 spec/build/testsuites/libtests/rcxx01.yml |  22 ++
 testsuites/libtests/rcxx01/init.c         |  89 ++++++
 testsuites/libtests/rcxx01/rcxx01.doc     |  16 +
 testsuites/libtests/rcxx01/rcxx01.scn     |  13 +
 testsuites/libtests/rcxx01/thread.cpp     | 110 +++++++
 12 files changed, 1312 insertions(+)

diff --git a/cpukit/include/rtems/error.hpp b/cpukit/include/rtems/error.hpp
new file mode 100644
index 0000000..a62ee96
--- /dev/null
+++ b/cpukit/include/rtems/error.hpp
@@ -0,0 +1,68 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+
+/**
+ * @file
+ *
+ * @ingroup RTEMSC++
+ *
+ * @brief RTEMS Error exception.
+ *
+ * Provide an error exception for RTEMS errors.
+ */
+
+/*
+ * Copyright (C) 2020 Chris Johns (http://contemporary.software)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if !defined(RTEMS_ERROR_HPP)
+#define RTEMS_ERROR_HPP
+
+#include <stdexcept>
+#include <string>
+
+#include <rtems/error.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/thread.hpp b/cpukit/include/rtems/thread.hpp
new file mode 100644
index 0000000..e90e664
--- /dev/null
+++ b/cpukit/include/rtems/thread.hpp
@@ -0,0 +1,477 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+
+/**
+ * @file
+ *
+ * @ingroup RTEMSC++
+ *
+ * @brief C++ standard thread support with thread attribute control.
+ *
+ * Provide a way to create a thread in C++ with attributes that let
+ * you control the real-time embedded parameters need to run
+ * threads on RTEMS.
+ *
+ * The code requires the `-std=c++17` option to access `std::invoke()`.
+ */
+
+/*
+ * Copyright (C) 2020 Chris Johns (http://contemporary.software)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if !defined(RTEMS_THREAD_HPP)
+#define RTEMS_THREAD_HPP
+
+#include <functional>
+#include <iostream>
+#include <string>
+#include <thread>
+#include <utility>
+
+namespace rtems
+{
+  namespace thread
+  {
+    /**
+     * @brief Manage the attributes of a thread.
+     */
+    class attributes
+    {
+    public:
+      /**
+       * The scheduler attribute.
+       */
+      enum sched_attr {
+        sched_inherit,    /**< Inherit the scheduler attributes
+                           *   from the creating thread. */
+        sched_explicit    /**< Explicitly set the scheduler to these
+                           *   attributes. */
+      };
+
+      /**
+       * The scheduler policies.
+       */
+      enum sched_policy {
+        sched_other,      /**< Other scheduler policy */
+        sched_fifo,       /**< FIFO scheduler policy */
+        sched_roundrobin, /**< Round robin scheduler policy */
+        sched_sporadic    /**< Sporadic scheduler policy */
+      };
+
+      /**
+       * Construct a thread attributes object with the current settings of the
+       * executing thread. The stack size is set to the configured minimum
+       * stack size.
+       */
+      attributes ();
+
+      /*
+       * Copy construct the thread attributes.
+       *
+       * @param attr The attributes to copy.
+       */
+      attributes (const attributes& attr);
+
+      /**
+       * Set the name of the thread. The thread is a classic API thread and
+       * the name is only 4 characters.
+       *
+       * @param name The name as a string.
+       */
+      void set_name (const std::string& name);
+
+      /**
+       * Set the name of the thread. The thread is a classic API thread and
+       * the name is only 4 characters.
+       *
+       * @param name The name as a string.
+       */
+      void set_name (const char* name);
+
+      /**
+       * Get the name of the thread.
+       *
+       * @retval const std::string& The name of the thread.
+       */
+      const std::string& get_name () const;
+
+      /**
+       * Set the priority of the thread.
+       *
+       * @param priority The POSIX API priority of the thread.
+       */
+      void set_priority (int priority);
+
+      /**
+       * Get the POSIX API priority of the thread.
+       *
+       * @retval int The POSIX API thread priority.
+       */
+      int get_priority () const;
+
+      /**
+       * Set the stack size. If the size is less than the configured minimum
+       * the minimum value is used.
+       *
+       * @param size The stack size in bytes.
+       */
+      void set_stack_size (size_t size);
+
+      /**
+       * Get the stack size.
+       *
+       * @retval size_t The stack size in bytes.
+       */
+      size_t get_stack_size () const;
+
+      /**
+       * Set the scheduler name. If not set no scheduler is set.
+       *
+       * @parrm scheduler The name of the scheduler.
+       */
+      void set_scheduler (const std::string& scheduler);
+
+      /**
+       * Set the scheduler name. If not set no scheduler is set.
+       */
+      void set_scheduler (const char* scheduler);
+
+      /**
+       * Get scheduler name.
+       */
+      const std::string& get_scheduler ();
+
+      /**
+       * Get the attributes' scheduler attribute for the thread.
+       *
+       * @return sched_attr The attributes' scheduler attribute
+       */
+      sched_attr get_scheduler_attr () const;
+
+      /**
+       * Set the scheduler policy for the thread. This call sets the
+       * scheduler attribute to @ref sched_explicit.
+       *
+       * @param policy The scheduler policy.
+       */
+      void set_scheduler_policy (sched_policy policy);
+
+      /**
+       * Get the scheduler policy for the thread.
+       */
+      sched_policy get_scheduler_policy () const;
+
+      /**
+       * Commit any changes to the executing thread.
+       *
+       * @note only the priority and attribute of a thread can be changed. The
+       * name and stack size are ignored.
+       */
+      void commit ();
+
+      /**
+       * Update the attribute values from the executing thread. The attributes
+       * are updated from the current thread when constructed and the values
+       * returned are those held since then. If another thread changes the
+       * attributes of the current thread those changes will not be seen until
+       * this method is called. Except for the name and stack size any local
+       * changes made will lost then the update call is made.
+       */
+      void update ();
+
+      /**
+       * Copy operator.
+       */
+      attributes& operator= (const attributes& attr);
+
+      /**
+       * The comparison operator does not check the name or stack size
+       * of a thread.
+       */
+      bool operator== (const attributes& attr) const;
+
+    private:
+      std::string  name;        /**< Name of the thread */
+      int          priority;    /**< POSIX API priority */
+      size_t       stack_size;  /**< Stack size in bytes */
+      std::string  scheduler;   /**< Name of the scheduler */
+      sched_attr   attr;        /**< Scheduler's attribute */
+      sched_policy policy;      /**< Scheduler's policy */
+      /* affinity, cpu set size is? */
+    };
+
+    /**
+     * @brief Create a thread with thread attributes.
+     *
+     * Create a thread optionally with thread attributes. The usage of this
+     * class follows the C++ standard for std::thread. The standard support
+     * for creating a thread does not let you control the attributes of a
+     * thread and control is important in embedded real-time
+     * applications. This class lets you control a thread attributes and use
+     * the extensive an excellent thread support the C++ standard provides.
+     *
+     * There is no indication attribute support for threads will be added to
+     * the C++ standard and what it will look like. The support provided here
+     * is designed to make as little impact on a code base as possible. While
+     * the attributes supported are specific to RTEMS they are common to all
+     * embedded operating systems.
+     *
+     * The support provided here is specific to GCC due to the use of some
+     * non-standard interfaces to get the indices of the template argument
+     * pack in new thread's context. A standards only solution would be
+     * preferred.
+     */
+    class thread
+    {
+      friend void* thread_generic_entry (void* arg);
+
+      /**
+       * Base state class to interface to derived template of the thread
+       * state from the generic entry point for the thread.
+       */
+      struct state_base
+      {
+        virtual ~state_base ();
+        virtual const attributes get_attributes () = 0;
+        virtual void run () = 0;
+      };
+
+      /**
+       * The state is passed to the new thread context as a unique
+       * pointer. This handles the hand over and clean up.
+       */
+      using state_ptr = std::unique_ptr<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 equivalent constructor. The attributes will be the
+       * same as the executing thread with a default thread name and the
+       * configured minimum stack size.
+       */
+      template<typename F, typename... Args>
+      explicit thread (F&& func, Args&&... args);
+
+      /**
+       * Create a thread with the provided attributes. The entry point and
+       * optional arguments are the same as std::thread.
+       */
+      template <typename A, typename F, typename ...Args,
+                class = enable_if_attributes<A>>
+      explicit thread (A&& attr, F&& func, Args&&... args);
+
+      /**
+       * Move the thread id to this instance.
+       */
+      thread& operator= (thread&& thread_);
+
+      void swap(thread& thread_) noexcept;
+
+      bool joinable() const noexcept;
+
+      /*
+       * Constrain use. These are not available.
+       */
+      thread (thread&) = delete;
+      thread (const thread&) = delete;
+      thread (const thread&&) = delete;
+      thread& operator= (const thread&) = delete;
+
+      std::thread::id get_id() const noexcept;
+
+    private:
+
+      id id_;
+
+      /**
+       * Invoke the thread's entry point with the parameter pack in the new
+       * thread's context. This object holds the parameters copied onto the
+       * new thread's stack making them available to entry point routine.
+       */
+      template<typename Parms>
+      struct invoker {
+        Parms p;
+
+        template<size_t Index>
+        static std::__tuple_element_t<Index, Parms>&& declval ();
+
+        template<size_t... Ind>
+        auto invoke (std::_Index_tuple<Ind...>)
+          noexcept (noexcept (std::invoke (declval<Ind>()...)))
+          -> decltype (std::invoke (declval<Ind> ()...)) {
+          return std::invoke (std::get<Ind> (std::move (p))...);
+        }
+
+        using indices =
+          typename std::_Build_index_tuple<std::tuple_size<Parms>::value>::__type;
+
+        void run () {
+          invoke (indices ());
+        }
+      };
+
+      /**
+       * The state holds the invoker with the parameters. The generic entry
+       * point calls the virtual methods to get the attributes and to run the
+       * new thread in the new thread's context.
+       */
+      template<typename Invoker>
+      struct state : state_base {
+        const attributes attr;
+        Invoker          i;
+
+        state (const attributes& attr, Invoker&& i)
+          : attr (attr),
+            i (std::forward<Invoker> (i)) {
+        }
+
+        const attributes get_attributes () override {
+          return attr;
+        }
+
+        void run () override {
+          i.run ();
+        }
+      };
+
+      /**
+       * Make the state. This dynamic memory is managed by the unique pointer
+       * and is passed to the generic thread entry point.
+       */
+      template<typename Invoker>
+      static state_ptr
+      make_state (const attributes& attr, Invoker&& i) {
+        using state_impl = state<Invoker>;
+        return state_ptr{ new state_impl (attr, std::forward<Invoker> (i)) };
+      }
+
+      /**
+       * Decay the parameters so they can be correctly packed into the
+       * parameter tuple.
+       */
+      template<typename... T>
+      using decayed_tuple = std::tuple<typename std::decay<T>::type...>;
+
+      /**
+       * Make the invoker with the parameters.
+       */
+      template<typename F, typename... Args>
+      static invoker<decayed_tuple<F, Args...>>
+      make_invoker (F&& func, Args&&... args)
+      {
+        return {
+          decayed_tuple<F, Args...> {
+            std::forward<F> (func), std::forward<Args> (args)...
+          }
+        };
+      }
+
+      /**
+       * Create and start the thread.
+       */
+      void start_thread (state_ptr s);
+    };
+
+    template <class T>
+    inline typename std::decay<T>::type
+    decay_copy (T&& t) {
+      return std::forward<T> (t);
+    }
+
+    template<typename F, typename... Args>
+    thread::thread (F&& func, Args&&... args)
+      : id_ (0)  {
+      attributes attr;
+      start_thread (
+        make_state (attr,
+                    make_invoker (decay_copy (std::forward<F> (func)),
+                                  decay_copy (std::forward<Args> (args))...))
+      );
+    }
+
+    template<typename A, typename F, typename... Args,
+             class = thread::enable_if_attributes<A>>
+    thread::thread (A&& attr, F&& func, Args&&... args)
+      : id_ (0) {
+      start_thread (
+        make_state (attr,
+                    make_invoker (decay_copy (std::forward<F> (func)),
+                                  decay_copy (std::forward<Args> (args))...))
+      );
+    }
+
+    inline std::thread::id thread::get_id() const noexcept {
+      return std::thread::id (id_.id_);
+    }
+
+    inline bool
+    operator== (thread::id l, thread::id r) noexcept {
+      return l.id_ == r.id_;
+    }
+
+    inline bool
+    operator!= (thread::id l, thread::id r) noexcept {
+      return !(l == r);
+    }
+
+    template<class C, class T>
+    inline std::basic_ostream<C, T>&
+    operator<< (std::basic_ostream<C, T>& out, thread::id id_) {
+      return out << std::thread::id (id_.id_);
+    }
+  };
+};
+
+#endif
diff --git a/cpukit/librtemscxx/error.cpp b/cpukit/librtemscxx/error.cpp
new file mode 100644
index 0000000..ba46a8c
--- /dev/null
+++ b/cpukit/librtemscxx/error.cpp
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+
+/*
+ * Copyright (C) 2020 Chris Johns (http://contemporary.software)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <rtems/error.hpp>
+
+namespace rtems
+{
+  runtime_error::runtime_error (const rtems_status_code sc)
+    : std::runtime_error (::rtems_status_text (sc)),
+      sc (sc)
+  {
+  }
+
+  runtime_error::runtime_error (const rtems_status_code sc,
+                                const std::string&      what)
+    : std::runtime_error (what + ": " + ::rtems_status_text (sc)),
+      sc (sc)
+  {
+  }
+
+  runtime_error::runtime_error (const rtems_status_code sc,
+                                const char*             what)
+    : std::runtime_error (std::string (what) + ": " + ::rtems_status_text (sc)),
+      sc (sc)
+  {
+  }
+
+  runtime_error::~runtime_error()
+  {
+  }
+
+  void
+  runtime_error_check (const rtems_status_code sc)
+  {
+    if (sc != RTEMS_SUCCESSFUL)
+      throw runtime_error (sc);
+  }
+
+  void
+  runtime_error_check (const rtems_status_code sc, const std::string& what)
+  {
+    if (sc != RTEMS_SUCCESSFUL)
+      throw runtime_error (sc, what);
+  }
+
+  void
+  runtime_error_check (const rtems_status_code sc, const char* what)
+  {
+    if (sc != RTEMS_SUCCESSFUL)
+      throw runtime_error (sc, what);
+  }
+};
diff --git a/cpukit/librtemscxx/thread.cpp b/cpukit/librtemscxx/thread.cpp
new file mode 100644
index 0000000..11bf3df
--- /dev/null
+++ b/cpukit/librtemscxx/thread.cpp
@@ -0,0 +1,416 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+
+/*
+ * Copyright (C) 2020 Chris Johns (http://contemporary.software)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if !defined(_GNU_SOURCE)
+#define _GNU_SOURCE
+#endif
+
+#include <system_error>
+
+#include <rtems/error.hpp>
+#include <rtems/thread.hpp>
+
+#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),
+        attr (sched_inherit),
+        policy (sched_fifo)
+    {
+      update ();
+    }
+
+    attributes::attributes (const attributes& attr)
+      : name (attr.name),
+        priority (attr.priority),
+        stack_size (attr.stack_size),
+        scheduler (attr.scheduler),
+        attr (attr.attr),
+        policy (attr.policy)
+    {
+    }
+
+    void
+    attributes::set_name (const std::string& name_)
+    {
+      name = name_;
+    }
+
+    void
+    attributes::set_name (const char* name_)
+    {
+      name = name_;
+    }
+
+    const std::string&
+    attributes::get_name () const
+    {
+      return name;
+    }
+
+    void
+    attributes::set_priority (int priority_)
+    {
+      priority = priority_;
+    }
+
+    int
+    attributes::get_priority () const
+    {
+      return priority;
+    }
+
+    void
+    attributes::set_stack_size(size_t size)
+    {
+      stack_size = size;
+    }
+
+    size_t
+    attributes::get_stack_size () const
+    {
+      return stack_size;
+    }
+
+    void
+    attributes::set_scheduler (const std::string& scheduler_)
+    {
+      scheduler = scheduler_;
+    }
+
+    void
+    attributes::set_scheduler (const char* scheduler_)
+    {
+      scheduler = scheduler_;
+    }
+
+    const std::string&
+    attributes::get_scheduler ()
+    {
+      return scheduler;
+    }
+
+    attributes::sched_attr
+    attributes::get_scheduler_attr () const
+    {
+      return attr;
+    }
+
+    void
+    attributes::set_scheduler_policy (sched_policy policy_)
+    {
+      attr = sched_explicit;
+      policy = policy_;
+    }
+
+    attributes::sched_policy
+    attributes::get_scheduler_policy () const
+    {
+      return policy;
+    }
+
+    void
+    attributes::commit ()
+    {
+      pthread_t pid = ::pthread_self ();
+
+      system_error_check (::pthread_setname_np (pid, name.c_str ()),
+                          "getting name");
+
+      int                spolicy;
+      struct sched_param sched_param;
+
+      system_error_check (::pthread_getschedparam (::pthread_self (),
+                                                   &spolicy,
+                                                   &sched_param),
+                          "getting scheduler parameters");
+
+      switch (policy) {
+      case sched_other:
+        spolicy = SCHED_OTHER;
+        break;
+      case sched_fifo:
+        spolicy = SCHED_FIFO;
+        break;
+      case sched_roundrobin:
+        spolicy = SCHED_RR;
+        break;
+      case sched_sporadic:
+        spolicy = SCHED_SPORADIC;
+        break;
+      default:
+        system_error_check (EINVAL, "get scheduler policy");
+        break;
+      }
+
+      sched_param.sched_priority = priority;
+
+      system_error_check (::pthread_setschedparam (::pthread_self (),
+                                                   spolicy,
+                                                   &sched_param),
+                          "getting scheduler parameters");
+
+      if (!scheduler.empty ()) {
+        char sname[4] = { ' ', ' ', ' ', ' ' };
+        for (size_t c = 0; c < sizeof (sname); ++c) {
+          if (c >= scheduler.length ())
+            break;
+          sname[c] = scheduler[c];
+        }
+        rtems_name scheduler_name = rtems_build_name (sname[0],
+                                                      sname[1],
+                                                      sname[2],
+                                                      sname[3]);
+        rtems_id scheduler_id;
+        runtime_error_check (::rtems_scheduler_ident (scheduler_name,
+                                                      &scheduler_id),
+                             "get scheduler id");
+        // runtime_error_check (::rtems_task_set_scheduler (RTEMS_SELF,
+        //                                                  scheduler_id,
+        //                                                  1),
+        //                      "set scheduler id");
+      }
+    }
+
+    void
+    attributes::update ()
+    {
+      char buf[32];
+      system_error_check (::pthread_getname_np (::pthread_self (),
+                                                buf,
+                                                sizeof (buf)),
+                          "getting name");
+      name = buf;
+
+      int                spolicy;
+      struct sched_param sched_param;
+      system_error_check (::pthread_getschedparam (::pthread_self (),
+                                                   &spolicy,
+                                                   &sched_param),
+                          "getting scheduler parameters");
+
+      switch (spolicy) {
+      case SCHED_OTHER:
+        policy = sched_other;
+        break;
+      case SCHED_FIFO:
+        policy = sched_fifo;
+        break;
+      case SCHED_RR:
+        policy = sched_roundrobin;
+        break;
+      case SCHED_SPORADIC:
+        policy = sched_sporadic;
+        break;
+      default:
+        system_error_check (EINVAL, "get scheduler policy");
+        break;
+      }
+      priority = sched_param.sched_priority;
+
+      pthread_attr_t pattr;
+      system_error_check (::pthread_getattr_np (::pthread_self (), &pattr),
+                          "getting thread attributes");
+      system_error_check (::pthread_attr_getstacksize (&pattr, &stack_size),
+                          "getting stack size");
+      int inheritsched = 0;
+      system_error_check (::pthread_attr_getinheritsched (&pattr, &inheritsched),
+                          "getting inherited sheduler attribute");
+      switch (inheritsched) {
+      case PTHREAD_INHERIT_SCHED:
+        attr = sched_inherit;
+        break;
+      case PTHREAD_EXPLICIT_SCHED:
+        attr = sched_explicit;
+        break;
+      default:
+        system_error_check (EINVAL, "get scheduler attribute");
+        break;
+      }
+
+      rtems_id scheduler_id;
+      runtime_error_check (::rtems_task_get_scheduler (RTEMS_SELF, &scheduler_id));
+#if HAVE_GET_SCHEDULER_NAME
+      char name[5];
+      if (!get_scheduler_name (scheduler_id, &name[0]))
+        system_error_check (ENOENT, "get scheduler name");
+      scheduler = name;
+#endif
+    }
+
+    attributes&
+    attributes::operator= (const attributes& other)
+    {
+      name = other.name;
+      priority = other.priority;
+      stack_size = other.stack_size;
+      attr = other.attr;
+      policy = other.policy;
+      return *this;
+    }
+
+    bool
+    attributes::operator== (const attributes& other) const
+    {
+      return
+        name == other.name &&
+        priority == other.priority &&
+        stack_size == other.stack_size &&
+        attr == other.attr &&
+        policy == other.policy;
+    }
+
+    void*
+    thread_generic_entry (void* arg)
+    {
+      thread::state_ptr s{ static_cast<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_attr () == attributes::sched_inherit)
+        ::pthread_attr_setinheritsched (&pattr, PTHREAD_INHERIT_SCHED);
+      else
+        ::pthread_attr_setinheritsched (&pattr, PTHREAD_EXPLICIT_SCHED);
+
+      system_error_check (::pthread_attr_setstacksize(&pattr,
+                                                      attr.get_stack_size ()),
+                          "set stack size");
+
+      /*
+       * Hold the new thread in the state's run handler until the rest
+       * of the thread is set up after the create call.
+       */
+      system_error_check (::pthread_create (&id_.id_,
+                                            &pattr,
+                                            thread_generic_entry,
+                                            s.get ()),
+                          "create thread");
+
+      system_error_check (::pthread_setname_np (id_.id_,
+                                                attr.get_name ().c_str ()),
+                          "setting thread name");
+
+      ::pthread_attr_destroy (&pattr);
+
+      s.release ();
+    };
+  };
+};
diff --git a/spec/build/cpukit/grp.yml b/spec/build/cpukit/grp.yml
index 3a285d0..91fa2d9 100644
--- a/spec/build/cpukit/grp.yml
+++ b/spec/build/cpukit/grp.yml
@@ -28,6 +28,8 @@ links:
 - role: build-dependency
   uid: librtemscpu
 - role: build-dependency
+  uid: librtemscxx
+- role: build-dependency
   uid: librtemsdfltcfg
 - role: build-dependency
   uid: librtemstest
diff --git a/spec/build/cpukit/librtemscxx.yml b/spec/build/cpukit/librtemscxx.yml
new file mode 100644
index 0000000..9a924bd
--- /dev/null
+++ b/spec/build/cpukit/librtemscxx.yml
@@ -0,0 +1,21 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+build-type: library
+cflags: []
+copyrights:
+- Copyright (C) 2020 Chris Johns (http://contemporary.software)
+cppflags: []
+cxxflags: [-std=c++17]
+enabled-by: true
+includes: []
+install:
+- destination: ${BSP_INCLUDEDIR}
+  source:
+  - cpukit/include/rtems/c++/error
+  - cpukit/include/rtems/c++/thread
+install-path: ${BSP_LIBDIR}
+links: []
+source:
+- cpukit/librtemscxx/error.cpp
+- cpukit/librtemscxx/thread.cpp
+target: rtemscxx
+type: build
diff --git a/spec/build/testsuites/libtests/grp.yml b/spec/build/testsuites/libtests/grp.yml
index 2b1f272..b9ca014 100644
--- a/spec/build/testsuites/libtests/grp.yml
+++ b/spec/build/testsuites/libtests/grp.yml
@@ -215,6 +215,8 @@ links:
 - role: build-dependency
   uid: rbheap01
 - role: build-dependency
+  uid: rcxx01
+- role: build-dependency
   uid: read
 - role: build-dependency
   uid: readv
diff --git a/spec/build/testsuites/libtests/rcxx01.yml b/spec/build/testsuites/libtests/rcxx01.yml
new file mode 100644
index 0000000..864ad4d
--- /dev/null
+++ b/spec/build/testsuites/libtests/rcxx01.yml
@@ -0,0 +1,22 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+build-type: test-program
+cflags: []
+copyrights:
+- Copyright (C) 2020 Chris Johns (http://contemporary.software)
+cppflags: []
+cxxflags: [-std=c++17]
+enabled-by: true
+features: c cxx cxxprogram
+includes: []
+ldflags: []
+links: []
+source:
+- testsuites/libtests/rcxx01/init.c
+- testsuites/libtests/rcxx01/thread.cpp
+stlib: []
+target: testsuites/libtests/rcxx01.exe
+type: build
+use-after: []
+use-before:
+- rtemsdefaultconfig
+- rtemscxx
diff --git a/testsuites/libtests/rcxx01/init.c b/testsuites/libtests/rcxx01/init.c
new file mode 100644
index 0000000..87d3155
--- /dev/null
+++ b/testsuites/libtests/rcxx01/init.c
@@ -0,0 +1,89 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+
+/*
+ * Copyright (C) 2020 Chris Johns (http://contemporary.software)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <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_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 0000000..3fd41b0
--- /dev/null
+++ b/testsuites/libtests/rcxx01/rcxx01.doc
@@ -0,0 +1,16 @@
+# Copyright (c) 2019 Chris Johns <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 0000000..37bf2d3
--- /dev/null
+++ b/testsuites/libtests/rcxx01/rcxx01.scn
@@ -0,0 +1,13 @@
+*** BEGIN OF TEST RCXX 1 ***
+*** TEST VERSION: 5.0.0.7ba04a62227286dcd3da20ea7319d9c64b8f5fd1
+*** TEST STATE: EXPECTED-PASS
+*** TEST BUILD:
+*** TEST TOOLS: 7.5.0 20191114 (RTEMS 5, RSB ceb811fa19ddcfdd449a8da8f1107e6e592727b6, Newlib d14714c69)
+Thread: start: default
+ 1 D
+Thread: start: attr
+ 2 R
+Thread: end: default
+Thread: end: attr
+
+*** END OF TEST RCXX 1 ***
diff --git a/testsuites/libtests/rcxx01/thread.cpp b/testsuites/libtests/rcxx01/thread.cpp
new file mode 100644
index 0000000..05a9de8
--- /dev/null
+++ b/testsuites/libtests/rcxx01/thread.cpp
@@ -0,0 +1,110 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+
+/*
+ * Copyright (C) 2020 Chris Johns (http://contemporary.software)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <chrono>
+#include <iostream>
+#include <thread>
+#include <mutex>
+
+#include <rtems/thread.hpp>
+
+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;
+  }
+}



More information about the vc mailing list