Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

C++ Exception Handling

78 views
Skip to first unread message

Rodrigo Bonifacio

unread,
Dec 18, 2015, 8:28:27 AM12/18/15
to dev-tech-js-en...@lists.mozilla.org
Dear all, (apologies if you receive multiple copies)

We are investigating the use of C++ exception handling constructs in well
known open-source C++ systems, and have published some results in the last
International Conference on Source Code Analysis and Manipulation [1]

We are expanding this research to a broader community, so I kindly invite
you to answer our new version of the survey at:

https://www.surveymonkey.com/r/SXCB7WJ

Sincerely,

Rodrigo Bonifácio (on behalf of the authors of this research effort).

[1] "The use of C++ exception handling constructs: A comprehensive study"
http://dx.doi.org/10.1109/SCAM.2015.7335398
http://rbonifacio.net/papers/scam2015/rbonifacio-scam2015.pdf

Jason Orendorff

unread,
Dec 18, 2015, 10:55:29 PM12/18/15
to Rodrigo Bonifacio, JS Internals list
I took the survey.

I would be happy to use C++ exceptions in SpiderMonkey. The primary reason
we don't is historical. Converting a large, existing codebase to use
exceptions is a huge investment of time. (It also involves touching the
build system.)

-j





On Fri, Dec 18, 2015 at 7:28 AM, Rodrigo Bonifacio <rbonif...@gmail.com>
wrote:
> _______________________________________________
> dev-tech-js-engine-internals mailing list
> dev-tech-js-en...@lists.mozilla.org
> https://lists.mozilla.org/listinfo/dev-tech-js-engine-internals
>

Luke Wagner

