source: rtems/cpukit/include/rtems/thread.hpp @ 89a22be

Last change on this file since 89a22be was 89a22be, checked in by Chris Johns <chrisj@…>, on 10/08/20 at 23:44:36

librtemscxx: Fix white space to match the coding standard

  • Property mode set to 100644
File size: 14.3 KB
Line 
1/* SPDX-License-Identifier: BSD-2-Clause */
2
3/**
4 * @file
5 *
6 * @ingroup RTEMSC++
7 *
8 * @brief C++ standard thread support with thread attribute control.
9 *
10 * Provide a way to create a thread in C++ with attributes that let
11 * you control the real-time embedded parameters need to run
12 * threads on RTEMS.
13 *
14 * The code requires the `-std=c++17` option to access `std::invoke()`.
15 */
16
17/*
18 * Copyright (C) 2020 Chris Johns (http://contemporary.software)
19 *
20 * Redistribution and use in source and binary forms, with or without
21 * modification, are permitted provided that the following conditions
22 * are met:
23 * 1. Redistributions of source code must retain the above copyright
24 *    notice, this list of conditions and the following disclaimer.
25 * 2. Redistributions in binary form must reproduce the above copyright
26 *    notice, this list of conditions and the following disclaimer in the
27 *    documentation and/or other materials provided with the distribution.
28 *
29 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
30 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
33 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
34 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
35 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
36 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
37 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
39 * POSSIBILITY OF SUCH DAMAGE.
40 */
41
42#if !defined(RTEMS_THREAD_HPP)
43#define RTEMS_THREAD_HPP
44
45#include <functional>
46#include <iostream>
47#include <string>
48#include <thread>
49#include <utility>
50
51namespace rtems
52{
53  namespace thread
54  {
55    /**
56     * @brief Manage the attributes of a thread.
57     */
58    class attributes
59    {
60    public:
61      /**
62       * The scheduler attribute.
63       */
64      enum sched_attr {
65        sched_inherit,    /**< Inherit the scheduler attributes
66                           *   from the creating thread. */
67        sched_explicit    /**< Explicitly set the scheduler to these
68                           *   attributes. */
69      };
70
71      /**
72       * The scheduler policies.
73       */
74      enum sched_policy {
75        sched_other,      /**< Other scheduler policy */
76        sched_fifo,       /**< FIFO scheduler policy */
77        sched_roundrobin, /**< Round robin scheduler policy */
78        sched_sporadic    /**< Sporadic scheduler policy */
79      };
80
81      /**
82       * Construct a thread attributes object with the current settings of the
83       * executing thread. The stack size is set to the configured minimum
84       * stack size.
85       */
86      attributes();
87
88      /*
89       * Copy construct the thread attributes.
90       *
91       * @param attr The attributes to copy.
92       */
93      attributes(const attributes& attr);
94
95      /**
96       * Set the name of the thread. The thread is a classic API thread and
97       * the name is only 4 characters.
98       *
99       * @param name The name as a string.
100       */
101      void set_name(const std::string& name);
102
103      /**
104       * Set the name of the thread. The thread is a classic API thread and
105       * the name is only 4 characters.
106       *
107       * @param name The name as a string.
108       */
109      void set_name(const char* name);
110
111      /**
112       * Get the name of the thread.
113       *
114       * @retval const std::string& The name of the thread.
115       */
116      const std::string& get_name() const;
117
118      /**
119       * Set the priority of the thread.
120       *
121       * @param priority The POSIX API priority of the thread.
122       */
123      void set_priority(int priority);
124
125      /**
126       * Get the POSIX API priority of the thread.
127       *
128       * @retval int The POSIX API thread priority.
129       */
130      int get_priority() const;
131
132      /**
133       * Set the stack size. If the size is less than the configured minimum
134       * the minimum value is used.
135       *
136       * @param size The stack size in bytes.
137       */
138      void set_stack_size(size_t size);
139
140      /**
141       * Get the stack size.
142       *
143       * @retval size_t The stack size in bytes.
144       */
145      size_t get_stack_size() const;
146
147      /**
148       * Set the scheduler name. If not set no scheduler is set.
149       *
150       * @parrm scheduler The name of the scheduler.
151       */
152      void set_scheduler(const std::string& scheduler);
153
154      /**
155       * Set the scheduler name. If not set no scheduler is set.
156       */
157      void set_scheduler(const char* scheduler);
158
159      /**
160       * Get scheduler name.
161       */
162      const std::string& get_scheduler();
163
164      /**
165       * Get the attributes' scheduler attribute for the thread.
166       *
167       * @return sched_attr The attributes' scheduler attribute
168       */
169      sched_attr get_scheduler_attr() const;
170
171      /**
172       * Set the scheduler policy for the thread. This call sets the
173       * scheduler attribute to @ref sched_explicit.
174       *
175       * @param policy The scheduler policy.
176       */
177      void set_scheduler_policy(sched_policy policy);
178
179      /**
180       * Get the scheduler policy for the thread.
181       */
182      sched_policy get_scheduler_policy() const;
183
184      /**
185       * Commit any changes to the executing thread.
186       *
187       * @note only the priority and attribute of a thread can be changed. The
188       * name and stack size are ignored.
189       */
190      void commit();
191
192      /**
193       * Update the attribute values from the executing thread. The attributes
194       * are updated from the current thread when constructed and the values
195       * returned are those held since then. If another thread changes the
196       * attributes of the current thread those changes will not be seen until
197       * this method is called. Except for the name and stack size any local
198       * changes made will lost then the update call is made.
199       */
200      void update();
201
202      /**
203       * Copy operator.
204       */
205      attributes& operator=(const attributes& attr);
206
207      /**
208       * The comparison operator does not check the name or stack size
209       * of a thread.
210       */
211      bool operator==(const attributes& attr) const;
212
213    private:
214      std::string  name;        /**< Name of the thread */
215      int          priority;    /**< POSIX API priority */
216      size_t       stack_size;  /**< Stack size in bytes */
217      std::string  scheduler;   /**< Name of the scheduler */
218      sched_attr   attr;        /**< Scheduler's attribute */
219      sched_policy policy;      /**< Scheduler's policy */
220      /* affinity, cpu set size is? */
221    };
222
223    /**
224     * @brief Create a thread with thread attributes.
225     *
226     * Create a thread optionally with thread attributes. The usage of this
227     * class follows the C++ standard for std::thread. The standard support
228     * for creating a thread does not let you control the attributes of a
229     * thread and control is important in embedded real-time
230     * applications. This class lets you control a thread attributes and use
231     * the extensive an excellent thread support the C++ standard provides.
232     *
233     * There is no indication attribute support for threads will be added to
234     * the C++ standard and what it will look like. The support provided here
235     * is designed to make as little impact on a code base as possible. While
236     * the attributes supported are specific to RTEMS they are common to all
237     * embedded operating systems.
238     *
239     * The support provided here is specific to GCC due to the use of some
240     * non-standard interfaces to get the indices of the template argument
241     * pack in new thread's context. A standards only solution would be
242     * preferred.
243     */
244    class thread
245    {
246      friend void* thread_generic_entry(void* arg);
247
248      /**
249       * Base state class to interface to derived template of the thread
250       * state from the generic entry point for the thread.
251       */
252      struct state_base
253      {
254        virtual ~state_base();
255        virtual const attributes get_attributes() = 0;
256        virtual void run() = 0;
257      };
258
259      /**
260       * The state is passed to the new thread context as a unique
261       * pointer. This handles the hand over and clean up.
262       */
263      using state_ptr = std::unique_ptr<state_base>;
264
265    public:
266
267      /**
268       * Template check to see if the first argument of a thread is a set of
269       * attributes.
270       */
271      template <typename A, class DecayA = typename std::decay<A>::type>
272      using enable_if_attributes = typename std::enable_if
273        <std::is_same<DecayA, attributes>::value>::type;
274
275      /**
276       * We need our own id type so the thread class can access the pthread
277       * handle to initialise it.
278       */
279      class id {
280      public:
281        id() noexcept : id_(0) { }
282        explicit id(pthread_t id_) : id_(id_) { }
283      private:
284        pthread_t id_;
285
286        friend class thread;
287        friend bool operator==(thread::id l, thread::id r) noexcept;
288
289        template<class CharT, class Traits>
290        friend std::basic_ostream<CharT, Traits>&
291        operator<<(std::basic_ostream<CharT, Traits>& out, thread::id id_);
292      };
293
294      /**
295       * The default thread constructions.
296       */
297      thread() noexcept = default;
298
299      /**
300       * The std::thread equivalent constructor. The attributes will be the
301       * same as the executing thread with a default thread name and the
302       * configured minimum stack size.
303       */
304      template<typename F, typename... Args>
305      explicit thread(F&& func, Args&&... args);
306
307      /**
308       * Create a thread with the provided attributes. The entry point and
309       * optional arguments are the same as std::thread.
310       */
311      template <typename A, typename F, typename ...Args,
312                class = enable_if_attributes<A>>
313      explicit thread(A&& attr, F&& func, Args&&... args);
314
315      /**
316       * Move the thread id to this instance.
317       */
318      thread& operator=(thread&& thread_);
319
320      void swap(thread& thread_) noexcept;
321
322      bool joinable() const noexcept;
323
324      void join();
325
326      void detach();
327
328      /*
329       * Constrain use. These are not available.
330       */
331      thread(thread&) = delete;
332      thread(const thread&) = delete;
333      thread(const thread&&) = delete;
334      thread& operator=(const thread&) = delete;
335
336      std::thread::id get_id() const noexcept;
337
338    private:
339
340      id id_;
341
342      /**
343       * Invoke the thread's entry point with the parameter pack in the new
344       * thread's context. This object holds the parameters copied onto the
345       * new thread's stack making them available to entry point routine.
346       */
347      template<typename Parms>
348      struct invoker {
349        Parms p;
350
351        template<size_t Index>
352        static std::__tuple_element_t<Index, Parms>&& declval();
353
354        template<size_t... Ind>
355        auto invoke(std::_Index_tuple<Ind...>)
356          noexcept(noexcept(std::invoke(declval<Ind>()...)))
357          -> decltype(std::invoke(declval<Ind>()...)) {
358          return std::invoke(std::get<Ind>(std::move(p))...);
359        }
360
361        using indices =
362          typename std::_Build_index_tuple<std::tuple_size<Parms>::value>::__type;
363
364        void run() {
365          invoke(indices());
366        }
367      };
368
369      /**
370       * The state holds the invoker with the parameters. The generic entry
371       * point calls the virtual methods to get the attributes and to run the
372       * new thread in the new thread's context.
373       */
374      template<typename Invoker>
375      struct state : state_base {
376        const attributes attr;
377        Invoker          i;
378
379        state(const attributes& attr, Invoker&& i)
380          : attr(attr),
381            i(std::forward<Invoker>(i)) {
382        }
383
384        const attributes get_attributes() override {
385          return attr;
386        }
387
388        void run() override {
389          i.run();
390        }
391      };
392
393      /**
394       * Make the state. This dynamic memory is managed by the unique pointer
395       * and is passed to the generic thread entry point.
396       */
397      template<typename Invoker>
398      static state_ptr
399      make_state(const attributes& attr, Invoker&& i) {
400        using state_impl = state<Invoker>;
401        return state_ptr{ new state_impl(attr, std::forward<Invoker>(i)) };
402      }
403
404      /**
405       * Decay the parameters so they can be correctly packed into the
406       * parameter tuple.
407       */
408      template<typename... T>
409      using decayed_tuple = std::tuple<typename std::decay<T>::type...>;
410
411      /**
412       * Make the invoker with the parameters.
413       */
414      template<typename F, typename... Args>
415      static invoker<decayed_tuple<F, Args...>>
416      make_invoker(F&& func, Args&&... args)
417      {
418        return {
419          decayed_tuple<F, Args...> {
420            std::forward<F>(func), std::forward<Args>(args)...
421          }
422        };
423      }
424
425      /**
426       * Create and start the thread.
427       */
428      void start_thread(state_ptr s);
429    };
430
431    template <class T>
432    inline typename std::decay<T>::type
433    decay_copy(T&& t) {
434      return std::forward<T>(t);
435    }
436
437    template<typename F, typename... Args>
438    thread::thread(F&& func, Args&&... args)
439      : id_(0)  {
440      attributes attr;
441      start_thread(
442        make_state(attr,
443                   make_invoker(decay_copy(std::forward<F>(func)),
444                                decay_copy(std::forward<Args>(args))...))
445      );
446    }
447
448    template<typename A, typename F, typename... Args,
449             class = thread::enable_if_attributes<A>>
450    thread::thread(A&& attr, F&& func, Args&&... args)
451      : id_(0) {
452      start_thread(
453        make_state(attr,
454                   make_invoker(decay_copy(std::forward<F>(func)),
455                                decay_copy(std::forward<Args>(args))...))
456      );
457    }
458
459    inline std::thread::id thread::get_id() const noexcept {
460      return std::thread::id(id_.id_);
461    }
462
463    inline bool
464    operator==(thread::id l, thread::id r) noexcept {
465      return l.id_ == r.id_;
466    }
467
468    inline bool
469    operator!=(thread::id l, thread::id r) noexcept {
470      return !(l == r);
471    }
472
473    template<class C, class T>
474    inline std::basic_ostream<C, T>&
475    operator<<(std::basic_ostream<C, T>& out, thread::id id_) {
476      return out << std::thread::id(id_.id_);
477    }
478  };
479};
480
481#endif
Note: See TracBrowser for help on using the repository browser.