EtcPal  HEAD (unstable)
ETC Platform Abstraction Layer (EtcPal)
View other versions:
thread.h
Go to the documentation of this file.
1 /******************************************************************************
2  * Copyright 2022 ETC Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *******************************************************************************
16  * This file is a part of EtcPal. For more information, go to:
17  * https://github.com/ETCLabs/EtcPal
18  ******************************************************************************/
19 
22 
23 #ifndef ETCPAL_CPP_THREAD_H_
24 #define ETCPAL_CPP_THREAD_H_
25 
26 #include <algorithm>
27 #include <cassert>
28 #include <chrono>
29 #include <functional>
30 #include <limits>
31 #include <memory>
32 #include <stdexcept>
33 #include <string>
34 #include <type_traits>
35 #include <utility>
36 #include "etcpal/common.h"
37 #include "etcpal/thread.h"
38 #include "etcpal/cpp/common.h"
39 #include "etcpal/cpp/error.h"
40 
41 namespace etcpal
42 {
127 
145 class Thread
146 {
147 public:
149  Thread() = default;
150  template <class Function,
151  class... Args,
152  typename std::enable_if<!std::is_arithmetic<Function>::value, bool>::type = true>
153  Thread(Function&& func, Args&&... args);
154  Thread(unsigned int priority, unsigned int stack_size, const char* name, void* platform_data = nullptr);
155  virtual ~Thread();
156 
157  Thread(Thread&& other) noexcept;
158  Thread& operator=(Thread&& other) noexcept;
159 
161  Thread(const Thread& other) = delete;
163  Thread& operator=(const Thread& other) = delete;
164 
165  bool joinable() const noexcept;
166 
169  unsigned int priority() const noexcept;
170  unsigned int stack_size() const noexcept;
171  const char* name() const noexcept;
172  void* platform_data() const noexcept;
173  const EtcPalThreadParams& params() const noexcept;
174  etcpal_thread_os_handle_t os_handle() const noexcept;
176 
179  Thread& SetPriority(unsigned int priority) noexcept;
180  Thread& SetStackSize(unsigned int stack_size) noexcept;
181  Thread& SetName(const char* name) noexcept;
182  Thread& SetName(const std::string& name) noexcept;
183  Thread& SetPlatformData(void* platform_data) noexcept;
184  Thread& SetParams(const EtcPalThreadParams& params) noexcept;
186 
187  template <class Function, class... Args>
188  Error Start(Function&& func, Args&&... args);
189  Error Join(int timeout_ms = ETCPAL_WAIT_FOREVER) noexcept;
190  Error Terminate() noexcept;
191 
192  static Error Sleep(unsigned int ms) noexcept;
193  template <typename Rep, typename Period>
194  static Error Sleep(const std::chrono::duration<Rep, Period>& sleep_duration) noexcept;
195 
197  using FunctionType = std::function<void()>;
199 
200 private:
201  std::unique_ptr<etcpal_thread_t> thread_;
203 };
204 
206 
207 extern "C" inline void CppThreadFn(void* arg)
208 {
209  std::unique_ptr<Thread::FunctionType> p_func(static_cast<Thread::FunctionType*>(arg));
210  // Tear the roof off the sucker
211  (*p_func)();
212 }
213 
215 
224 template <class Function, class... Args, typename std::enable_if<!std::is_arithmetic<Function>::value, bool>::type>
225 Thread::Thread(Function&& func, Args&&... args)
226 {
228  auto result = Start(std::forward<Function>(func), std::forward<Args>(args)...);
229  if (!result)
230  ETCPAL_THROW(std::runtime_error("Error while starting EtcPal thread: " + result.ToString()));
231 }
232 
242 inline Thread::Thread(unsigned int priority, unsigned int stack_size, const char* name, void* platform_data)
243  : params_{priority, stack_size, name, platform_data}
244 {
245 }
246 
252 {
253  if (thread_)
254  etcpal_thread_join(thread_.get());
255 }
256 
262 inline Thread::Thread(Thread&& other) noexcept
263 {
264  *this = std::move(other);
265 }
266 
272 inline Thread& Thread::operator=(Thread&& other) noexcept
273 {
274  thread_ = std::move(other.thread_);
275  params_ = other.params_;
276  ETCPAL_THREAD_SET_DEFAULT_PARAMS(&other.params_);
277  return *this;
278 }
279 
281 inline bool Thread::joinable() const noexcept
282 {
283  return (bool)thread_;
284 }
285 
287 inline unsigned int Thread::priority() const noexcept
288 {
289  return params_.priority;
290 }
291 
293 inline unsigned int Thread::stack_size() const noexcept
294 {
295  return params_.stack_size;
296 }
297 
299 inline const char* Thread::name() const noexcept
300 {
301  return params_.thread_name;
302 }
303 
305 inline void* Thread::platform_data() const noexcept
306 {
307  return params_.platform_data;
308 }
309 
311 inline const EtcPalThreadParams& Thread::params() const noexcept
312 {
313  return params_;
314 }
315 
320 {
321  return thread_ ? etcpal_thread_get_os_handle(thread_.get()) : ETCPAL_THREAD_OS_HANDLE_INVALID;
322 }
323 
331 inline Thread& Thread::SetPriority(unsigned int priority) noexcept
332 {
333  params_.priority = priority;
334  return *this;
335 }
336 
344 inline Thread& Thread::SetStackSize(unsigned int stack_size) noexcept
345 {
346  params_.stack_size = stack_size;
347  return *this;
348 }
349 
358 inline Thread& Thread::SetName(const char* name) noexcept
359 {
360  params_.thread_name = name;
361  return *this;
362 }
363 
372 inline Thread& Thread::SetName(const std::string& name) noexcept
373 {
374  params_.thread_name = name.c_str();
375  return *this;
376 }
377 
386 inline Thread& Thread::SetPlatformData(void* platform_data) noexcept
387 {
388  params_.platform_data = platform_data;
389  return *this;
390 }
391 
399 inline Thread& Thread::SetParams(const EtcPalThreadParams& params) noexcept
400 {
401  params_ = params;
402  return *this;
403 }
404 
427 template <class Function, class... Args>
428 Error Thread::Start(Function&& func, Args&&... args)
429 {
430  if (thread_)
431  return kEtcPalErrInvalid;
432 
433  thread_ = std::unique_ptr<etcpal_thread_t>(new etcpal_thread_t);
434  if (!thread_)
435  return kEtcPalErrNoMem;
436 
437  // TODO evaluate changing bind to lambda
438  auto new_f = std::unique_ptr<FunctionType>(new FunctionType(
439  std::bind(std::forward<Function>(func), std::forward<Args>(args)...))); // NOLINT(modernize-avoid-bind)
440  if (!new_f)
441  return kEtcPalErrNoMem;
442 
443  Error create_res = etcpal_thread_create(thread_.get(), &params_, CppThreadFn, new_f.get());
444  if (create_res)
445  new_f.release();
446  else
447  thread_.reset();
448  return create_res;
449 }
450 
462 inline Error Thread::Join(int timeout_ms) noexcept
463 {
464  if (!thread_)
465  return kEtcPalErrInvalid;
466 
467  Error join_res = etcpal_thread_timed_join(thread_.get(), timeout_ms);
468  if (join_res)
469  thread_.reset();
470  return join_res;
471 }
472 
482 inline Error Thread::Terminate() noexcept
483 {
484  if (!thread_)
485  return kEtcPalErrInvalid;
486 
487  Error terminate_res = etcpal_thread_terminate(thread_.get());
488  if (terminate_res)
489  thread_.reset();
490  return terminate_res;
491 }
492 
497 inline Error Thread::Sleep(unsigned int ms) noexcept
498 {
499  return etcpal_thread_sleep(ms);
500 }
501 
505 template <typename Rep, typename Period>
506 Error Thread::Sleep(const std::chrono::duration<Rep, Period>& sleep_duration) noexcept
507 {
508  // This implementation cannot sleep longer than UINT_MAX.
509  unsigned int sleep_ms_clamped = static_cast<unsigned int>(
510  std::min(std::chrono::milliseconds(sleep_duration).count(),
511  static_cast<std::chrono::milliseconds::rep>(std::numeric_limits<unsigned int>::max())));
512  return Sleep(sleep_ms_clamped);
513 }
514 
515 }; // namespace etcpal
516 
517 #endif // ETCPAL_CPP_THREAD_H_
A wrapper class for the EtcPal error type.
Definition: error.h:94
A thread class, modeled after std::thread.
Definition: thread.h:146
Error Terminate() noexcept
Forcefully kill the thread.
Definition: thread.h:482
unsigned int stack_size() const noexcept
Get the stack size of this thread in bytes (not valid on all platforms).
Definition: thread.h:293
etcpal_thread_os_handle_t os_handle() const noexcept
Get the native OS handle of this thread.
Definition: thread.h:319
unsigned int priority() const noexcept
Get the priority of this thread (not valid on all platforms).
Definition: thread.h:287
Thread & SetPlatformData(void *platform_data) noexcept
Set the platform-specific parameter data.
Definition: thread.h:386
bool joinable() const noexcept
Whether the thread object identifies an active thread of execution.
Definition: thread.h:281
const char * name() const noexcept
Get the name of this thread.
Definition: thread.h:299
Thread & SetStackSize(unsigned int stack_size) noexcept
Set the stack size of this thread in bytes.
Definition: thread.h:344
static Error Sleep(unsigned int ms) noexcept
Blocks the current thread for the specified number of milliseconds.
Definition: thread.h:497
Error Start(Function &&func, Args &&... args)
Associate this thread object with a new thread of execution.
Definition: thread.h:428
Thread & operator=(Thread &&other) noexcept
Move another thread into this thread.
Definition: thread.h:272
Thread(const Thread &other)=delete
Deleted copy constructor - threads are not copyable.
virtual ~Thread()
Destroy the thread object.
Definition: thread.h:251
Thread & SetParams(const EtcPalThreadParams &params) noexcept
Set this thread's parameters from an existing EtcPalThreadParams struct.
Definition: thread.h:399
const EtcPalThreadParams & params() const noexcept
Get a reference the parameters of this thread.
Definition: thread.h:311
Thread & operator=(const Thread &other)=delete
Deleted copy assignment operator - threads are not copyable.
void * platform_data() const noexcept
Get the platform-specific data of this thread.
Definition: thread.h:305
Thread & SetPriority(unsigned int priority) noexcept
Set the priority of this thread.
Definition: thread.h:331
Thread()=default
Create a new thread object which does not yet represent a thread.
Thread & SetName(const char *name) noexcept
Set the name of this thread.
Definition: thread.h:358
Error Join(int timeout_ms=ETCPAL_WAIT_FOREVER) noexcept
Wait for the thread to finish execution.
Definition: thread.h:462
Common definitions used by EtcPal C++ wrappers.
C++ wrapper and utilities for etcpal/error.h.
@ kEtcPalErrNoMem
A dynamic memory allocation failed, or there is no space left in a statically allocated array.
Definition: error.h:56
@ kEtcPalErrInvalid
An invalid argument was provided to an API function.
Definition: error.h:62
etcpal_error_t etcpal_thread_create(etcpal_thread_t *id, const EtcPalThreadParams *params, void(*thread_fn)(void *), void *thread_arg)
Create a new thread.
etcpal_error_t etcpal_thread_timed_join(etcpal_thread_t *id, int timeout_ms)
Wait for a thread to finish execution, giving up after a timeout.
PLATFORM_DEFINED etcpal_thread_t
The thread handle.
Definition: thread.dox:35
etcpal_error_t etcpal_thread_join(etcpal_thread_t *id)
Wait for a thread to finish execution.
#define ETCPAL_THREAD_OS_HANDLE_INVALID
An invalid value for the native OS thread handle type.
Definition: thread.dox:49
etcpal_error_t etcpal_thread_terminate(etcpal_thread_t *id)
Forcefully kill a thread.
etcpal_error_t etcpal_thread_sleep(unsigned int sleep_ms)
Provides a platform-neutral sleep.
#define ETCPAL_THREAD_SET_DEFAULT_PARAMS(threadparamsptr)
Set the platform-default values for the EtcPalThreadParams struct.
Definition: thread.h:163
#define ETCPAL_THREAD_PARAMS_INIT_VALUES
The set of default values for an EtcPalThreadParamsStructure.
Definition: thread.h:180
PLATFORM_DEFINED etcpal_thread_os_handle_t
The native OS handle type for threads on this platform.
Definition: thread.dox:42
etcpal_thread_os_handle_t etcpal_thread_get_os_handle(etcpal_thread_t *id)
Get the native OS handle of an EtcPal thread.
#define ETCPAL_WAIT_FOREVER
For etcpal_ functions that take a millisecond timeout, this means to wait indefinitely.
Definition: common.h:118
A set of parameters for an etcpal_thread.
Definition: thread.h:130
unsigned int priority
The priority of the thread.
Definition: thread.h:132
void * platform_data
Pointer to a platform-specific parameter structure.
Definition: thread.h:159
unsigned int stack_size
The stack size of the thread in bytes.
Definition: thread.h:141
const char * thread_name
A name for the thread, maximum length ETCPAL_THREAD_NAME_MAX_LENGTH.
Definition: thread.h:143