unread,
Dec 21, 2015, 11:11:50 AM12/21/15
to Jason Orendorff, Rodrigo Bonifacio, JS Internals list
I think there was also a performance reason: IIUC, the non-zero-cost
exception handling strategy is baked into the Win32 ABI
(https://www.microsoft.com/msj/0197/exception/exception.aspx) and thus
enabling EH has a runtime cost for frames containing auto objects with
destructors (which in modern SM is very common). I vaguely remember
seeing a platform email thread about using EH a while back (>6 years)
that measured the then-overhead in Gecko to be ~10%; I can't find the
thread now. I talked to an engineer who worked on EH optimizations in
MSVC and he said that there have been some 32-bit EH optimizations
more recently but there is still runtime cost to having auto objects
with destructors, so probably this is worth measuring again. Assuming
we're able to move a large percentage of FF users to Win64 builds over
the next year, then it seems like we'd care about this even less.

FWIW, though, I'd love to be able to use EH in SM.

Ehsan Akhgari

unread,
Dec 21, 2015, 11:26:50 AM12/21/15
to lu...@mozilla.com, Jason Orendorff, Rodrigo Bonifacio, JS Internals list
There is also the issue of writing exception safe code, which is
extremely difficult to do, especially when you have piles of code assume
there are no exceptions. Just turning exceptions on will cause
incorrect behavior (where a scope does the wrong thing when terminated
by an exception, such as update one field of an object but not the other
at the same time) to crashes (where you would trigger an exception while
another is being handled, causing the runtime to terminate the process.)

AFAIK this is why many large C++ projects don't use EH.

Jason Orendorff

unread,
Jan 4, 2016, 12:40:58 PM1/4/16
to Ehsan Akhgari, Luke Wagner, Rodrigo Bonifacio, JS Internals list
On Mon, Dec 21, 2015 at 10:26 AM, Ehsan Akhgari <ehsan....@gmail.com>
wrote:

> There is also the issue of writing exception safe code, which is extremely
> difficult to do, especially when you have piles of code assume there are no
> exceptions.


Yup. This is what I was referring to in my original post ("huge investment
of time").

I know from experience that it is NOT "extremely difficult" to write
exception-safe code. It's really pretty simple: use RAII, use destructors
for cleanup.

But converting existing code is hard. (And maintaining a boundary between
EH and non-EH parts of a program sounds like a huge headache.)

-j

Luke Wagner

unread,
Jan 4, 2016, 1:08:49 PM1/4/16
to Jason Orendorff, Ehsan Akhgari, Rodrigo Bonifacio, JS Internals list
> I know from experience that it is NOT "extremely difficult" to write
> exception-safe code. It's really pretty simple: use RAII, use destructors
> for cleanup.

Agreed. Or, as Bjarne used to say "no naked news" :) It feels like
we've been gradually migrating to the codebase to this style *anyway*
since it's more maintainable even w/o EH.

> But converting existing code is hard. (And maintaining a boundary between EH
> and non-EH parts of a program sounds like a huge headache.)

Also agreed :( However, based on our relative success using static
analysis to switch from conservative to precise GC, I was thinking
that it would be possible to apply similar techniques to incrementally
switch to EH. Still, as you said, a relatively large investment so
not one I'd advocate for making atm.

Jason Orendorff

unread,
Jan 4, 2016, 1:24:37 PM1/4/16
to Luke Wagner, Ehsan Akhgari, Rodrigo Bonifacio, JS Internals list
On Mon, Jan 4, 2016 at 12:08 PM, Luke Wagner <lwa...@mozilla.com> wrote:

> > I know from experience that it is NOT "extremely difficult" to write
> > exception-safe code. It's really pretty simple: use RAII, use destructors
> > for cleanup.
>
> Agreed. Or, as Bjarne used to say "no naked news" :) It feels like
> we've been gradually migrating to the codebase to this style *anyway*
> since it's more maintainable even w/o EH.
>

For sure.

I inadvertently understated my point. The other half is, experience also
clearly shows that we screw up explicit error-return-checking a lot.

-j

Mike Shaver

unread,
Jan 4, 2016, 2:55:12 PM1/4/16
to Luke Wagner, Jason Orendorff, Ehsan Akhgari, Rodrigo Bonifacio, JS Internals list
Has there been some innovation in EH since Cargill's spine-chilling
exploration of making a simple container strongly exception safe? I recall
the consensus being that it was quite difficult to actually be strongly
exception safe, but it sounds like that's no longer the case now.

Mike

On Mon, Jan 4, 2016 at 10:08 AM, Luke Wagner <lwa...@mozilla.com> wrote:

> > I know from experience that it is NOT "extremely difficult" to write
> > exception-safe code. It's really pretty simple: use RAII, use destructors
> > for cleanup.
>
> Agreed. Or, as Bjarne used to say "no naked news" :) It feels like
> we've been gradually migrating to the codebase to this style *anyway*
> since it's more maintainable even w/o EH.
>
> > But converting existing code is hard. (And maintaining a boundary
> between EH
> > and non-EH parts of a program sounds like a huge headache.)
>
> Also agreed :( However, based on our relative success using static
> analysis to switch from conservative to precise GC, I was thinking
> that it would be possible to apply similar techniques to incrementally
> switch to EH. Still, as you said, a relatively large investment so
> not one I'd advocate for making atm.

Ehsan Akhgari

unread,
Jan 4, 2016, 3:53:00 PM1/4/16
to Mike Shaver, Luke Wagner, Jason Orendorff, Rodrigo Bonifacio, JS Internals list
On 2016-01-04 2:55 PM, Mike Shaver wrote:
> Has there been some innovation in EH since Cargill's spine-chilling
> exploration of making a simple container strongly exception safe? I
> recall the consensus being that it was quite difficult to actually be
> strongly exception safe, but it sounds like that's no longer the case now.

See Dave Abrahams' write-up on the matter which is sort of the answer to
Cargill's findings (at least one cited by proponents of C++ exception
handling: <http://www.boost.org/community/exception_safety.html>).
(<http://www.gotw.ca/gotw/082.htm> is also a related and great read.)

Without meaning to open up a discussion about C++ exception safety
(since it seems like nobody is actually proposing that we should use it,
at least right now!), writing exception safe code is not just a matter
of using RAII and dtors to clean up. Please see
<http://herbsutter.com/gotw/_102/> for example. Also, see things like
exceptions escaping from destructors and so on. And most importantly,
logic errors caused by the state of the program being left in an
inconsistent form after an exception is risen and handled properly as
far as the language is concerned.

To summarize, it's _possible_ to write exception safe code is all of the
engineers working on your code know the recipe for doing so, but such
code needs to be designed for exception safety (since there are clearly
APIs that can never be made exception safe, for example without changing
the APIs), and for converting existing code, you also need to have a
reason why the time and money spent doing so is worth it...

</soapbox>

Ehsan

Mike Shaver

unread,
Jan 4, 2016, 3:54:08 PM1/4/16
to Ehsan Akhgari, Luke Wagner, Jason Orendorff, Rodrigo Bonifacio, JS Internals list
On Mon, Jan 4, 2016 at 12:52 PM, Ehsan Akhgari <ehsan....@gmail.com>
wrote:

> (since there are clearly APIs that can never be made exception safe, for
> example without changing the APIs)


If you're making an API throw that didn't before, you're already changing
it, so IMO go nuts.

Mike

Luke Wagner

unread,
Jan 4, 2016, 4:48:49 PM1/4/16
to Ehsan Akhgari, Jason Orendorff, JS Internals list, Rodrigo Bonifacio, Mike Shaver
> right now!), writing exception safe code is not just a matter of using RAII
> and dtors to clean up. Please see <http://herbsutter.com/gotw/_102/> for
> example.

Yes; noone is suggesting there's an automatic conversion; it'd be a
lot of careful work. This particular classic EH hazard should be
particularly amenable to a local static analysis.

> Also, see things like exceptions escaping from destructors and so on.

It seems like a convention involving noexcept and supported with extra
static checks (analogous to the "can this function GC?" static
analyses we have now) would be effective here too.

> And most importantly, logic errors caused by the state of the program
> being left in an inconsistent form after an exception is risen and handled
> properly as far as the language is concerned.

In general, the exception exits would be at the same points as the
'return false'. Of course there will be weird cases like:

bool ok = foo();
doSomething();
if (!ok)
return false;

but these are precisely the maintenance hazards we've been refactoring
away from. If we were to undertake a migration to EH, an intermediate
step could be to have the static analysis require that all error
return values return immediately on failure before doing anything
else. I'm sure there are other interesting corner cases and I'd be
curious to hear about any examples that weren't things weren't already
maintenance hazards today and amenable to static checks.

Ehsan Akhgari

unread,
Jan 4, 2016, 9:03:59 PM1/4/16
to lu...@mozilla.com, Jason Orendorff, JS Internals list, Rodrigo Bonifacio, Mike Shaver
On 2016-01-04 4:48 PM, Luke Wagner wrote:
>> right now!), writing exception safe code is not just a matter of using RAII
>> and dtors to clean up. Please see <http://herbsutter.com/gotw/_102/> for
>> example.
>
> Yes; noone is suggesting there's an automatic conversion; it'd be a
> lot of careful work.

