Task: process "uncaughtException" event

166 views
Skip to first unread message

r...@tinyclouds.org

unread,
Nov 3, 2009, 7:00:06 PM11/3/09
to nodejs
For fun and to attract people to hacking Node, I'm going to
occasionally post little tasks to the mailing list. If you do this,
please create a patch against HEAD and post it to this mailing list.
I'll wait a week for someone to do them before doing it myself :)

The task is to have the process object emit an "uncaughtException"
event when an exception makes its way all the way down to the event
loop.

This task is of medium difficulty, it will be mostly or all in C++.

Probably FatalException() in node.cc is the right place to hook in. It
might also be that ReallyEmit() in node_events.cc is better. I'm not
sure, you'll have to play around with it and make some tests.

Maybe, once this is implemented a second commit can reimplement
ReportException() from node.cc in javascript (in src/node.js).

Joshaven Potter

unread,
Nov 4, 2009, 1:04:15 PM11/4/09
to nod...@googlegroups.com
Great idea!  

--
Sincerely,
Joshaven Potter

"No man making a profession of faith ought to sin, nor one possessed of love to hate his brother. For He that said, “Thou shalt love the Lord thy God,”  said also, “and thy neighbor as thyself.”  Those that profess themselves to be Christ’s are known not only by what they say, but by what they practice. “For the tree is known by its fruit.”" -- Ignatius

Felix Geisendörfer

unread,
Nov 12, 2009, 5:29:15 AM11/12/09
to nodejs
I just started on this and have a few questions for anybody interested
in the topic.

The main idea here is to have a way to provide a last wall of defense
for uncaught exceptions. So we could write code like this:

process.addListener("uncaughtException", function(exception) {
// Log the error to a file, but don't let node crash in production
});

However, sometimes it is still useful to go for a hard crash, even if
there was an exception listener registered. For example:


