Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:14

0001 /* SPDX-License-Identifier: BSD-2-Clause */
0002 
0003 /**
0004  * @file
0005  *
0006  * @ingroup RTEMSC++
0007  *
0008  * @brief C++ standard thread support with thread attribute control.
0009  *
0010  * Provide a way to create a thread in C++ with attributes that let
0011  * you control the real-time embedded parameters need to run
0012  * threads on RTEMS.
0013  *
0014  * The code requires the `-std=c++17` option to access `std::invoke()`.
0015  */
0016 
0017 /*
0018  * Copyright (C) 2020 Chris Johns (http://contemporary.software)
0019  *
0020  * Redistribution and use in source and binary forms, with or without
0021  * modification, are permitted provided that the following conditions
0022  * are met:
0023  * 1. Redistributions of source code must retain the above copyright
0024  *    notice, this list of conditions and the following disclaimer.
0025  * 2. Redistributions in binary form must reproduce the above copyright
0026  *    notice, this list of conditions and the following disclaimer in the
0027  *    documentation and/or other materials provided with the distribution.
0028  *
0029  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0030  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0031  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0032  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0033  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0034  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0035  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0036  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0037  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0038  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0039  * POSSIBILITY OF SUCH DAMAGE.
0040  */
0041 
0042 #if !defined(RTEMS_THREAD_HPP)
0043 #define RTEMS_THREAD_HPP
0044 
0045 #include <functional>
0046 #include <iostream>
0047 #include <string>
0048 #include <thread>
0049 #include <utility>
0050 
0051 namespace rtems
0052 {
0053   namespace thread
0054   {
0055     /**
0056      * @brief Manage the attributes of a thread.
0057      */
0058     class attributes
0059     {
0060     public:
0061       /**
0062        * The scheduler attribute.
0063        */
0064       enum sched_attr {
0065         sched_inherit,    /**< Inherit the scheduler attributes
0066                            *   from the creating thread. */
0067         sched_explicit    /**< Explicitly set the scheduler to these
0068                            *   attributes. */
0069       };
0070 
0071       /**
0072        * The scheduler policies.
0073        */
0074       enum sched_policy {
0075         sched_other,      /**< Other scheduler policy */
0076         sched_fifo,       /**< FIFO scheduler policy */
0077         sched_roundrobin, /**< Round robin scheduler policy */
0078         sched_sporadic    /**< Sporadic scheduler policy */
0079       };
0080 
0081       /**
0082        * Construct a thread attributes object with the current settings of the
0083        * executing thread. The stack size is set to the configured minimum
0084        * stack size.
0085        */
0086       attributes();
0087 
0088       /*
0089        * Copy construct the thread attributes.
0090        *
0091        * @param attr The attributes to copy.
0092        */
0093       attributes(const attributes& attr);
0094 
0095       /**
0096        * Set the name of the thread. The thread is a classic API thread and
0097        * the name is only 4 characters.
0098        *
0099        * @param name The name as a string.
0100        */
0101       void set_name(const std::string& name);
0102 
0103       /**
0104        * Set the name of the thread. The thread is a classic API thread and
0105        * the name is only 4 characters.
0106        *
0107        * @param name The name as a string.
0108        */
0109       void set_name(const char* name);
0110 
0111       /**
0112        * Get the name of the thread.
0113        *
0114        * @retval const std::string& The name of the thread.
0115        */
0116       const std::string& get_name() const;
0117 
0118       /**
0119        * Set the priority of the thread.
0120        *
0121        * @param priority The POSIX API priority of the thread.
0122        */
0123       void set_priority(int priority);
0124 
0125       /**
0126        * Get the POSIX API priority of the thread.
0127        *
0128        * @retval int The POSIX API thread priority.
0129        */
0130       int get_priority() const;
0131 
0132       /**
0133        * Set the stack size. If the size is less than the configured minimum
0134        * the minimum value is used.
0135        *
0136        * @param size The stack size in bytes.
0137        */
0138       void set_stack_size(size_t size);
0139 
0140       /**
0141        * Get the stack size.
0142        *
0143        * @retval size_t The stack size in bytes.
0144        */
0145       size_t get_stack_size() const;
0146 
0147       /**
0148        * Set the scheduler name. If not set no scheduler is set.
0149        *
0150        * @parrm scheduler The name of the scheduler.
0151        */
0152       void set_scheduler(const std::string& scheduler);
0153 
0154       /**
0155        * Set the scheduler name. If not set no scheduler is set.
0156        */
0157       void set_scheduler(const char* scheduler);
0158 
0159       /**
0160        * Get scheduler name.
0161        */
0162       const std::string& get_scheduler();
0163 
0164       /**
0165        * Get the attributes' scheduler attribute for the thread.
0166        *
0167        * @return sched_attr The attributes' scheduler attribute
0168        */
0169       sched_attr get_scheduler_attr() const;
0170 
0171       /**
0172        * Set the scheduler policy for the thread. This call sets the
0173        * scheduler attribute to @ref sched_explicit.
0174        *
0175        * @param policy The scheduler policy.
0176        */
0177       void set_scheduler_policy(sched_policy policy);
0178 
0179       /**
0180        * Get the scheduler policy for the thread.
0181        */
0182       sched_policy get_scheduler_policy() const;
0183 
0184       /**
0185        * Commit any changes to the executing thread.
0186        *
0187        * @note only the priority and attribute of a thread can be changed. The
0188        * name and stack size are ignored.
0189        */
0190       void commit();
0191 
0192       /**
0193        * Update the attribute values from the executing thread. The attributes
0194        * are updated from the current thread when constructed and the values
0195        * returned are those held since then. If another thread changes the
0196        * attributes of the current thread those changes will not be seen until
0197        * this method is called. Except for the name and stack size any local
0198        * changes made will lost then the update call is made.
0199        */
0200       void update();
0201 
0202       /**
0203        * Copy operator.
0204        */
0205       attributes& operator=(const attributes& attr);
0206 
0207       /**
0208        * The comparison operator does not check the name or stack size
0209        * of a thread.
0210        */
0211       bool operator==(const attributes& attr) const;
0212 
0213     private:
0214       std::string  name;        /**< Name of the thread */
0215       int          priority;    /**< POSIX API priority */
0216       size_t       stack_size;  /**< Stack size in bytes */
0217       std::string  scheduler;   /**< Name of the scheduler */
0218       sched_attr   attr;        /**< Scheduler's attribute */
0219       sched_policy policy;      /**< Scheduler's policy */
0220       /* affinity, cpu set size is? */
0221     };
0222 
0223     template <class T>
0224     inline typename std::decay<T>::type
0225     decay_copy(T&& t) {
0226       return std::forward<T>(t);
0227     }
0228 
0229     /**
0230      * @brief Create a thread with thread attributes.
0231      *
0232      * Create a thread optionally with thread attributes. The usage of this
0233      * class follows the C++ standard for std::thread. The standard support
0234      * for creating a thread does not let you control the attributes of a
0235      * thread and control is important in embedded real-time
0236      * applications. This class lets you control a thread attributes and use
0237      * the extensive an excellent thread support the C++ standard provides.
0238      *
0239      * There is no indication attribute support for threads will be added to
0240      * the C++ standard and what it will look like. The support provided here
0241      * is designed to make as little impact on a code base as possible. While
0242      * the attributes supported are specific to RTEMS they are common to all
0243      * embedded operating systems.
0244      *
0245      * The support provided here is specific to GCC due to the use of some
0246      * non-standard interfaces to get the indices of the template argument
0247      * pack in new thread's context. A standards only solution would be
0248      * preferred.
0249      */
0250     class thread
0251     {
0252       friend void* thread_generic_entry(void* arg);
0253 
0254       /**
0255        * Base state class to interface to derived template of the thread
0256        * state from the generic entry point for the thread.
0257        */
0258       struct state_base
0259       {
0260         virtual ~state_base();
0261         virtual const attributes get_attributes() = 0;
0262         virtual void run() = 0;
0263       };
0264 
0265       /**
0266        * The state is passed to the new thread context as a unique
0267        * pointer. This handles the hand over and clean up.
0268        */
0269       using state_ptr = std::unique_ptr<state_base>;
0270 
0271     public:
0272 
0273       /**
0274        * Template check to see if the first argument of a thread is a set of
0275        * attributes.
0276        */
0277       template <typename A, class DecayA = typename std::decay<A>::type>
0278       using enable_if_attributes = typename std::enable_if
0279         <std::is_same<DecayA, attributes>::value>::type;
0280 
0281       /**
0282        * We need our own id type so the thread class can access the pthread
0283        * handle to initialise it.
0284        */
0285       class id {
0286       public:
0287         id() noexcept : id_(0) { }
0288         explicit id(pthread_t id_) : id_(id_) { }
0289       private:
0290         pthread_t id_;
0291 
0292         friend class thread;
0293         friend bool operator==(thread::id l, thread::id r) noexcept;
0294 
0295         template<class CharT, class Traits>
0296         friend std::basic_ostream<CharT, Traits>&
0297         operator<<(std::basic_ostream<CharT, Traits>& out, thread::id id_);
0298       };
0299 
0300       /**
0301        * The default thread constructions.
0302        */
0303       thread() noexcept = default;
0304 
0305       /**
0306        * The std::thread equivalent constructor. The attributes will be the
0307        * same as the executing thread with a default thread name and the
0308        * configured minimum stack size.
0309        */
0310       template<typename F, typename... Args>
0311       explicit thread(F&& func, Args&&... args);
0312 
0313       /**
0314        * Create a thread with the provided attributes. The entry point and
0315        * optional arguments are the same as std::thread.
0316        */
0317       template <typename A, typename F, typename ...Args,
0318                 class = enable_if_attributes<A>>
0319       explicit thread(A&& attr, F&& func, Args&&... args)
0320         : id_(0) {
0321         start_thread(
0322           make_state(attr,
0323                      make_invoker(decay_copy(std::forward<F>(func)),
0324                                   decay_copy(std::forward<Args>(args))...))
0325         );
0326       }
0327 
0328       /**
0329        * Move the thread id to this instance.
0330        */
0331       thread& operator=(thread&& thread_);
0332 
0333       void swap(thread& thread_) noexcept;
0334 
0335       bool joinable() const noexcept;
0336 
0337       void join();
0338 
0339       void detach();
0340 
0341       /*
0342        * Constrain use. These are not available.
0343        */
0344       thread(thread&) = delete;
0345       thread(const thread&) = delete;
0346       thread(const thread&&) = delete;
0347       thread& operator=(const thread&) = delete;
0348 
0349       std::thread::id get_id() const noexcept;
0350 
0351     private:
0352 
0353       id id_;
0354 
0355       /**
0356        * Invoke the thread's entry point with the parameter pack in the new
0357        * thread's context. This object holds the parameters copied onto the
0358        * new thread's stack making them available to entry point routine.
0359        */
0360       template<typename Parms>
0361       struct invoker {
0362         Parms p;
0363 
0364         template<size_t Index>
0365         static std::__tuple_element_t<Index, Parms>&& declval();
0366 
0367         template<size_t... Ind>
0368         auto invoke(std::_Index_tuple<Ind...>)
0369           noexcept(noexcept(std::invoke(declval<Ind>()...)))
0370           -> decltype(std::invoke(declval<Ind>()...)) {
0371           return std::invoke(std::get<Ind>(std::move(p))...);
0372         }
0373 
0374         using indices =
0375           typename std::_Build_index_tuple<std::tuple_size<Parms>::value>::__type;
0376 
0377         void run() {
0378           invoke(indices());
0379         }
0380       };
0381 
0382       /**
0383        * The state holds the invoker with the parameters. The generic entry
0384        * point calls the virtual methods to get the attributes and to run the
0385        * new thread in the new thread's context.
0386        */
0387       template<typename Invoker>
0388       struct state : state_base {
0389         const attributes attr;
0390         Invoker          i;
0391 
0392         state(const attributes& attr, Invoker&& i)
0393           : attr(attr),
0394             i(std::forward<Invoker>(i)) {
0395         }
0396 
0397         const attributes get_attributes() override {
0398           return attr;
0399         }
0400 
0401         void run() override {
0402           i.run();
0403         }
0404       };
0405 
0406       /**
0407        * Make the state. This dynamic memory is managed by the unique pointer
0408        * and is passed to the generic thread entry point.
0409        */
0410       template<typename Invoker>
0411       static state_ptr
0412       make_state(const attributes& attr, Invoker&& i) {
0413         using state_impl = state<Invoker>;
0414         return state_ptr{ new state_impl(attr, std::forward<Invoker>(i)) };
0415       }
0416 
0417       /**
0418        * Decay the parameters so they can be correctly packed into the
0419        * parameter tuple.
0420        */
0421       template<typename... T>
0422       using decayed_tuple = std::tuple<typename std::decay<T>::type...>;
0423 
0424       /**
0425        * Make the invoker with the parameters.
0426        */
0427       template<typename F, typename... Args>
0428       static invoker<decayed_tuple<F, Args...>>
0429       make_invoker(F&& func, Args&&... args)
0430       {
0431         return {
0432           decayed_tuple<F, Args...> {
0433             std::forward<F>(func), std::forward<Args>(args)...
0434           }
0435         };
0436       }
0437 
0438       /**
0439        * Create and start the thread.
0440        */
0441       void start_thread(state_ptr s);
0442     };
0443 
0444     template<typename F, typename... Args>
0445     thread::thread(F&& func, Args&&... args)
0446       : id_(0)  {
0447       attributes attr;
0448       start_thread(
0449         make_state(attr,
0450                    make_invoker(decay_copy(std::forward<F>(func)),
0451                                 decay_copy(std::forward<Args>(args))...))
0452       );
0453     }
0454 
0455     inline std::thread::id thread::get_id() const noexcept {
0456       return std::thread::id(id_.id_);
0457     }
0458 
0459     inline bool
0460     operator==(thread::id l, thread::id r) noexcept {
0461       return l.id_ == r.id_;
0462     }
0463 
0464     inline bool
0465     operator!=(thread::id l, thread::id r) noexcept {
0466       return !(l == r);
0467     }
0468 
0469     template<class C, class T>
0470     inline std::basic_ostream<C, T>&
0471     operator<<(std::basic_ostream<C, T>& out, thread::id id_) {
0472       return out << std::thread::id(id_.id_);
0473     }
0474   };
0475 };
0476 
0477 #endif