1 /* exceptionhandler.cc
2 *
3 * Copyright 2002 The gtkmm Development Team
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #ifndef GLIBMM_CAN_USE_THREAD_LOCAL
20 #include <glibmm/threads.h>
21 #endif
22 #include <glibmmconfig.h>
23 #include <glibmm/error.h>
24 #include <glibmm/exceptionhandler.h>
25 #include <glib.h>
26 #include <exception>
27 #include <list>
28
29 namespace
30 {
31
32 using HandlerList = std::list<sigc::slot<void()>>;
33
34 // Each thread has its own list of exception handlers
35 // to avoid thread synchronization problems.
36 #ifdef GLIBMM_CAN_USE_THREAD_LOCAL
37 static thread_local HandlerList* thread_specific_handler_list = nullptr;
38 #else
39 static Glib::Threads::Private<HandlerList> thread_specific_handler_list;
40 #endif
41
42 static void
glibmm_exception_warning(const GError * error)43 glibmm_exception_warning(const GError* error)
44 {
45 g_assert(error != nullptr);
46
47 g_critical("\n"
48 "unhandled exception (type Glib::Error) in signal handler:\n"
49 "domain: %s\n"
50 "code : %d\n"
51 "what : %s\n",
52 g_quark_to_string(error->domain), error->code, (error->message) ? error->message : "(null)");
53 }
54
55 static void
glibmm_unexpected_exception()56 glibmm_unexpected_exception()
57 {
58 try
59 {
60 throw; // re-throw current exception
61 }
62 catch (const Glib::Error& error)
63 {
64 // Access the GError directly, to avoid possible exceptions from C++ code.
65 glibmm_exception_warning(error.gobj());
66
67 // For most failures that cause a Glib::Error exception, aborting the
68 // program seems too harsh. Instead, give control back to the main loop.
69 return;
70 }
71 catch (const std::exception& except)
72 {
73 g_error("\n"
74 "unhandled exception (type std::exception) in signal handler:\n"
75 "what: %s\n",
76 except.what());
77 }
78 catch (...)
79 {
80 g_error("\nunhandled exception (type unknown) in signal handler\n");
81 }
82 }
83
84 } // anonymous namespace
85
86 namespace Glib
87 {
88
89 sigc::connection
add_exception_handler(const sigc::slot<void> & slot)90 add_exception_handler(const sigc::slot<void>& slot)
91 {
92 #ifdef GLIBMM_CAN_USE_THREAD_LOCAL
93 HandlerList* handler_list = thread_specific_handler_list;
94 #else
95 HandlerList* handler_list = thread_specific_handler_list.get();
96 #endif
97
98 if (!handler_list)
99 {
100 handler_list = new HandlerList();
101 #ifdef GLIBMM_CAN_USE_THREAD_LOCAL
102 thread_specific_handler_list = handler_list;
103 #else
104 thread_specific_handler_list.set(handler_list);
105 #endif
106 }
107
108 handler_list->emplace_back(slot);
109 auto& added_slot = handler_list->back();
110 return sigc::connection(added_slot);
111 }
112
113 // internal
114 void
exception_handlers_invoke()115 exception_handlers_invoke() noexcept
116 {
117 // This function will be called from our GLib signal handler proxies
118 // if an exception has been caught. It's not possible to throw C++
119 // exceptions through C signal handlers. To handle this situation, the
120 // programmer can install slots to global Reusable Exception Handlers.
121 //
122 // A handler has to re-throw the current exception in a try block, and then
123 // catch the exceptions it knows about. Any unknown exceptions should just
124 // fall through, i.e. the handler must not do catch(...).
125 //
126 // We now invoke each of the installed slots until the exception has been
127 // handled. If there are no more handlers in the list and the exception
128 // is still unhandled, call glibmm_unexpected_exception().
129
130 #ifdef GLIBMM_CAN_USE_THREAD_LOCAL
131 if (HandlerList* const handler_list = thread_specific_handler_list)
132 #else
133 if(HandlerList *const handler_list = thread_specific_handler_list.get())
134 #endif
135 {
136 HandlerList::iterator pslot = handler_list->begin();
137
138 while (pslot != handler_list->end())
139 {
140 // Calling an empty slot would mean ignoring the exception,
141 // thus we have to check for dead slots explicitly.
142 if (pslot->empty())
143 {
144 pslot = handler_list->erase(pslot);
145 continue;
146 }
147
148 // Call the Reusable Exception Handler, which should re-throw
149 // the exception that's currently on the stack.
150 try
151 {
152 (*pslot)();
153 }
154 catch (...) // unhandled, try next slot
155 {
156 ++pslot;
157 continue;
158 }
159
160 // The exception has either been handled or ignored.
161 // Give control back to the GLib main loop.
162 return;
163 }
164 }
165
166 // Critical: The exception is still unhandled.
167 glibmm_unexpected_exception();
168 }
169
170 } // namespace Glib
171