Tuesday, March 6, 2007

Exceptional Delegates

I committed some rework to the GObject signal system in Gtk# today. The signal system relies heavily on native to managed callback delegates for all the unmanaged widget signals we bind. Paolo and Lluis pointed out a gap in our previous implementation, where we neglected to take into account the interaction of this code with managed exception handling.

When an exception is thrown in a managed event handler, we need to ensure that the stack is not unrolled across native-to-managed boundaries in the call stack. When that happens, you tend to get unpredictable behavior with stack corruption and segfaults. Today's fixes add try/catch blocks to the signal and virtual method delegate marshallers to ensure the stack only unrolls to the boundary.

The catch blocks raise static events on a new GLib.ExceptionManager class. If you want to "catch" events happening in managed signal handlers, you can attach to the UnhandledException event. The UnhandledExceptionArgs passed to your delegate expose an IsTerminating property (from its System.UnhandledExceptionEventArgs base class) to indicate if the exception is recoverable and an additional ExitApplication property to request program termination if desired after the event propogation is complete.

Lluis, always the bearer of interesting insight, pointed out we need similar code for Idle and Timeout handlers, which made me realize that all the Callback parameters like foreach method callbacks and DestroyNotify handlers are also vulnerable to the problem.

I'll be fixing this bug for a while, but feel free to try out the new signal stuff on trunk in the meantime and report any warts you run across.