diff options
Diffstat (limited to 'glib/glibmm/exceptionhandler.cc')
-rw-r--r-- | glib/glibmm/exceptionhandler.cc | 193 |
1 files changed, 193 insertions, 0 deletions
diff --git a/glib/glibmm/exceptionhandler.cc b/glib/glibmm/exceptionhandler.cc new file mode 100644 index 00000000..52408b91 --- /dev/null +++ b/glib/glibmm/exceptionhandler.cc @@ -0,0 +1,193 @@ +// -*- c++ -*- +/* $Id$ */ + +/* exceptionhandler.cc + * + * Copyright 2002 The gtkmm Development Team + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <glib.h> +#include <exception> +#include <list> + +#include <glibmmconfig.h> +#include <glibmm/error.h> +#include <glibmm/exceptionhandler.h> +#include <glibmm/thread.h> + +GTKMM_USING_STD(exception) +GTKMM_USING_STD(list) + + +namespace +{ + +typedef std::list< SigC::Slot0<void> > HandlerList; + +// Each thread has its own list of exception handlers +// to avoid thread synchronization problems. +Glib::StaticPrivate<HandlerList> thread_specific_handler_list = GLIBMM_STATIC_PRIVATE_INIT; + + +class HandlerConnectionNode : public SigC::ConnectionNode +{ +public: + explicit HandlerConnectionNode(const SigC::Slot0<void>& slot) + : SigC::ConnectionNode(static_cast<SigC::SlotNode*>(slot.impl())) {} + + virtual void notify(bool from_child); +}; + +void HandlerConnectionNode::notify(bool from_child) +{ + if(HandlerList *const handler_list = thread_specific_handler_list.get()) + { + // We can't use remove_if() because it's possible to insert the same + // slot multiple times, although unlikely. std::list<>::remove_if() + // would remove only the first matching element. + + HandlerList::iterator pslot = handler_list->begin(); + + while(pslot != handler_list->end()) + { + if(pslot->impl() == slot().impl()) + pslot = handler_list->erase(pslot); + else + ++pslot; + } + } + + SigC::ConnectionNode::notify(from_child); +} + + +void glibmm_exception_warning(const GError* error) +{ + g_assert(error != 0); + + g_critical("\n" + "unhandled exception (type Glib::Error) in signal handler:\n" + "domain: %s\n" + "code : %d\n" + "what : %s\n", + g_quark_to_string(error->domain), error->code, + (error->message) ? error->message : "(null)"); +} + +void glibmm_unexpected_exception() +{ + try + { + throw; // re-throw current exception + } + catch(const Glib::Error& error) + { + // Access the GError directly, to avoid possible exceptions from C++ code. + glibmm_exception_warning(error.gobj()); + + // For most failures that cause a Glib::Error exception, aborting the + // program seems too harsh. Instead, give control back to the main loop. + return; + } + catch(const std::exception& except) + { + g_error("\n" + "unhandled exception (type std::exception) in signal handler:\n" + "what: %s\n", except.what()); + } + catch(...) + { + g_error("\nunhandled exception (type unknown) in signal handler\n"); + } +} + +} // anonymous namespace + + +namespace Glib +{ + +SigC::Connection add_exception_handler(const SigC::Slot0<void>& slot) +{ + HandlerList* handler_list = thread_specific_handler_list.get(); + + if(!handler_list) + { + handler_list = new HandlerList(); + thread_specific_handler_list.set(handler_list); + } + + const SigC::Connection connection (new HandlerConnectionNode(slot)); + handler_list->push_front(slot); + + return connection; +} + +// internal +void exception_handlers_invoke() throw() +{ + // This function will be called from our GLib signal handler proxies + // if an exception has been caught. It's not possible to throw C++ + // exceptions through C signal handlers. To handle this situation, the + // programmer can install slots to global Reusable Exception Handlers. + // + // A handler has to re-throw the current exception in a try block, and then + // catch the exceptions it knows about. Any unknown exceptions should just + // fall through, i.e. the handler must not do catch(...). + // + // We now invoke each of the installed slots until the exception has been + // handled. If there are no more handlers in the list and the exception + // is still unhandled, call glibmm_unexpected_exception(). + + if(HandlerList *const handler_list = thread_specific_handler_list.get()) + { + HandlerList::iterator pslot = handler_list->begin(); + + while(pslot != handler_list->end()) + { + // Calling an empty slot would mean ignoring the exception, + // thus we have to check for dead slots explicitly. + if(pslot->empty()) + { + pslot = handler_list->erase(pslot); + continue; + } + + // Call the Reusable Exception Handler, which should re-throw + // the exception that's currently on the stack. + try + { + (*pslot)(); + } + catch(...) // unhandled, try next slot + { + ++pslot; + continue; + } + + // The exception has either been handled or ignored. + // Give control back to the GLib main loop. + return; + } + } + + // Critical: The exception is still unhandled. + glibmm_unexpected_exception(); +} + +} // namespace Glib + |