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