EtcPal  0.4.1
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 {
124 
142 class Thread
143 {
144 public:
146  Thread() = default;
147  template <class Function,
148  class... Args,
149  typename std::enable_if<!std::is_arithmetic<Function>::value, bool>::type = true>
150  Thread(Function&& func, Args&&... args);
151  Thread(unsigned int priority, unsigned int stack_size, const char* name, void* platform_data = nullptr);
152  virtual ~Thread();
153 
154  Thread(Thread&& other) noexcept;
155  Thread& operator=(Thread&& other) noexcept;
156 
158  Thread(const Thread& other) = delete;
160  Thread& operator=(const Thread& other) = delete;
161 
162  bool joinable() const noexcept;
163 
166  unsigned int priority() const noexcept;
167  unsigned int stack_size() const noexcept;
168  const char* name() const noexcept;
169  void* platform_data() const noexcept;
170  const EtcPalThreadParams& params() const noexcept;
171  etcpal_thread_os_handle_t os_handle() const noexcept;
173 
176  Thread& SetPriority(unsigned int priority) noexcept;
177  Thread& SetStackSize(unsigned int stack_size) noexcept;
178  Thread& SetName(const char* name) noexcept;
179  Thread& SetName(const std::string& name) noexcept;
180  Thread& SetPlatformData(void* platform_data) noexcept;
181  Thread& SetParams(const EtcPalThreadParams& params) noexcept;
183 
184  template <class Function, class... Args>
185  Error Start(Function&& func, Args&&... args);
186  Error Join(int timeout_ms = ETCPAL_WAIT_FOREVER) noexcept;
187  Error Terminate() noexcept;
188 
189  static void Sleep(unsigned int ms) noexcept;
190  template <typename Rep, typename Period>
191  static void Sleep(const std::chrono::duration<Rep, Period>& sleep_duration) noexcept;
192 
194  using FunctionType = std::function<void()>;
196 
197 private:
198  std::unique_ptr<etcpal_thread_t> thread_;
200 };
201 
203 
204 extern "C" inline void CppThreadFn(void* arg)
205 {
206  std::unique_ptr<Thread::FunctionType> p_func(static_cast<Thread::FunctionType*>(arg));
207  // Tear the roof off the sucker
208  (*p_func)();
209 }
210 
212 
221 template <class Function, class... Args, typename std::enable_if<!std::is_arithmetic<Function>::value, bool>::type>
222 Thread::Thread(Function&& func, Args&&... args)
223 {
225  auto result = Start(std::forward<Function>(func), std::forward<Args>(args)...);
226  if (!result)
227  ETCPAL_THROW(std::runtime_error("Error while starting EtcPal thread: " + result.ToString()));
228 }
229 
239 inline Thread::Thread(unsigned int priority, unsigned int stack_size, const char* name, void* platform_data)
240  : params_{priority, stack_size, name, platform_data}
241 {
242 }
243 
249 {
250  if (thread_)
251  etcpal_thread_join(thread_.get());
252 }
253 
259 inline Thread::Thread(Thread&& other) noexcept
260 {
261  *this = std::move(other);
262 }
263 
269 inline Thread& Thread::operator=(Thread&& other) noexcept
270 {
271  thread_ = std::move(other.thread_);
272  params_ = other.params_;
273  ETCPAL_THREAD_SET_DEFAULT_PARAMS(&other.params_);
274  return *this;
275 }
276 
278 inline bool Thread::joinable() const noexcept
279 {
280  return (bool)thread_;
281 }
282 
284 inline unsigned int Thread::priority() const noexcept
285 {
286  return params_.priority;
287 }
288 
290 inline unsigned int Thread::stack_size() const noexcept
291 {
292  return params_.stack_size;
293 }
294 
296 inline const char* Thread::name() const noexcept
297 {
298  return params_.thread_name;
299 }
300 
302 inline void* Thread::platform_data() const noexcept
303 {
304  return params_.platform_data;
305 }
306 
308 inline const EtcPalThreadParams& Thread::params() const noexcept
309 {
310  return params_;
311 }
312 
317 {
318  return thread_ ? etcpal_thread_get_os_handle(thread_.get()) : ETCPAL_THREAD_OS_HANDLE_INVALID;
319 }
320 
328 inline Thread& Thread::SetPriority(unsigned int priority) noexcept
329 {
330  params_.priority = priority;
331  return *this;
332 }
333 
341 inline Thread& Thread::SetStackSize(unsigned int stack_size) noexcept
342 {
343  params_.stack_size = stack_size;
344  return *this;
345 }
346 
355 inline Thread& Thread::SetName(const char* name) noexcept
356 {
357  params_.thread_name = name;
358  return *this;
359 }
360 
369 inline Thread& Thread::SetName(const std::string& name) noexcept
370 {
371  params_.thread_name = name.c_str();
372  return *this;
373 }
374 
383 inline Thread& Thread::SetPlatformData(void* platform_data) noexcept
384 {
385  params_.platform_data = platform_data;
386  return *this;
387 }
388 
396 inline Thread& Thread::SetParams(const EtcPalThreadParams& params) noexcept
397 {
398  params_ = params;
399  return *this;
400 }
401 
424 template <class Function, class... Args>
425 Error Thread::Start(Function&& func, Args&&... args)
426 {
427  if (thread_)
428  return kEtcPalErrInvalid;
429 
430  thread_ = std::unique_ptr<etcpal_thread_t>(new etcpal_thread_t);
431  if (!thread_)
432  return kEtcPalErrNoMem;
433 
434  // TODO evaluate changing bind to lambda
435  auto new_f = std::unique_ptr<FunctionType>(new FunctionType(
436  std::bind(std::forward<Function>(func), std::forward<Args>(args)...))); // NOLINT(modernize-avoid-bind)
437  if (!new_f)
438  return kEtcPalErrNoMem;
439 
440  Error create_res = etcpal_thread_create(thread_.get(), &params_, CppThreadFn, new_f.get());
441  if (create_res)
442  new_f.release();
443  else
444  thread_.reset();
445  return create_res;
446 }
447 
459 inline Error Thread::Join(int timeout_ms) noexcept
460 {
461  if (!thread_)
462  return kEtcPalErrInvalid;
463 
464  Error join_res = etcpal_thread_timed_join(thread_.get(), timeout_ms);
465  if (join_res)
466  thread_.reset();
467  return join_res;
468 }
469 
479 inline Error Thread::Terminate() noexcept
480 {
481  if (!thread_)
482  return kEtcPalErrInvalid;
483 
484  Error terminate_res = etcpal_thread_terminate(thread_.get());
485  if (terminate_res)
486  thread_.reset();
487  return terminate_res;
488 }
489 
491 inline void Thread::Sleep(unsigned int ms) noexcept
492 {
494 }
495 
499 template <typename Rep, typename Period>
500 void Thread::Sleep(const std::chrono::duration<Rep, Period>& sleep_duration) noexcept
501 {
502  // This implementation cannot sleep longer than UINT_MAX.
503  unsigned int sleep_ms_clamped = static_cast<unsigned int>(
504  std::min(std::chrono::milliseconds(sleep_duration).count(),
505  static_cast<std::chrono::milliseconds::rep>(std::numeric_limits<unsigned int>::max())));
506  Sleep(sleep_ms_clamped);
507 }
508 
509 }; // namespace etcpal
510 
511 #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:143
Error Terminate() noexcept
Forcefully kill the thread.
Definition: thread.h:479
unsigned int stack_size() const noexcept
Get the stack size of this thread in bytes (not valid on all platforms).
Definition: thread.h:290
etcpal_thread_os_handle_t os_handle() const noexcept
Get the native OS handle of this thread.
Definition: thread.h:316
unsigned int priority() const noexcept
Get the priority of this thread (not valid on all platforms).
Definition: thread.h:284
Thread & SetPlatformData(void *platform_data) noexcept
Set the platform-specific parameter data.
Definition: thread.h:383
bool joinable() const noexcept
Whether the thread object identifies an active thread of execution.
Definition: thread.h:278
const char * name() const noexcept
Get the name of this thread.
Definition: thread.h:296
Thread & SetStackSize(unsigned int stack_size) noexcept
Set the stack size of this thread in bytes.
Definition: thread.h:341
Error Start(Function &&func, Args &&... args)
Associate this thread object with a new thread of execution.
Definition: thread.h:425
Thread & operator=(Thread &&other) noexcept
Move another thread into this thread.
Definition: thread.h:269
Thread(const Thread &other)=delete
Deleted copy constructor - threads are not copyable.
virtual ~Thread()
Destroy the thread object.
Definition: thread.h:248
Thread & SetParams(const EtcPalThreadParams &params) noexcept
Set this thread's parameters from an existing EtcPalThreadParams struct.
Definition: thread.h:396
const EtcPalThreadParams & params() const noexcept
Get a reference the parameters of this thread.
Definition: thread.h:308
static void Sleep(unsigned int ms) noexcept
Blocks the current thread for the specified number of milliseconds.
Definition: thread.h:491
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:302
Thread & SetPriority(unsigned int priority) noexcept
Set the priority of this thread.
Definition: thread.h:328
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:355
Error Join(int timeout_ms=ETCPAL_WAIT_FOREVER) noexcept
Wait for the thread to finish execution.
Definition: thread.h:459
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.
#define ETCPAL_THREAD_SET_DEFAULT_PARAMS(threadparamsptr)
Set the platform-default values for the EtcPalThreadParams struct.
Definition: thread.h:160
#define ETCPAL_THREAD_PARAMS_INIT_VALUES
The set of default values for an EtcPalThreadParamsStructure.
Definition: thread.h:177
PLATFORM_DEFINED etcpal_thread_os_handle_t
The native OS handle type for threads on this platform.
Definition: thread.dox:42
void etcpal_thread_sleep(unsigned int sleep_ms)
Provides a platform-neutral sleep.
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:111
A set of parameters for an etcpal_thread.
Definition: thread.h:127
unsigned int priority
The priority of the thread.
Definition: thread.h:129
void * platform_data
Pointer to a platform-specific parameter structure.
Definition: thread.h:156
unsigned int stack_size
The stack size of the thread in bytes.
Definition: thread.h:138
const char * thread_name
A name for the thread, maximum length ETCPAL_THREAD_NAME_MAX_LENGTH.
Definition: thread.h:140