libjmmcg  release_579_6_g8cffd
A C++ library containing an eclectic mix of useful, advanced components.
thread_params_traits.hpp
Go to the documentation of this file.
1 /******************************************************************************
2 ** Copyright © 2004 by J.M.McGuiness, coder@hussar.me.uk
3 **
4 ** This library is free software; you can redistribute it and/or
5 ** modify it under the terms of the GNU Lesser General Public
6 ** License as published by the Free Software Foundation; either
7 ** version 2.1 of the License, or (at your option) any later version.
8 **
9 ** This library is distributed in the hope that it will be useful,
10 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
11 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 ** Lesser General Public License for more details.
13 **
14 ** You should have received a copy of the GNU Lesser General Public
15 ** License along with this library; if not, write to the Free Software
16 ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18 
20 
21 #include <array>
22 #include <atomic>
23 #include <cerrno>
24 #include <limits>
25 
26 #include <pthread.h>
27 #include <unistd.h>
28 
29 namespace jmmcg { namespace LIBJMMCG_VER_NAMESPACE { namespace ppd {
30 
31  /// A constant, suitably defined to be the appropriate API for the platform on which the code is compiled, to allow one to be platform-agnostic.
32  static constexpr generic_traits::api_type platform_api=generic_traits::api_type::posix_pthreads;
33 
34  /// Wrap up the PThreads details regarding creating a thread, so that it is normalised for the rest of the library.
35  template<>
37  public:
39 
41  static constexpr stack_size_type max_stack_size=PTHREAD_STACK_MIN<<14; ///< The large stack size, about 66Mb.
42 
43  /**
44  Pthreads doesn't allow threads to be created suspended, only running.
45  */
48  };
49 
50  /**
51  Note that these states are in a specific order - the higher the number, the more severe any error.
52  */
53  enum states : std::uint32_t {
54  no_kernel_thread=0, ///< This is not a failure - the thread may not be started yet, or may have exited.
56  active=EBUSY,
58  // Error conditions now...
69 
72 
74  };
75  friend tostream &operator<<(tostream &os, states const s) {
76  switch (s) {
77  case no_kernel_thread:
78  os<<_T("no_kernel_thread");
79  break;
80  case suspended:
81  os<<_T("suspended");
82  break;
83  case active:
84  os<<_T("active");
85  break;
86  case cancelled:
87  os<<_T("cancelled");
88  break;
89  case get_exit_code_failure:
90  os<<_T("get_exit_code_failure");
91  break;
92  case deadlocked_with_another_thread:
93  os<<_T("deadlocked_with_another_thread");
94  break;
95  case invalid_thread:
96  os<<_T("invalid_thread");
97  break;
98  case thread_id_not_found:
99  os<<_T("thread_id_not_found");
100  break;
101  case failed_to_cancel:
102  os<<_T("failed_to_cancel");
103  break;
104  case null_this_pointer:
105  os<<_T("null_this_pointer");
106  break;
107  case jmmcg_exception:
108  os<<_T("jmmcg_exception");
109  break;
110  case stl_exception:
111  os<<_T("stl_exception");
112  break;
113  case unknown_exception:
114  os<<_T("unknown_exception");
115  break;
116  case terminated:
117  os<<_T("terminated");
118  break;
119  default:
120  os<<_T("unknown");
121  }
122  return os;
123  }
125  cancel_enable=PTHREAD_CANCEL_ENABLE,
126  cancel_disable=PTHREAD_CANCEL_DISABLE
127  };
128 
130  detached=PTHREAD_CREATE_DETACHED,
131  joinable=PTHREAD_CREATE_JOINABLE
132  };
133 
134  class attr {
135  public:
136  typedef pthread_attr_t element_type;
137 
138  attr(const detach_state_type detach_state, const stack_size_type stack_size, const int scope) noexcept(false) FORCE_INLINE {
139  int err=::pthread_attr_init(&attr_);
140  if (err!=0) {
142  ss<<"Unable to initialize the pthread attr object.\n"
143  <<info::function(__LINE__, __PRETTY_FUNCTION__, typeid(*this))<<'\n'
145  throw std::runtime_error(ss.str());
146  }
148  if (err!=0) {
150  ss<<"Unable to set the pthread detached state.\n"
151  <<info::function(__LINE__, __PRETTY_FUNCTION__, typeid(*this), info::function::argument("detach_state", detach_state))<<'\n'
153  throw std::runtime_error(ss.str());
154  }
155  if (stack_size==0) {
157  ss<<"Unable to set a pthread stack-size.\n"
158  <<info::function(__LINE__, __PRETTY_FUNCTION__, typeid(*this), info::function::argument("stack_size", stack_size))<<'\n'
160  throw std::runtime_error(ss.str());
161  }
163  if (err!=0) {
165  ss<<"Unable to set the pthread stack-size.\n"
166  <<info::function(__LINE__, __PRETTY_FUNCTION__, typeid(*this), info::function::argument("stack_size", stack_size))<<'\n'
168  throw std::runtime_error(ss.str());
169  }
171  if (err!=0) {
173  ss<<"Unable to set the pthread scope.\n"
174  <<info::function(__LINE__, __PRETTY_FUNCTION__, typeid(*this), info::function::argument("scope", scope))<<'\n'
176  throw std::runtime_error(ss.str());
177  }
178  err=::pthread_attr_setinheritsched(&attr_, PTHREAD_EXPLICIT_SCHED);
179  if (err!=0) {
181  ss<<"Unable to set the pthread scheduling.\n"
182  <<info::function(__LINE__, __PRETTY_FUNCTION__, typeid(*this), info::function::argument("scheduling", PTHREAD_EXPLICIT_SCHED))<<'\n'
184  throw std::runtime_error(ss.str());
185  }
186  }
187  attr(const attr &a) noexcept(true) FORCE_INLINE {
189  assert(err==0);
190  int val;
192  assert(err==0);
194  assert(err==0);
197  assert(err==0);
199  assert(err==0);
201  assert(err==0);
203  assert(err==0);
204  err=::pthread_attr_setinheritsched(&attr_, PTHREAD_EXPLICIT_SCHED);
205  assert(err==0);
206  }
207  explicit attr(const element_type &a) noexcept(true) FORCE_INLINE {
209  assert(err==0);
210  int val;
212  assert(err==0);
214  assert(err==0);
217  assert(err==0);
219  assert(err==0);
221  assert(err==0);
223  assert(err==0);
224  err=::pthread_attr_setinheritsched(&attr_, PTHREAD_EXPLICIT_SCHED);
225  assert(err==0);
226  }
227  ~attr(void) noexcept(true) FORCE_INLINE {
228  [[maybe_unused]] const int err=::pthread_attr_destroy(&attr_);
229  assert(err==0);
230  }
231 
232  attr &operator=(const attr &a) noexcept(true) FORCE_INLINE {
233  int val;
235  assert(err==0);
237  assert(err==0);
240  assert(err==0);
242  assert(err==0);
244  assert(err==0);
246  assert(err==0);
247  err=::pthread_attr_setinheritsched(&attr_, PTHREAD_EXPLICIT_SCHED);
248  assert(err==0);
249  return *this;
250  }
251 
252  attr &operator=(const creation_flags) noexcept(true) FORCE_INLINE {
253  return *this;
254  }
255 
256  operator const pthread_attr_t *() const noexcept(true) FORCE_INLINE {
257  return &attr_;
258  }
259 
260  operator pthread_attr_t *() noexcept(true) FORCE_INLINE {
261  return &attr_;
262  }
263 
264  stack_size_type stack_size() const noexcept(true) FORCE_INLINE {
266  [[maybe_unused]] const int err=::pthread_attr_getstacksize(&attr_, &sz);
267  assert(err==0);
268  return sz;
269  }
270 
271  detach_state_type detach_state() const noexcept(true) FORCE_INLINE {
273  [[maybe_unused]] const int err=::pthread_attr_getdetachstate(&attr_, reinterpret_cast<int *>(&st));
274  assert(err==0);
275  return st;
276  }
277 
278  friend inline tostream & operator<<(tostream &os, attr const &a) noexcept(false) {
280  os<<_T("Detached state=")<<(detach_st==detached ? _T("detached") : (detach_st==joinable ? _T("joinable") : _T("unknown")))
281  <<_T(", stack size=")<<a.stack_size();
282  int val;
284  assert(err==0);
285  os<<_T(", scope=")<<(val==PTHREAD_SCOPE_SYSTEM ? _T("system") : _T("unknown"));
287  assert(err==0);
288  os<<_T(", scheduling=")<<(val==PTHREAD_INHERIT_SCHED ? _T("inherit") : (val==PTHREAD_EXPLICIT_SCHED ? _T("explicit") : _T("unknown")));
289  return os;
290  }
291 
292  private:
294  };
295 
296  typedef pthread_t handle_type;
297  typedef pid_t pid_type;
298  typedef pthread_t tid_type;
300 
301  typedef unsigned long suspend_count;
302  typedef unsigned long suspend_period_ms;
303 
304  typedef void *security_type; // Not implemented on Posix.
306  public:
307  typedef cpu_set_t element_type; // Not implemented on Posix.
308 
309  __stdcall processor_mask_type() noexcept(true) FORCE_INLINE {
310  CPU_ZERO(&mask_);
311  }
312  explicit __stdcall processor_mask_type(std::size_t i) noexcept(true) FORCE_INLINE
313  : cpu_(i) {
314  CPU_ZERO(&mask_);
315  CPU_SET(cpu_, &mask_);
316  }
317 
318  element_type const &__fastcall mask() const noexcept(true) FORCE_INLINE {
319  return mask_;
320  }
321  element_type &__fastcall mask() noexcept(true) FORCE_INLINE {
322  return mask_;
323  }
324  std::size_t get_cpu() const noexcept(true) {
325  return cpu_;
326  }
327 
328  friend inline tostream &__fastcall FORCE_INLINE
329  operator<<(tostream &os, processor_mask_type const &m) noexcept(false) {
330  os<<CPU_COUNT(&m.mask_)<<", "<<m.cpu_;
331  return os;
332  }
333 
334  private:
336  const std::size_t cpu_{};
337  };
338 
339  typedef void * core_work_fn_ret_t;
340  typedef void * core_work_fn_arg_t;
341 
342 #pragma GCC diagnostic push
343 #pragma GCC diagnostic ignored "-Wattributes"
344 
345  typedef core_work_fn_ret_t (__cdecl core_work_fn_type)(core_work_fn_arg_t);
346 
347 #pragma GCC diagnostic pop
348 
351 
357  };
358 
359  core_work_fn_type * work_fn{};
362 
363  /**
364  Warning - this is not a valid parameter to check to identify if the managed thread has exited. On the other hand, the state parameter is.
365  */
367  std::atomic<states> state{no_kernel_thread}; ///< The Pthread API doesn't allow one to call pthread_join() on a thread, successfully, more than once, so we need to store the state here to ensure that if we've called it successfully, the state is correctly stored, and we can return the appropriate stored state upon subsequent calls.
368 
369  thread_params() noexcept(false) FORCE_INLINE
370  : initflag(joinable, PTHREAD_STACK_MIN, PTHREAD_SCOPE_SYSTEM) {
371  }
372  /**
373  \param sa A pointer to a function that will called by the OS-specific thread.
374  \param ss We set the stack size by default to be "big enough". Well, really I haven't a clue what this should be, as it is down to how the library is used. If you get bizarre SIGSEGV failures whilst constructing objects that should "just work", then you might need to set this value larger.
375 
376  \todo Have a better way of setting the stack size, rather than magically, here.
377  */
378  explicit thread_params(core_work_fn_type * const sa, const security_type=nullptr, const stack_size_type ss=max_stack_size) noexcept(false) FORCE_INLINE
379  : work_fn(sa), arglist(), initflag(joinable, ss, PTHREAD_SCOPE_SYSTEM) {
380  assert(work_fn);
381  }
382 
385  }
386 
387  stack_size_type stack_size() const noexcept(true) FORCE_INLINE {
388  return initflag.stack_size();
389  }
390  detach_state_type detach_state() const noexcept(true) FORCE_INLINE {
391  return initflag.detach_state();
392  }
393 
394  const tstring to_string() const noexcept(false) {
396  ss<<_T("API type=0x")<<std::hex<<api_type
397  <<_T(", max. stack size=0x")<<std::hex<<max_stack_size
398  <<_T(", work function ptr=0x")<<std::hex<<work_fn
399  <<_T(", argument list=0x")<<std::hex<<arglist
400  <<_T(", attributes: ")<<initflag
401  <<_T(", ID=0x")<<std::hex<<id
402  <<_T(", state=")<<std::dec<<state;
403  return ss.str();
404  }
405  };
406 
407 } } }