process.addListener("uncaughtException", function(exception) {
if (totallyEvil(exception) {
process.fatalException(exception);
} else {
// Log the to file
}
});

The problem with the above approach is that it will almost certainly
not play well with multiple "uncaughtException" listeners. Thus an
alternative might be setting a passive attribute on the exception:

process.addListener("uncaughtException", function(exception) {
if (totallyEvil(exception) {
exception.fatal = true;
} else {
// Log the to file
}
});

That gives the other listeners a chance to evaluate the current
situations and possibly change the course of action. The opposite
approach is also worth considering:

process.addListener("uncaughtException", function(exception) {
if (totallyEvil(exception) {

} else {
exception.ignore = true;
// Log the to file
}
});

There may even be better solutions out there, so if you have any
references to prior art those would be highly appreciated.

-- Felix Geisendörfer aka the_undefined

Hagen

unread,
Nov 12, 2009, 5:39:15 AM11/12/09
to nod...@googlegroups.com
The usual route for this is to chain the listeners and expect them to return true if they manage to handle the exception.

you can then do

if (!exceptionListeners.some(function(handler) { return handler.handleException(e); }) {
  process.fatalException(e);
}

returning true in any handler will stop the chain. If non returns true, you got an unhandled exception. Given this approach, it would be nice to have one special handler, that is always called last. that way, you can design focused handlers, that look for something specific and return false, if they don't find themself suitable. The generic one (the last hope or the clean up handler) will not have the comfort of ignoring exceptions.

2009/11/12 Felix Geisendörfer <fe...@debuggable.com>



--
Dissertations are a successful walk through a minefield -- summarizing them is not. - Roy Fielding

Ryan Dahl

unread,
Nov 12, 2009, 6:27:36 AM11/12/09
to nod...@googlegroups.com
On Thu, Nov 12, 2009 at 11:39 AM, Hagen <six...@gmail.com> wrote:
> The usual route for this is to chain the listeners and expect them to return
> true if they manage to handle the exception.
> you can then do
> if (!exceptionListeners.some(function(handler) { return
> handler.handleException(e); }) {
>   process.fatalException(e);
> }
> returning true in any handler will stop the chain. If non returns true, you
> got an unhandled exception. Given this approach, it would be nice to have
> one special handler, that is always called last. that way, you can design
> focused handlers, that look for something specific and return false, if they
> don't find themself suitable. The generic one (the last hope or the clean up
> handler) will not have the comfort of ignoring exceptions.

Generally this is a good idea, except that we have lots of event
handlers where we don't want to have to return true or false - e.g.
the "connection" event from tcp.Server. The default (returning
undefined) should mean that all event listeners are run. It might be
nice to have some sort of way of canceling the rest of the listeners
(returning true? calling EventEmitter.cancelListeners()? What does the
DOM do?). But for this case I have a different suggestion: How about
"throw e" to have a hard crash?

Urban Hafner

unread,
Nov 12, 2009, 6:37:14 AM11/12/09
to nod...@googlegroups.com
Hagen wrote:

> returning true in any handler will stop the chain. If non returns true,
> you got an unhandled exception. Given this approach, it would be nice to
> have one special handler, that is always called last. that way, you can
> design focused handlers, that look for something specific and return
> false, if they don't find themself suitable. The generic one (the last
> hope or the clean up handler) will not have the comfort of ignoring
> exceptions.

Hm. Why true? I would have expected false. That's how it works in the
DOM doesn't it? At least for preventing the default action of an event.

And how about having two different event listeners? One for the "normal"
exceptions and one for all. This way you don't have to make the decision
based on the exception type in every handler (which seems like a design
flaw to me). I get the idea from the Ruby world. By default you only
catch exceptions that are subclass of RuntimeError (I think). You
explicitly have to catch all exceptions.

Urban

Felix Geisendörfer

unread,
Nov 12, 2009, 7:05:14 AM11/12/09
to nodejs
jQuery offers return false as a convenience layer, but internally it
triggers:

event.preventDefault();
event.stopPropagation();

Those are both DOM functions. preventDefault() will make sure whatever
the browser wanted to do naturally with an event (like follow the link
on click) gets canceled. stopPropagation() will keep the event from
bubbling. Bubbling, for those not familar with it, means that the same
event will be fired on all parent elements one by one if they have a
listener for the given event type. I do believe stopPropagation() will
also stop the listener chain on the current element, but I'm not 100%
certain on that.

IE uses a mixture of evaluating the return value (instead of
preventDefault) and setting a property called event.cancelBubble
(instead of stopPropagation).

So as far as prior art goes in the DOM world, we may use
exception.preventDefault() as a way to keep the exception from killing
the process and exception.stopPropagation() to keep the exception from
spreading to additional listeners.

What do you think?

-- Felix Geisendörfer aka the_undefined

r...@tinyclouds.org

unread,
Nov 13, 2009, 6:53:19 AM11/13/09
to nod...@googlegroups.com
2009/11/12 Felix Geisendörfer <fe...@debuggable.com>:

>
> jQuery offers return false as a convenience layer, but internally it
> triggers:
>
> event.preventDefault();
> event.stopPropagation();
>
> Those are both DOM functions. preventDefault() will make sure whatever
> the browser wanted to do naturally with an event (like follow the link
> on click) gets canceled. stopPropagation() will keep the event from
> bubbling. Bubbling, for those not familar with it, means that the same
> event will be fired on all parent elements one by one if they have a
> listener for the given event type. I do believe stopPropagation() will
> also stop the listener chain on the current element, but I'm not 100%
> certain on that.
>
> IE uses a mixture of evaluating the return value (instead of
> preventDefault) and setting a property called event.cancelBubble
> (instead of stopPropagation).
>
> So as far as prior art goes in the DOM world, we may use
> exception.preventDefault() as a way to keep the exception from killing
> the process and exception.stopPropagation() to keep the exception from
> spreading to additional listeners.
>
> What do you think?

I think that for now, the unhandledException event should live along
side the normal unhandledException handler in C++ (which is, to print
a stack trace and exit). If someone registers an unhandledException
listener, then the default action is not taken. Let's think about
interrupting chains of listeners later...

r...@tinyclouds.org

unread,
Nov 14, 2009, 5:45:37 PM11/14/09
to nodejs
On Wed, Nov 4, 2009 at 1:00 AM, <r...@tinyclouds.org> wrote:
>
> The task is to have the process object emit an "uncaughtException"
> event when an exception makes its way all the way down to the event
> loop.

Completed by Felix in 2b252ac.

Reply all
Reply to author
Forward
0 new messages