![]() |
|
|||
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
[ Source navigation ] | [ Diff markup ] | [ Identifier search ] | [ general search ] |
This page was automatically generated by the 2.3.7 LXR engine. The LXR team |
![]() ![]() |