Agreed. :-)

> This particular classic EH hazard should be
> particularly amenable to a local static analysis.

Yes. So I have actually been thinking about this a bit. It's true that
for this example you can perform a simple static check, but other cases
can't really be usefully checked statically. Here are two examples:

1. In order to detect whether an exception can escape a destructor, you
need to disallow calling non-noexcept functions, which is problematic
since many destructors for RAII types for example need to call into
non-noexcept library functions. In this case you need checks that will
incur some runtime overhead in order to enforce exception safety.

2. In order to detect whether a resource leaks if something throws, you
need to know what conditions are considered as a "leak". It's trivial
to know that for object allocations, but not for logical resources (for
example an OS handle that you received from a C API, which is an integer
as far as the compiler is concerned.

Some of these can be solved at runtime if the overhead is acceptable.
But in general creating tools for all of them is time consuming, and I'm
not surprised if a few cases are undetectable.

>> Also, see things like exceptions escaping from destructors and so on.
>
> It seems like a convention involving noexcept and supported with extra
> static checks (analogous to the "can this function GC?" static
> analyses we have now) would be effective here too.

For this kind of analysis, we need two things. One is the callgraph,
similar to the current checker. The other is knowing whether a function
actually throws, or whether it's only declared as non-noexcept because
for example it calls a non-noexcept API that actually never throws.

But yeah, there may be approaches that I’m missing.

Cheers,
Ehsan

Luke Wagner

unread,
Jan 5, 2016, 11:45:02 AM1/5/16
to Ehsan Akhgari, Jason Orendorff, JS Internals list, Rodrigo Bonifacio, Mike Shaver
> 2. In order to detect whether a resource leaks if something throws, you need
> to know what conditions are considered as a "leak". It's trivial to know
> that for object allocations, but not for logical resources (for example an
> OS handle that you received from a C API, which is an integer as far as the
> compiler is concerned.
[...]
> For this kind of analysis, we need two things. One is the callgraph,
> similar to the current checker. The other is knowing whether a function
> actually throws, or whether it's only declared as non-noexcept because for
> example it calls a non-noexcept API that actually never throws.

I'm not super-familiar with the details, bhackett or the GC team can
fill in more, but I think this is all stuff we're doing now with the
GC rooting analysis. IIUC, it's based on a pretty awesome C++ static
analysis framework bhackett wrote in grad school called sixgill that's
able to reason about dataflow, the global control flow graph and all
the complicated C++ features. The basis of reasoning is annotations
on various C++ types that indicate they should not be held live across
calls that could GC and a bunch of other annotations for tweaking the
analysis when we don't want to pay a dynamic cost. Of course, as you
pointed out, we'd need new annotations on all the resource-y things;
that's the "lots of careful work" that we all agree would be
necessary. But even before we turned on EH, this could help tighten
the code by instituting a regular, idiomatic scheme for managing
resource-y things in a way that avoids accidental leaks when someone
refactors the code and adds a new return path.

Steve Fink

unread,
Jan 5, 2016, 2:20:30 PM1/5/16
to dev-tech-js-en...@lists.mozilla.org, Luke Wagner, Brian Hackett
The GC hazard static analysis uses sixgill, but really only the part
that produces a simplified version of the control flow graph. Brian
implemented some dataflow and general analysis infrastructure in
sixgill, but we don't make use of any of that; we just process the CFG
with custom JS code. I don't know much about the "built-in" constraint
solver that sixgill uses; Brian would have to talk about that. Also,
sixgill definitely doesn't handle all C++ features. I've had to teach it
about a couple that turned out to be needed for the GC analysis, but
there are a number of others that it simply punts on. (Code using those
features is just discarded.) Implementing more features is certainly
possible, but requires understanding the (undocumented?
underdocumented?) GCC internal data structures. Admittedly, many fancy
C++ features are handled earlier, so sixgill doesn't need to do anything
for them. What we have now is certainly adequate for the GC hazard
analysis, but it's something of an open question as to whether it would
be adequate for exception safety.

The bigger issue is that, at least the way we're using it now, sixgill
doesn't give you anything for dataflow other than a simplified CFG.
You'd have to implement that. For intraprocedural stuff, that's probably
not too horribly difficult if there are relatively straightforward ways
of recognizing (or annotating) values of interest, but it's a lot of
busywork.

Also, the call graph is necessarily conservative. Any function pointer
is assumed to be able to call anything. Can function pointers be labeled
noexcept? Also, while it does (conservatively) handle virtual function
calls, currently it accepts the possibility of binary extensions so
unless annotated otherwise, it assumes that you might override any
virtual method with one that invokes arbitrary code. But maybe we're
comfortable disallowing that now.

Oh, and the "annotations on various C++ types" currently take the form
of "a hardcoded list of types stored in a JS script", but that's fixed
in a version that I still haven't managed to deploy because b2g is
giving me trouble for unrelated reasons. Now you can do something like
struct MyTaggedPointer { ... } JS_HAZ_GC_POINTER; which boils down to
__attribute__((tag("GC Pointer"))). And similar for functions, which
introduces the possibility of having __attribute__((tag("throws
exceptions, yo"))) and then using the same code to do callgraph
traversals for the ones currently tagged __attribute__((tag("GC
Call"))). In other words: it is easy to add additional analyses, as long
as they want to do pretty much the same thing as the existing GC
analysis. I'm sure each analysis will need its own ugly collection of
special cases and things. As I said, though, tagging things as "leakable
resource" is not at all trivial, due to the lack of dataflow. I don't
even know if the dataflow would be precise enough in practice.

Jason Orendorff

unread,
Jan 5, 2016, 9:20:02 PM1/5/16
to Mike Shaver, Luke Wagner, Ehsan Akhgari, Rodrigo Bonifacio, JS Internals list
On Mon, Jan 4, 2016 at 1:55 PM, Mike Shaver <mike....@gmail.com> wrote:

> Has there been some innovation in EH since Cargill's spine-chilling
> exploration of making a simple container strongly exception safe? I recall
> the consensus being that it was quite difficult to actually be strongly
> exception safe, but it sounds like that's no longer the case now.
>

I hadn't seen Cargill's article before: <
http://ptgmedia.pearsoncmg.com/images/020163371x/supplements/Exception_Handling_Article.html
>

Frankly, yeah, C++ as it's actually practiced is a lot different now. The
code being critiqued here is chock full of bugs, and no surprise:
non-placement new and raw pointers everywhere. The first three bugs Cargill
finds have nothing to do with exceptions! The next involves a `throw`
statement, but the same bug would obtain if it said `return false;`
instead. I saw a lot of this caliber of C++ in the 1990s, but twenty years
on, it reads like a straw man.

Where I'm coming from is: Using exceptions instead of explicit error
checking can turn 30 lines of code into 10. Being able to see what I'm
doing is kind of a big deal for me. YMMV.

The tradeoff is, mfbt/Vector.h would have to be deeper code. It isn't
currently exception-safe. It'd require different primitives, no more
Impl::copyConstruct() on a whole range of values. But what the hell. Many
Mozilla hackers can hack that grade of code, and the benefit would be worth
the ongoing maintenance (if we could afford the full initial investment,
which we still can't).

-j

Robert O'Callahan

unread,
Jan 5, 2016, 9:57:48 PM1/5/16
to Luke Wagner, Jason Orendorff, Rodrigo Bonifacio, JS Internals list
On Tue, Dec 22, 2015 at 5:11 AM, Luke Wagner <lwa...@mozilla.com> wrote:

> I think there was also a performance reason: IIUC, the non-zero-cost
> exception handling strategy is baked into the Win32 ABI
> (https://www.microsoft.com/msj/0197/exception/exception.aspx) and thus
> enabling EH has a runtime cost for frames containing auto objects with
> destructors (which in modern SM is very common). I vaguely remember
> seeing a platform email thread about using EH a while back (>6 years)
> that measured the then-overhead in Gecko to be ~10%; I can't find the
> thread now. I talked to an engineer who worked on EH optimizations in
> MSVC and he said that there have been some 32-bit EH optimizations
> more recently but there is still runtime cost to having auto objects
> with destructors, so probably this is worth measuring again. Assuming
> we're able to move a large percentage of FF users to Win64 builds over
> the next year, then it seems like we'd care about this even less.
>

As I recall, there was a significant performance hit just enabling RTTI,
which is a prerequisite for exception handling. I think it was a space
problem. Mike Hommey would know more.

Rob
--
lbir ye,ea yer.tnietoehr rdn rdsme,anea lurpr edna e hnysnenh hhe uresyf
toD
selthor stor edna siewaoeodm or v sstvr esBa kbvted,t
rdsme,aoreseoouoto
o l euetiuruewFa kbn e hnystoivateweh uresyf tulsa rehr rdm or rnea
lurpr
.a war hsrer holsa rodvted,t nenh hneireseoouot.tniesiewaoeivatewt sstvr
esn

Luke Wagner

unread,
Jan 5, 2016, 10:28:07 PM1/5/16
to robert, Jason Orendorff, Rodrigo Bonifacio, JS Internals list
In theory, EH doesn't need full RTTI (dynamic_cast/typeid); just
enough for the thrown types. I can also see that gcc/msvc have
separate EH/RTTI flags. I don't have any actual measurements on this,
but it seems worth remeasuring since it might've improved in the
interim.
0 new messages