libjmmcg  release_579_6_g8cffd
A C++ library containing an eclectic mix of useful, advanced components.
thread_api_traits_impl.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 
19 #include "../core/deleter.hpp"
20 
21 #include <cxxabi.h>
22 #include <execinfo.h>
23 #include <signal.h>
24 
25 namespace jmmcg { namespace LIBJMMCG_VER_NAMESPACE { namespace ppd {
26 
27 // Implementation details..... Don't look below here!!! ;)
28 
29 // "api_threading_traits" implementation...
30 
33  return ::getpid();
34  }
35 
38  return ::pthread_self();
39  }
40 
43  return ::pthread_self();
44  }
45 
49  ::cuserid(uname.data());
50  return uname;
51  }
52 
53  template<> inline void __fastcall
55  // Find the beginning and the end of the useful part of the trace.
56  char const *begin=mangled_name;
57  char const *end=::strrchr(mangled_name, '+');
58  // If they were found, we'll go ahead and demangle.
59  if (begin && end) {
60  int demangleStatus=-1;
62  // The demangled name is now in our trace string.
65  // If demangling fails, copy the original string in....
66  if (ret!=demangled_name.data() || demangleStatus!=0) {
68  }
69  }
70  }
73  char const *begin=mangled_type.name();
75  int demangleStatus=-1;
79  // If demangling fails, copy the original string in....
80  if (ret!=demangled_name.data() || demangleStatus!=0) {
82  }
83  return demangled_name;
84  }
85  /**
86  Implementation blagged from:
87  <a href="http://stackoverflow.com/questions/1274036/generating-c-backtraces-in-os-x-10-5-7"/>
88  */
91 
92  try {
93  void *buffer[max_num_fn_ptrs];
96  if (fn_strings.ptr==nullptr) {
98  __FILE__ "[" LIBJMMCG_ENQUOTE(__LINE__) "]: Call to backtrace_symbols() failed."
99  };
100  return demangled_names;
101  } else {
103  demangled_names[0]='\0';
105  for (int j=0; j<num_fn_ptrs_ret; ++j) {
107  a_demangled_name[0]='\0';
111  *demangled_ptr='\n';
112  ++demangled_ptr;
113  }
114  *demangled_ptr='\0';
115  return demangled_names;
116  }
117  } catch (...) {
119  __FILE__ "[" LIBJMMCG_ENQUOTE(__LINE__) "]: Unknown exception in demangling names."
120  };
121  return demangled_names;
122  }
123  }
124 
125  inline char const * SIG_to_string(int signal_number) noexcept(true) {
126  static char const * const signal_names[]={
127  "SIGILL /* Illegal instruction (ANSI). */",
128  "SIGABRT /* Abort (ANSI). */",
129  "SIGBUS /* BUS error (4.2 BSD). */",
130  "SIGFPE /* Floating-point exception (ANSI). */",
131  "SIGSEGV /* Segmentation violation (ANSI). */",
132  "SIGSTKFLT /* Stack fault. */",
133  "SIGSYS /* Bad system call. */",
134  "unknown"
135  };
136  switch (signal_number) {
137  case SIGILL:
138  return signal_names[0];
139  case SIGABRT:
140  return signal_names[1];
141  case SIGBUS:
142  return signal_names[2];
143  case SIGFPE:
144  return signal_names[3];
145  case SIGSEGV:
146  return signal_names[4];
147  case SIGSTKFLT:
148  return signal_names[5];
149  case SIGSYS:
150  return signal_names[6];
151  default:
152  return signal_names[7];
153  };
154  }
155 
156  inline std::ostream &
157  operator<<(std::ostream &os, siginfo_t const &si) noexcept(true) {
158  os
159  <<"Signal number=0x"<<std::hex<<si.si_signo
161  <<", signal code=0x"<<std::hex<<si.si_code;
162  return os;
163  }
164 
165  static inline struct sigaction old_sa;
166 
167  extern "C" inline void abort_handler(int signal_number, siginfo_t *info, void */*context*/) noexcept(true) {
168  try {
169  [[maybe_unused]] int ret=::sigaction(SIGSEGV, &old_sa, nullptr);
170  assert(ret==0);
171 // TODO output some useful info to the user: ucontext_t const *cxt=reinterpret_cast<ucontext_t const *>(context);
172  std::cerr<<"Signal trapped=0x"<<std::hex<<signal_number<<_T(", ")<<SIG_to_string(signal_number)<<std::endl
173  <<*info<<std::endl
174  <<api_threading_traits<generic_traits::api_type::posix_pthreads, sequential_mode>::gen_backtrace().begin()<<std::endl;
175  } catch (...) {
176  std::cerr<<"Error with backtracing..."<<std::endl;
177  }
178  }
179 
180  template<> inline void __fastcall
182  struct sigaction new_sa;
184  new_sa.sa_handler=nullptr;
185  new_sa.sa_sigaction=abort_handler;
186  new_sa.sa_flags=0;
187  new_sa.sa_flags&=SA_SIGINFO;
188  [[maybe_unused]] int ret=::sigaction(SIGABRT, &new_sa, nullptr);
189  assert(ret==0);
190  ret=::sigaction(SIGILL, &new_sa, nullptr);
191  assert(ret==0);
192  ret=::sigaction(SIGTRAP, &new_sa, nullptr);
193  assert(ret==0);
194  ret=::sigaction(SIGABRT, &new_sa, nullptr);
195  assert(ret==0);
196  ret=::sigaction(SIGBUS, &new_sa, nullptr);
197  assert(ret==0);
198  ret=::sigaction(SIGFPE, &new_sa, nullptr);
199  assert(ret==0);
200  ret=::sigaction(SIGSEGV, &new_sa, &old_sa);
201  assert(ret==0);
202  ret=::sigaction(SIGSTKFLT, &new_sa, nullptr);
203  assert(ret==0);
204  ret=::sigaction(SIGSYS, &new_sa, nullptr);
205  assert(ret==0);
206  }
207 
208  inline void dump_bt_to_cerr() {
210  std::cerr<<bt.begin()<<std::endl;
211  }
212 
213  template<> inline void __fastcall
216  }
217 
218  template<> inline void __fastcall
220  }
221 
222  template<> inline void __fastcall
224  [[maybe_unused]] auto const ret=::raise(sig);
225  assert(ret==0);
226  }
227 
228  /// Run the work only once if it returns at all, unlike the multi-threaded variant.
229  /**
230  If the worker_fn() returns true then it will run once, then exit, otherwise it will depend upon the return value for pre_exit(). If this always returns false, then the system will enter an infinite loop. By default pre_exit() for sequential mode returns false.
231 
232  \see worker_fn(), process()
233  */
234  template<> inline bool __fastcall
240  }
241  return true;
242  }
243 
246  return static_cast<api_params_type::suspend_count>(0);
247  }
248 
249  /// This does not run the work at all, unlike the multi-threaded variant.
253  return static_cast<api_params_type::suspend_count>(0);
254  }
255 
259  }
260 
261  template<> inline void __fastcall
263  }
264 
265  template<> inline void __fastcall
267  }
268 
269  template<> inline void __fastcall
271  }
272 
273  template<> inline void __fastcall
275  }
276 
277  template<> inline void __fastcall
279  }
280 
284  }
285 
288  return api_params_type::normal;
289  }
290 
291  template<> inline void __fastcall
293  }
294 
295  template<> inline void __fastcall
297  }
298 
302  }
303 
304  template<> inline void __fastcall
307  }
308 
309  template<> inline bool
311  assert(cp.work_fn);
312  assert(cp.arglist);
313  const int ret=::pthread_create(&cp.id, static_cast<const pthread_attr_t *>(cp.initflag), cp.work_fn, cp.arglist);
314  assert(ret==0 && cp.id!=0);
315  return !static_cast<bool>(ret);
316  }
317 
320 // TODO ::pthread_kill(params.id, SIGTHSTOP);
321  return static_cast<api_params_type::suspend_count>(0);
322  }
323 
326 // TODO ::pthread_kill(params.id, SIGTHCONT);
327  return static_cast<api_params_type::suspend_count>(0);
328  }
329 
334  const int ret=::pthread_tryjoin_np(thread.id, reinterpret_cast<void **>(&exit_code));
336  switch (ret) {
337  case 0:
338  thread.id=0;
339  if (!thread.state) {
341  } else if (thread.state==static_cast<api_params_type::states>(reinterpret_cast<long>(PTHREAD_CANCELED))) {
343  }
344  break;
345  case EDEADLK:
347  break;
348  case EINVAL:
350  break;
351  case ESRCH:
353  break;
354  case EBUSY:
356  break;
357  default:
359  }
360  return thread.state;
361  } else {
362  return thread.state;
363  }
364  }
365 
366  template<> inline bool __fastcall
370  }
371 
372  template<> inline void
374  ::pthread_exit(static_cast<void *>(&exit_code));
375  }
376 
377  template<> inline void __fastcall
379  int old_state;
381  if (pth_err) {
382  info::function fun(__LINE__, __PRETTY_FUNCTION__, typeid(void), info::function::argument(_T("api_params_type::thread_cancel_state state"), tostring(state)));
383  fun.add_arg(info::function::argument(_T("Return code"), tostring(pth_err)));
384  throw lock_traits::exception_type(_T("Could not set the cancel state of the current thread."), std::move(fun), JMMCG_REVISION_HDR(_T(LIBJMMCG_VERSION_NUMBER)));
385  }
386  }
387 
388  template<> inline void
390  assert(thread);
391  try {
393  } catch (...) {
394  // We don't care about an error as we cannot do anything about it.
395  }
396  const int pth_err=::pthread_cancel(thread);
397  if (pth_err!=EXIT_SUCCESS && pth_err!=ESRCH) {
398  assert(pth_err);
399  info::function fun(__LINE__, __PRETTY_FUNCTION__, typeid(void), info::function::argument(_T("api_params_type::handle_type thread"), tostring(thread)));
400  fun.add_arg(info::function::argument(_T("Return code"), tostring(pth_err)));
401  throw lock_traits::exception_type(_T("Could not send a cancel request to the thread."), std::move(fun), JMMCG_REVISION_HDR(_T(LIBJMMCG_VERSION_NUMBER)));
402  }
403  }
404 
405  template<> inline void
407  if (thread) {
408  void *exit_code_p=reinterpret_cast<void *>(api_params_type::unknown);
409  const int pth_err=::pthread_join(thread, &exit_code_p);
410  const api_params_type::states exit_code=static_cast<api_params_type::states>(reinterpret_cast<long>(exit_code_p));
411  if (pth_err) {
412  info::function fun(__LINE__, __PRETTY_FUNCTION__, typeid(void), info::function::argument(_T("api_params_type::handle_type thread"), tostring(thread)));
413  fun.add_arg(info::function::argument(_T("Return code"), tostring(pth_err)));
414  fun.add_arg(info::function::argument(_T("Return code from thread"), tostring(exit_code)));
415  throw lock_traits::exception_type(_T("Could not join with the requested thread."), std::move(fun), JMMCG_REVISION_HDR(_T(LIBJMMCG_VERSION_NUMBER)));
416  }
417  }
418  }
419 
420  template<> inline void
422  // Note that this affects the entire process & all threads within it.
423  sched_param sp;
426  if (schedule_policy!=-1) {
427  const int checked_priority=(std::min(std::max(static_cast<int>(priority), static_cast<int>(api_params_type::priority_type::idle)), static_cast<int>(api_params_type::priority_type::time_critical)));
429  if (!err) {
430  return;
431  } else {
432  throw lock_traits::exception_type(_T("Failed to set the new priority."), info::function(__LINE__, __PRETTY_FUNCTION__, typeid(void), info::function::argument(_T("api_params_type::priority_type priority"), tostring(priority))), JMMCG_REVISION_HDR(_T(LIBJMMCG_VERSION_NUMBER)));
433  }
434  } else {
435  throw lock_traits::exception_type(_T("Failed to set the type of scheduler to use."), info::function(__LINE__, __PRETTY_FUNCTION__, typeid(void), info::function::argument(_T("SCHED_RR"), tostring(SCHED_RR))), JMMCG_REVISION_HDR(_T(LIBJMMCG_VERSION_NUMBER)));
436  }
437  }
438 
442  const int err=::sched_getparam(0, &param);
443  if (err!=-1) {
444  return static_cast<api_params_type::priority_type>(param.sched_priority);
445  } else {
446  info::function fun(__LINE__, __PRETTY_FUNCTION__, typeid(void), info::function::argument(_T("typename api_params_type::handle_type thread"), tostring(thread)));
447  fun.add_arg(info::function::argument(_T("Return code"), tostring(err)));
448  throw lock_traits::exception_type(_T("Could not get the kernel priority of the specified thread."), std::move(fun), JMMCG_REVISION_HDR(_T(LIBJMMCG_VERSION_NUMBER)));
449  }
450  }
451 
454  return ::getpid();
455  }
456 
459  return ::pthread_self();
460  }
461 
464  return ::pthread_self();
465  }
466 
470  }
471 
472  template<> inline void __fastcall
475  if (pth_err) {
476  info::function fun(__LINE__, __PRETTY_FUNCTION__, typeid(void), info::function::argument(_T("typename api_params_type::handle_type const thread_id"), tostring(thread_id)));
477  fun.add_arg(info::function::argument(_T("api_params_type::processor_mask_type const mask"), tostring(mask)));
478  fun.add_arg(info::function::argument(_T("Return code"), tostring(pth_err)));
479  throw lock_traits::exception_type(_T("Could not set the kernel affinity of the specified thread."), std::move(fun), JMMCG_REVISION_HDR(_T(LIBJMMCG_VERSION_NUMBER)));
480  }
481  }
482 
487  if (pth_err) {
488  info::function fun(__LINE__, __PRETTY_FUNCTION__, typeid(void), info::function::argument(_T("typename api_params_type::handle_type const thread_id"), tostring(thread_id)));
489  fun.add_arg(info::function::argument(_T("Return code"), tostring(pth_err)));
490  throw lock_traits::exception_type(_T("Could not get the kernel affinity of the specified thread."), std::move(fun), JMMCG_REVISION_HDR(_T(LIBJMMCG_VERSION_NUMBER)));
491  }
492  return mask;
493  }
494 
495  template<> inline void
497  if (period) {
499  const timespec period_ns={
500  static_cast<long>(period/1000),
501  static_cast<long>((period%1000)*1000000)
502  };
504  const int err=::nanosleep(&period_ns, &requested);
505  if (err) {
506  info::function fun(__LINE__, __PRETTY_FUNCTION__, typeid(void), info::function::argument(_T("api_params_type::suspend_period_ms period"), tostring(period)));
507  fun.add_arg(info::function::argument(_T("Return code"), tostring(err)));
508  throw lock_traits::exception_type(_T("Could not suspend for the requested period."), std::move(fun), JMMCG_REVISION_HDR(_T(LIBJMMCG_VERSION_NUMBER)));
509  }
510  } else {
511  const int err=::sched_yield();
512  if (err) {
513  info::function fun(__LINE__, __PRETTY_FUNCTION__, typeid(void), info::function::argument(_T("api_params_type::suspend_period_ms period"), tostring(period)));
514  fun.add_arg(info::function::argument(_T("Return code"), tostring(err)));
515  throw lock_traits::exception_type(_T("Period of zero implies a yield, which failed."), std::move(fun), JMMCG_REVISION_HDR(_T(LIBJMMCG_VERSION_NUMBER)));
516  }
517  }
518  }
519 
522  if (thread.id) {
525  try {
527  } catch (...) {
528  // Ignore any errors....
529  }
530  // TODO should use "pthread_timedjoin_np()", but it uses the very irritating Epoch for the abstimeout.
531  const int ret=::pthread_join(thread.id, reinterpret_cast<void **>(&exit_code));
533  switch (ret) {
534  case 0:
535  thread.id=0;
536  if (!thread.state) {
538  }
539  break;
540  case EDEADLK:
542  break;
543  case EINVAL:
544  thread.id=0;
546  break;
547  case ESRCH:
548  thread.id=0;
550  break;
551  case EBUSY:
553  break;
554  default:
556  }
557  return thread.state;
558  } else {
559  return thread.state;
560  }
561  }
562 
563  template<> inline void __fastcall
566  }
570  }
571 
575  }
576 
577  template<> inline void __fastcall
580  }
581 
582  template<> inline void __fastcall
585  }
586 
587  template<> inline void __fastcall
589  assert(name);
590  assert(::strlen(name)<16);
591  const int pth_err=::pthread_setname_np(thread, name);
592  if (pth_err) {
593  info::function fun(__LINE__, __PRETTY_FUNCTION__, typeid(void), info::function::argument(_T("char const *name"), name));
594  fun.add_arg(info::function::argument(_T("Return code"), tostring(pth_err)));
595  throw lock_traits::exception_type(_T("Could not set the name of the current thread. (Possibly the name exceeds 16 chars incl. the null-terminator.)"), std::move(fun), JMMCG_REVISION_HDR(_T(LIBJMMCG_VERSION_NUMBER)));
596  }
597  }
598 
599 } } }