Fawkes API Fawkes Development Version
exception.cpp
1
2/***************************************************************************
3 * exception.cpp - basic exception
4 *
5 * Generated: Thu Feb 09 13:04:45 2006 (from FireVision)
6 * Copyright 2005-2006 Tim Niemueller [www.niemueller.de]
7 *
8 ****************************************************************************/
9
10/* This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version. A runtime exception applies to
14 * this software (see LICENSE.GPL_WRE file mentioned below for details).
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Library General Public License for more details.
20 *
21 * Read the full text in the LICENSE.GPL_WRE file in the doc directory.
22 */
23
24#include <core/exception.h>
25#include <core/threading/mutex.h>
26
27#ifndef _GNU_SOURCE
28# define _GNU_SOURCE
29#endif
30
31#include <cstdio>
32#include <cstdlib>
33#include <cstring>
34#ifdef HAVE_EXECINFO
35# include <execinfo.h>
36#endif
37
38namespace fawkes {
39
40/** @class Exception core/exception.h
41 * Base class for exceptions in Fawkes.
42 * Exceptions are a good way to handle errors. If you choose to use
43 * exceptions use derivates of this class so that there is a unified way of
44 * handling errors in Fawkes. Do <i>not</i> throw an arbitrary class such as
45 * a string or integer as this is hard to handle.
46 *
47 * For your exceptions in general you only need to override the constructor
48 * and call the Exception constructor with the appropriate message. In
49 * cases where more information is needed about the error add appropriate
50 * handlers.
51 *
52 * In most cases it is bad to just throw an Exception like this:
53 *
54 * @code
55 * if ( error_condition ) {
56 * throw Exception("Out of memory");
57 * }
58 * @endcode
59 *
60 * Rather you should explicitly subclass Exception appropriately. For the
61 * above example you could have something like this as exception class:
62 *
63 * @code
64 * class OutOfMemException : public Exception
65 * {
66 * public:
67 * OutOfMemoryException() : Exception("Out of memory") {}
68 * }
69 * @endcode
70 *
71 * And in your handling code you throw a OutOfMemoryException. This is
72 * especially useful if it is possible to throw several different exceptions.
73 * If the message was different you would have to parse the string for
74 * the exact error. This can be avoided if you just catch different
75 * exceptions. This is also useful if the Exception is not catched explicitly
76 * as this will printout the name of the exception class thrown just before
77 * exiting the program. And reading something like
78 * "terminate called after throwing an instance of 'OutOfMemoryException'"
79 * makes it a lot easier to spot the problem.
80 *
81 * Exceptions should be catched by reference like this:
82 * @code
83 * try {
84 * some_operation();
85 * } catch (OutOfMemoryException &e) {
86 * std::cout << e.c_str() << std::endl;
87 * error_handling();
88 * }
89 * @endcode
90 *
91 * Messages are stored as list. The first message however is called the
92 * primary message and it should contain as much information as available.
93 * This message is printed on the screen if the application crashes with an
94 * unhandled exception. So having meaningful content here means that the
95 * error can be traced more easily.
96 *
97 * You can utilize the list feature by adding appropriate information
98 * through appropriate try/catch statements. This way you can
99 * build information path ways that will help to debug your software. Use
100 * block like this to append information:
101 * @code
102 * try {
103 * potentially_failing();
104 * } catch {MyException &e) {
105 * e.append("info where exception happened");
106 * throw; // re-throw exception
107 * }
108 * @endcode
109 * This is especially useful if the exception may occur at several different
110 * places and it cannot be fixed where it happens.
111 *
112 *
113 * @see example_exception.cpp
114 * @ingroup FCL
115 * @ingroup Exceptions
116 *
117 * @author Tim Niemueller
118 */
119/** @var Exception::messages
120 * List of messages. Should not be NULL. Messages are append with append().
121 * Using a custom list to avoid including STL stuff in this core file.
122 * @see append()
123 */
124/** @var Exception::messages_iterator
125 * Iterator to iterate over messages
126 */
127/** @var Exception::messages_end
128 * Pointer that points to the very last message. Used for fast appending.
129 */
130/** @var Exception::messages_mutex
131 * Mutex to protect operations on messages list.
132 */
133/** @var Exception::_errno
134 * Error number, should be used if the error was caused by a method that supplies
135 * errno.
136 */
137
138/** Constructor.
139 * Constructs a new exception with the given message.
140 * @param format The format of the primary message. Supports the same
141 * arguments as append(). The message is copied and not just referenced.
142 * Thus the memory has to be freed if it is a dynamic string on the heap.
143 */
144Exception::Exception(const char *format, ...) noexcept
145{
146 messages_mutex = new Mutex();
147
148 _errno = 0;
149 type_id_ = "unknown";
150
151 messages = NULL;
152 messages_end = NULL;
153 messages_iterator = NULL;
154
155 if (format != NULL) {
156 va_list arg;
157 va_start(arg, format);
158 append_nolock_va(format, arg);
159 va_end(arg);
160 } else {
161 append_nolock("Unnkown Exception");
162 }
163}
164
165/** Constructor.
166 * Constructs a new exception with the given message and errno value. This
167 * is particularly handy when throwing the exception after a function failed
168 * that returns an error code in errno.
169 * @param errnoval error number
170 * @param format The format of the primary message. Supports the same
171 * arguments as append(). The message is copied and not just referenced.
172 * Thus the memory has to be freed if it is a dynamic string on the heap.
173 */
174Exception::Exception(int errnoval, const char *format, ...) noexcept
175{
176 messages_mutex = new Mutex();
177
178 _errno = errnoval;
179 type_id_ = "unknown";
180
181 messages = NULL;
182 messages_end = NULL;
183 messages_iterator = NULL;
184
185 if (format != NULL) {
186 va_list arg;
187 va_start(arg, format);
188 char *ext_format;
189 if (asprintf(&ext_format, "%s (errno: %i, %s)", format, _errno, strerror(_errno)) == -1) {
190 append_nolock_va(format, arg);
191 } else {
192 append_nolock_va(ext_format, arg);
193 free(ext_format);
194 }
195 va_end(arg);
196 } else {
197 append_nolock("Exception with errno=%i (%s)", _errno, strerror(_errno));
198 }
199}
200
201/** Copy constructor.
202 * The copy constructor is worth some extra discussion. If you do an exception
203 * by value (which you shouldn't in the first place since this will generate a
204 * copy, only do this if you can't avoid it for some reason. Not if you only
205 * THINK that you can't avoid it) the copy constructor is called. If your catch
206 * statements reads like
207 * @code
208 * try {
209 * ...
210 * } catch (Exception e) {
211 * ...
212 * }
213 * @endcode
214 * then a copy will be created for the catch block. You throw the exception with
215 * something like
216 * @code
217 * throw Exception("Boom");
218 * @endcode
219 * This will create an Exception which is valid in the block where you throw the
220 * exception. Now for the catch block a copy is created. Since the exception
221 * holds a pointer on the heap the implicit copy constructor would just copy
222 * the pointer, not the data. So both exceptions point to the same data (to the
223 * message for the base exception). If now both destructors for the exception
224 * are called they both try to free the very same memory. Of course the second
225 * destructor will cause a disaster. If you are lucky your glibc detectes the
226 * problem an kills the application. If you are not that fortunate you will
227 * cause very strange behaviour of your application.
228 *
229 * In general you should not have to worry about this. But if you choose to have
230 * own storage on the heap using either new, malloc or a method that returns
231 * memory on the heap (like strdup()) you have to write your own copy contructor
232 * and copy the memory area or take care that only one exception frees the memory.
233 * @param exc Exception to copy
234 */
235Exception::Exception(const Exception &exc) noexcept
236{
237 messages_mutex = new Mutex();
238
239 messages = NULL;
240 messages_end = NULL;
241 messages_iterator = NULL;
242
243 _errno = exc._errno;
244 type_id_ = exc.type_id_;
245 copy_messages(exc);
246}
247
248/** Constructor for subclasses.
249 * This constructor can be used in subclasses is some processing code is
250 * needed (like sprintf) to assign the message. At least assign the empty
251 * string to the message.
252 */
254{
255 messages_mutex = new Mutex();
256 _errno = 0;
257 type_id_ = "unknown";
258 messages = NULL;
259 messages_end = NULL;
260 messages_iterator = NULL;
261}
262
263/** Destructor. */
265{
266 message_list_t *msg_this;
268 while (messages_iterator) {
269 free(messages_iterator->msg);
270 msg_this = messages_iterator;
272 free(msg_this);
273 }
274 messages = NULL;
275 messages_end = NULL;
276 delete messages_mutex;
277}
278
279/** Set exception type ID.
280 * Set the type ID of this exception.
281 * @param id new type ID, note that this must be a static string which is
282 * guaranteed to exist for the whole lifetime of the exception.
283 * @see Exception::type_id()
284 */
285void
287{
288 type_id_ = id;
289}
290
291/** Get type ID.
292 * Exceptions can have a type ID. This can be used to avoid having to declare
293 * numerous specific exception sub-classes to different errors, if it is
294 * essential to be able to differentiate them in the exception handling code.
295 * The type ID is a free-form string. It should NOT contain any message, rather
296 * it should be a one-word internal identifier that is never leaked to the user
297 * of the software, i.e. it is not printed anywhere. Note that the ID must be
298 * a static string, which exists for the whole life time of the exception, is
299 * generally not in a dynamically allocated memory (this very exception could
300 * indicate memory shortage). This also makes it thread-safe.
301 * @return type ID
302 */
303const char *
305{
306 return type_id_;
307}
308
309/** Prepend messages to the message list.
310 * @param format format of the message to prepend, see printf(3) for details about formatting
311 * options.
312 */
313void
314Exception::prepend(const char *format, ...) noexcept
315{
316 // do not append empty messages
317 if (format == NULL)
318 return;
319
320 va_list arg;
321 va_start(arg, format);
323 prepend_nolock_va(format, arg);
325 va_end(arg);
326}
327
328/** Append messages to the message list.
329 * @param format format of the message to append, see printf(3) for details about formatting
330 * options.
331 */
332void
333Exception::append(const char *format, ...) noexcept
334{
335 // do not append empty messages
336 if (format == NULL)
337 return;
338
339 va_list arg;
340 va_start(arg, format);
342 append_nolock_va(format, arg);
344 va_end(arg);
345}
346
347/** Append messages to the message list.
348 * @param format format of the message to append, see printf(3) for details about formatting
349 * options.
350 * @param va va_list with arguments matching the format
351 */
352void
353Exception::append_va(const char *format, va_list va) noexcept
354{
355 // do not append empty messages
356 if (format == NULL)
357 return;
358
359 messages_mutex->lock();
360 append_nolock_va(format, va);
361 messages_mutex->unlock();
362}
363
364/** Append message that are from another Exception.
365 * @param e Exception to copy messages from
366 */
367void
368Exception::append(const Exception &e) noexcept
369{
370 copy_messages(e);
371}
372
373/** Append messages without lock.
374 * this can be used to append messages without locking the mutex if the mutex
375 * has been locked already to append many messages and keep their order intact
376 * and thus to prevent messages to be appended inbetween.
377 * Used for example in copy constructor.
378 * @param format The format of the primary message. Supports the same
379 * arguments as append(). The message is copied and not just referenced.
380 * Thus the memory has to be freed if it is a dynamic string on the heap.
381 */
382void
383Exception::append_nolock(const char *format, ...) noexcept
384{
385 va_list arg;
386 va_start(arg, format);
387
388 char *msg;
389 if (vasprintf(&msg, format, arg) == -1) {
390 msg = strdup(format);
391 }
392
393 va_end(arg);
394
395 if (messages == NULL) {
396 // This is our first message
397 messages = (message_list_t *)malloc(sizeof(message_list_t));
398 messages->next = NULL;
399 messages->msg = msg;
401 } else {
402 message_list_t *ml = (message_list_t *)malloc(sizeof(message_list_t));
403 ml->next = NULL;
404 ml->msg = msg;
405 messages_end->next = ml;
406 messages_end = ml;
407 }
408}
409
410/** Prepend messages without lock by formatted string.
411 * This can be used to append messages without locking the mutex if the mutex
412 * has been locked already to append many messages and keep their order intact
413 * and thus to prevent messages to be appended inbetween.
414 * Used for example in copy constructor.
415 * @param format format of the message to be appended
416 * @param ap argument va_list for format
417 */
418void
419Exception::prepend_nolock_va(const char *format, va_list ap) noexcept
420{
421 char *msg;
422 if (vasprintf(&msg, format, ap) == -1) {
423 msg = strdup(format);
424 }
425
426 if (messages == NULL) {
427 // This is our first message
428 messages = (message_list_t *)malloc(sizeof(message_list_t));
429 messages->next = NULL;
430 messages->msg = msg;
431 messages_end = messages;
432 } else {
433 message_list_t *ml = (message_list_t *)malloc(sizeof(message_list_t));
434 ml->next = messages;
435 ml->msg = msg;
436 messages = ml;
437 }
438}
439
440/** Append messages without lock by formatted string.
441 * this can be used to append messages without locking the mutex if the mutex
442 * has been locked already to append many messages and keep their order intact
443 * and thus to prevent messages to be appended inbetween.
444 * Used for example in copy constructor.
445 * @param format format of the message to be appended
446 * @param ap argument va_list for format
447 */
448void
449Exception::append_nolock_va(const char *format, va_list ap) noexcept
450{
451 char *msg;
452 if (vasprintf(&msg, format, ap) == -1) {
453 msg = strdup(format);
454 }
455
456 if (messages == NULL) {
457 // This is our first message
458 messages = (message_list_t *)malloc(sizeof(message_list_t));
459 messages->next = NULL;
460 messages->msg = msg;
461 messages_end = messages;
462 } else {
463 message_list_t *ml = (message_list_t *)malloc(sizeof(message_list_t));
464 ml->next = NULL;
465 ml->msg = msg;
466 messages_end->next = ml;
467 messages_end = ml;
468 }
469}
470
471/** Append message without copying.
472 * Can be used in subclasses to append messages that have been allocated
473 * on the heap. Use with extreme care. Do not add constant strings! This would
474 * cause your application to crash since the destructor will try to free all
475 * messages. The message list is not locked.
476 * @param msg Message to append.
477 */
478void
480{
481 if (messages == NULL) {
482 // This is our first message
483 messages = (message_list_t *)malloc(sizeof(message_list_t));
484 messages->next = NULL;
485 messages->msg = msg;
486 messages_end = messages;
487 } else {
488 message_list_t *ml = (message_list_t *)malloc(sizeof(message_list_t));
489 ml->next = NULL;
490 ml->msg = msg;
491 messages_end->next = ml;
492 messages_end = ml;
493 }
494}
495
496/** Assign an Exception.
497 * As this is one of the Big Three (see C++ FAQ at
498 * http://www.parashift.com/c++-faq-lite/coding-standards.html#faq-27.10) this
499 * is needed because we already need a copy constructor. Read about the
500 * copy constructor why this is the case.
501 * @see Exception(const Exception &exc)
502 * @param exc The exception with the values to assign to this exception.
503 * @return reference to this object. Allows assignment chaining.
504 */
505Exception &
506Exception::operator=(const Exception &exc) noexcept
507{
508 messages_mutex = new Mutex();
509 copy_messages(exc);
510
511 return *this;
512}
513
514/** Copy messages from given exception.
515 * Copies the messages from exc to this exception.
516 * @param exc Exception to copy messages from.
517 */
518void
520{
521 messages_mutex->lock();
522 exc.messages_mutex->lock();
523
524 // copy messages
525 messages_iterator = exc.messages;
526 while (messages_iterator) {
527 append_nolock(messages_iterator->msg);
528 messages_iterator = messages_iterator->next;
529 }
530
531 exc.messages_mutex->unlock();
532 messages_mutex->unlock();
533}
534
535/** This can be used to throw this exception.
536 * This can be used to throw this exception instance. This is a precaution if
537 * it is needed. See C++ FAQ 17.10.
538 */
539void
541{
542 throw *this;
543}
544
545/** Prints a backtrace. */
546void
548{
549#ifdef HAVE_EXECINFO
550 void * array[25];
551 int size = backtrace(array, 25);
552 char **symbols = backtrace_symbols(array, size);
553
554 printf("Backtrace:\n");
555 for (int i = 0; i < size; ++i) {
556 printf(" %s\n", symbols[i]);
557 }
558
559 free(symbols);
560#else
561 printf("Backtrace not available on current system\n");
562#endif
563}
564
565/** Generate backtrace string.
566 * @return freshly allocated string of backtrace. Free after you are done.
567 */
568char *
570{
571#ifdef HAVE_BACKTRACE
572 void * array[25];
573 int size = backtrace(array, 25);
574 char **symbols = backtrace_symbols(array, size);
575
576 size_t total_size = 1; //null termination
577 for (int i = 0; i < size; ++i) {
578 total_size += strlen(symbols[i]) + 1;
579 }
580 char *rv = (char *)calloc(1, total_size);
581 char *r = rv;
582 for (int i = 0; i < size; ++i) {
583 sprintf(r, "%s\n", symbols[i]);
584 r += strlen(symbols[i]);
585 }
586
587 free(symbols);
588#else
589 char *rv = strdup("Backtrace not available on current system\n");
590#endif
591
592 return rv;
593}
594
595/** Prints trace to stderr.
596 * This prints out a message trace of all messages appended to the exception
597 * in chronological order starting with the oldest (first message appended
598 * via constructor or append(). Output will be sent to stderr.
599 */
600void
602{
604 fprintf(stderr, "=================================================== BEGIN OF EXCEPTION =====\n");
605 if (messages == NULL) {
606 fprintf(stderr, "No messages recorded.\n");
607 } else {
609 while (messages_iterator) {
610 fprintf(stderr, "%s\n", messages_iterator->msg);
612 }
613 }
614 fprintf(stderr, "=================================================== END OF EXCEPTION =======\n");
616}
617
618/** Get errno.
619 * @return error number, may be 0 if not set
620 */
621int
623{
624 return _errno;
625}
626
627/** Get primary string.
628 * Messages are stored in a list. The first entry in this list is called primary
629 * message. This is why it is important to have a meaningful first message!
630 * @return Returns a constant char pointer with the message. The message is
631 * private to the exception and may not be modified or freed (hence const)
632 * If no message has been set "Unknown error" is returned. This method may be
633 * overidden by other exceptions.
634 * This method is also called by the runtime system if the exception was not
635 * caught and resulted in a program termination.
636 * @return string describing the general cause of the current error
637 */
638const char *
639Exception::what() const noexcept
640{
641#ifdef HAVE_EXECINFO
643#endif
644 if (messages != NULL) {
645 return messages->msg;
646 } else {
647 return "Unknown error";
648 }
649}
650
651/** Get primary string (does not implicitly print the back trace).
652 * Messages are stored in a list. The first entry in this list is called primary
653 * message. This is why it is important to have a meaningful first message!
654 * @return Returns a constant char pointer with the message. The message is
655 * private to the exception and may not be modified or freed (hence const)
656 * If no message has been set "Unknown error" is returned. This method may be
657 * overidden by other exceptions.
658 * This method is also called by the runtime system if the exception was not
659 * caught and resulted in a program termination.
660 * @return string describing the general cause of the current error
661 */
662const char *
664{
665 if (messages != NULL) {
666 return messages->msg;
667 } else {
668 return "Unknown error";
669 }
670}
671
672/** Get iterator for messages.
673 * @return iterator for messages
674 */
677{
679 return i;
680}
681
682/** @class Exception::iterator <core/exception.h>
683 * Message iterator for exceptions.
684 * This iterator allows for iterating over all messages carried by an Exception.
685 * @author Tim Niemueller
686 */
687
688/** Get end iterator for messages.
689 * @return end iterator for messages.
690 */
693{
695 return i;
696}
697
698/** Constructor.
699 * @param message_list list of messages, will be used unlocked so use
700 * with care.
701 */
702Exception::iterator::iterator(message_list_t *message_list)
703{
704 mlist = message_list;
705}
706
707/** Plain constructor.
708 * Creates a new invalid iterator (same as Exception::end()).
709 */
711{
712 this->mlist = NULL;
713}
714
715/** Copy constructor.
716 * @param i iterator to copy
717 */
719{
720 this->mlist = i.mlist;
721}
722
723/** Prefix ++ operator.
724 * @return reference to this iterator after advancing.
725 */
728{
729 if (mlist != NULL) {
730 mlist = mlist->next;
731 }
732 return *this;
733}
734
735/** Postfix ++ operator.
736 * @param inc used to denote postfix operator
737 * @return copy of iterator before advancing.
738 */
741{
742 iterator i(mlist);
743 if (mlist != NULL) {
744 mlist = mlist->next;
745 }
746 return i;
747}
748
749/** Check equality.
750 * @param i iterator to compare to
751 * @return true, if iterators point to the same message, false otherwise
752 */
753bool
755{
756 return (mlist == i.mlist);
757}
758
759/** Check inequality.
760 * @param i iterator to compare to
761 * @return true, if iterators point to different messages, false otherwise
762 */
763bool
765{
766 return (mlist != i.mlist);
767}
768
769/** Get current message.
770 * Get message at current position. Returns NULL for the invalid ieterator.
771 * @return message or NULL if iterator is invalid
772 */
773const char *
775{
776 if (mlist != NULL) {
777 return mlist->msg;
778 } else {
779 return NULL;
780 }
781}
782
783/** Assignment operator.
784 * @param i iterator to assign to this iterator.
785 * @return reference to this iterator.
786 */
789{
790 this->mlist = i.mlist;
791 return *this;
792}
793
794} // end namespace fawkes
Message iterator for exceptions.
Definition: exception.h:73
iterator()
Plain constructor.
Definition: exception.cpp:710
bool operator!=(const iterator &i) const
Check inequality.
Definition: exception.cpp:764
const char * operator*() const
Get current message.
Definition: exception.cpp:774
iterator & operator++()
Prefix ++ operator.
Definition: exception.cpp:727
bool operator==(const iterator &i) const
Check equality.
Definition: exception.cpp:754
iterator & operator=(const iterator &i)
Assignment operator.
Definition: exception.cpp:788
Base class for exceptions in Fawkes.
Definition: exception.h:36
void print_trace() noexcept
Prints trace to stderr.
Definition: exception.cpp:601
virtual const char * what() const noexcept
Get primary string.
Definition: exception.cpp:639
Mutex * messages_mutex
Mutex to protect operations on messages list.
Definition: exception.h:111
iterator end() noexcept
Get end iterator for messages.
Definition: exception.cpp:692
message_list_t * messages_end
Pointer that points to the very last message.
Definition: exception.h:110
message_list_t * messages
List of messages.
Definition: exception.h:108
virtual const char * what_no_backtrace() const noexcept
Get primary string (does not implicitly print the back trace).
Definition: exception.cpp:663
void prepend_nolock_va(const char *format, va_list va) noexcept
Prepend messages without lock by formatted string.
Definition: exception.cpp:419
void copy_messages(const Exception &exc) noexcept
Copy messages from given exception.
Definition: exception.cpp:519
virtual void raise()
This can be used to throw this exception.
Definition: exception.cpp:540
void prepend(const char *format,...) noexcept
Prepend messages to the message list.
Definition: exception.cpp:314
void set_type_id(const char *id)
Set exception type ID.
Definition: exception.cpp:286
void print_backtrace() const noexcept
Prints a backtrace.
Definition: exception.cpp:547
void append_va(const char *format, va_list va) noexcept
Append messages to the message list.
Definition: exception.cpp:353
void append(const char *format,...) noexcept
Append messages to the message list.
Definition: exception.cpp:333
int get_errno() noexcept
Get errno.
Definition: exception.cpp:622
void append_nolock(const char *format,...) noexcept
Append messages without lock.
Definition: exception.cpp:383
void append_nolock_va(const char *format, va_list va) noexcept
Append messages without lock by formatted string.
Definition: exception.cpp:449
Exception() noexcept
Constructor for subclasses.
Definition: exception.cpp:253
void append_nolock_nocopy(char *msg) noexcept
Append message without copying.
Definition: exception.cpp:479
message_list_t * messages_iterator
Iterator to iterate over messages.
Definition: exception.h:109
int _errno
Error number, should be used if the error was caused by a method that supplies errno.
Definition: exception.h:113
const char * type_id() const
Get type ID.
Definition: exception.cpp:304
Exception & operator=(const Exception &exc) noexcept
Assign an Exception.
Definition: exception.cpp:506
iterator begin() noexcept
Get iterator for messages.
Definition: exception.cpp:676
virtual ~Exception() noexcept
Destructor.
Definition: exception.cpp:264
char * generate_backtrace() const noexcept
Generate backtrace string.
Definition: exception.cpp:569
Mutex mutual exclusion lock.
Definition: mutex.h:33
void lock()
Lock this mutex.
Definition: mutex.cpp:87
void unlock()
Unlock the mutex.
Definition: mutex.cpp:131
Fawkes library namespace.
Internal exception message list.
Definition: exception.h:65
message_list_t * next
pointer to next element, NULL if last element
Definition: exception.h:66
char * msg
pointer to message, may not be NULL, will be freed in dtor
Definition: exception.h:67