Categories
Cplusplus Firefox

The dangers of -fno-exceptions

When Firefox is built with GCC, the -fno-exceptions option is used, which means that exception-handling is disabled.  I’ve been told that this is because the performance of code that uses exceptions is unacceptable.

Sounds simple, until you realize that libraries such as libstdc++.so are not built with this option.  This means, for example, that the vanilla operator new will throw an exception if it fails, because it’s in libstdc++.so, but Firefox code cannot catch the exception, because -fno-exceptions is specified.  (If you write a try-block, GCC will give you an error.)

This has important consequences:  if you compile your application with -fno-exceptions, you cannot use any standard library functions that might throw exceptionsSpiderMonkey’s C++ coding standard is succinct, perhaps overly so: “No exceptions, so std is hard to use.”

Another fine example of the “so you think you’ll be able to use a subset of C++, eh?” fallacy.  See bug 624878 for a specific manifestation of this problem.  I wonder if there are others case like that in Firefox.

17 replies on “The dangers of -fno-exceptions

One of these days we’re going to have to give up and turn exceptions back on. Sure would be nice if we fixed the performance problems (which are fixable — at least for GCC the only actual issue is a bunch of extra runtime relocations, that could be gotten rid of, AFAIK) before we were up against the wall.

Fallacy? Isn’t SM (after bug 624878 of course 🙂 a demonstration of us doing just that? Subsetting isn’t as easy as not subsetting, and its lame to lose most of the standard library if you cut out a feature, to be sure, but we seem to be able to ship a browser.

I’m confused. We build Chrome without exception support in exactly the same way. When you fail to alloc and operator new() throws, the program crashes. That’s generally what you want; in most code it’s hard to correctly deal with OOM, and crashing is normally best (especially if the OOM is caused by someone trying to exploit a security hole).

You don’t address this at all above; instead your post makes it sound as if it’s simply not possible to proceed with this combination at all. Why is crashing on OOM not acceptable for you?

In SpiderMonkey the policy is to handle OOMs as gracefully as possible. It’s used in other settings than Firefox, and we’ve been told that the ability to handle OOM without crashing is valuable. It does make life more difficult, though.

I should add that there are plans to use an “infallible malloc” in the non-SpiderMonkey parts of Firefox after 4.0. If an allocation fails it’ll try to free up memory (e.g. do a GC or deallocate optional data structures) and then abort if that doesn’t work. There’s also been talk of doing likewise in SpiderMonkey but it’s further off, AIUI.

Peter Kasting: I’ve since learnt there are some plans to introduce “infallible malloc” in SpiderMonkey, that would be used for small, fixed-sized allocations. Larger allocations would still be fallible; immediately aborting on any OOM sounds very brittle, I can imagine lots of cases where recovery is straightforward and worthwhile, eg. if a user tried to create a too-large array, or if the browser failed to create a large but optional cache.

Just going with a subset isn’t that hard if you do it everywhere which means you might need to do more things yourself.

@Peter: I presume that in Chrome, OOM is tolerated because it just takes down a tab or two, but not the whole browser? Which raises a good point: once electrolysis lands, can we get rid of our OOM propagation code?

Why can’t you overload operator new and delete for each class that you want to allocate? It doesn’t seem so hard, after all it’s exactly the same code each time so you can even hide it behind a macro.

As for the other exceptions, Gecko apparently has to ensure that its infallible new is included before certain STL headers, although on my machine I don’t actually see where those headers need to use it.

“Graceful OOM handling” is next to impossible on systems that have virtual memory on slow hard disk drives: by the time you trigger the OOM killer, your machine has been swapping like hell and the user is pissed already, if he hasn’t just force-rebooted yet.

So I think that ‘ignore OOM, just crash’ is a very good policy even without Chrome’s multi process architecture.

Are there other kinds of exceptions that the STL throws that are more useful trying to handle ‘gracefully’ to?

Sid: I’m not sure if I understand your question, but SpiderMonkey doesn’t use mozalloc.

Neil: overloading new/delete for each class works falls short if some of your uses aren’t within a class. This is the case for SpiderMonkey. And globally overriding new/delete is apparently difficult, see https://bugzilla.mozilla.org/show_bug.cgi?id=624878#c5

have you considered the nothrow overload of operator new? that would allow you to handle alloc failures in your code calling new/new[], right?
#include
thing *t = new (std::nothrow) thing(a, b);

for new-calling libstdc++ code you call, you can consider std::set_new_handler, which installs a handler to be invoked in lieu of throwing or returning 0, after which the alloc is retried (in case your handler frees up some space)

i am curious about the performance problems mozilla was seeing with -fexceptions. was it only at load time (as mentioned in comment 1)?

br: SpiderMonkey is used in places other than Firefox, and it can be configured so that the embedding environment provides a custom allocator. So using the std::nothrow variant isn’t suitable.

Benoit: that’s one category of OOMs; another category is giant allocations caused by content. Again, with process-per-tab, this doesn’t sound so bad: so a site gets to crash its own tab, big deal. However, its not so excusable when multiple tabs are put in the same process, either to save resources or because the windows are reachable.

Sid: that’s right, the fallible_t version in mozalloc.h just returns NULL on failure.

Comments are closed.