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