Topic: Differentiating destructor invocation contexts (updated)


Author: Jens Maurer <Jens.Maurer@gmx.net>
Date: Tue, 08 Jan 2013 23:29:06 +0100
Raw View
This is a multi-part message in MIME format.
--------------020507080804090307090300
Content-Type: text/plain; charset=ISO-8859-1


Thanks for all the helpful input.  I've thought about the issue a bit more,
and it seems that specifying a  std::uncaught_exception_count()  function
in the standard would be the least disruptive way of providing the fringe
feature I'm looking for.  Plus, it's already available in major
implementations.

Updated paper is attached, further feedback is welcome.

Jens

--




--------------020507080804090307090300
Content-Type: text/html;
 name="destructor-unwinding.html"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename="destructor-unwinding.html"

<html>
<head>
<title>Differentiating destructor invocation contexts</title>

<style type="text/css">
  ins { text-decoration:none; font-weight:bold; background-color:#A0FFA0 }
  del { text-decoration:line-through; background-color:#FFA0A0 }
</style>

</head>

<body>
Jens Maurer<br>
2013-01-08

<h1>Differentiating destructor invocation contexts</h1>

<h2>Motivation</h2>

<p>
Herb Sutter's <a href="http://www.gotw.ca/gotw/047.htm">discussion of
std::uncaught_exception()</a> concludes "Unfortunately, I do not know
of any good and safe use for std::uncaught_exception. My advice: Don't
use it."
</p>

<p>
The technical reason (aside from the moral objection) is the inability
to differentiate between "this destructor was directly called by
stack unwinding" and "stack unwinding is ongoing, but this destructor
was called from a regular scope exit".  However, sometimes exactly
this differentiation would be helpful to perform additional cleanup
specific to stack unwinding only.  Example:
</p>
<pre>
  struct Transaction {
    Transaction();
    ~Transaction() {
      if (std::uncaught_exception())
        RollBack();
      else
        Commit();
    }
  };
</pre>

The well-intended meaning was for a transaction to roll back if its
scope was exited via an exception.  However, this detection is
unreliable for the following usage:

<pre>
  U::~U() {
    try {
      Transaction t( /*...*/ );
      // do work
    } catch( ... ) {
      // clean up
    }
  }
</pre>

If U::~U() is called during stack unwinding, the transaction inside
will always roll back, although "commit" was expected. Note that the
transaction construct could appear in a called function that is not
(and should not be) aware of the context from which it is called.

<h2>Approach 1: Add a second destructor called for stack unwinding</h2>

A class can have two destructors, one that is called during stack
unwinding and another one that is called for regular cleanup.
This also fits well with today's
implementations of stack unwinding, where a distinct code path from
normal scope exit is used.

<pre>
struct S {
  ~S();    // regular destructor
  ~S(int); // unwinding destructor
};
</pre>

Features:
<ul>
<li>A regular destructor always calls regular base and member
destructors.</li>
<li>An unwinding destructor prefers unwinding destructors for bases
and members, but calls the regular destructor if no unwinding one is
available.</li>
<li>If there is no regular destructor in a class, an implicit one is
declared/defined.</li>
<li>If there is no unwinding destructor in a class and any base or
member has an unwinding destructor, an implicit one is
declared/defined.</li>
<li>Scope exit, delete-expressions, and destruction of static/thread
storage duration objects call the regular destructor.</li>
<li>Stack unwinding (including failed construction) calls the
unwinding destructor (if present) with an unspecified value for the
argument.</li>
<li>The S(int) destructor can be called explicitly, e.g. using
<code>s.~S()</code></li>
</ul>

The transaction example could then be written like this:

<pre>
  struct Transaction {
    ~Transaction()
    {
      Commit();
    }

    ~Transaction(int)
    {
      RollBack();
    }
  };
</pre>

Open issues:
<ul>
<li>virtual destructors</li>
<li>Calling base and member destructors when the subobject has or does
not have a S(int) -style destructor</li>
<li>smart pointers may want to forward the "unwinding" call context,
thus an appropriate "delete" syntax must be invented</li>
</ul>

<h2>Approach 2: Differentiation by destructor parameter value</h2>

(General idea by
<a href="https://github.com/panaseleus/stack_unwinding#d-style-scope-guardsactions">Evgeny
Panasyuk</a>.)

A new kind of destructor taking a <code>bool</code> parameter
conceptually replaces (not: complements) the existing destructor
syntax.  A class can either have a traditional destructor without any
parameters, or one that takes a <code>bool</code> parameter.
Example:
<pre>
struct S {
  ~S(bool unwinding);  // called with argument value "true" if unwinding
};
</pre>

Features:

<ul>
<li>no parameterless ~S() allowed in the same class</li>
<li>~S(bool) calls base and member destructors; if those take a bool
parameter, the value is forwarded</li>
<li>Scope exit, delete-expressions, and destruction of static/thread
storage duration objects call ~S(false) or ~S() if the former is not
present</li>
<li>Stack unwinding (including failed construction) calls
~S(true) or ~S(), if the former is not present</li>
<li>A virtual destructor must match the signature in the base class
exactly.</li>
<li>The S(bool) destructor can be called explicitl, e.g. using <code>
s.~S(true)</code></li>
</ul>

The transaction example could then be written like this:
<pre>
  struct Transaction {
    ~Transaction(bool unwinding)
    {
      if (unwinding)
        RollBack();
      else
        Commit();
    }
  };
</pre>

Open issues:
<ul>
<li>smart pointers may want to forward the "unwinding" call context,
thus an appropriate "delete" syntax must be invented</li>
</ul>


<h2>Approach 3: uncaught_exception_count()</h2>

The exception implementation maintains a count of exceptions
currently causing stack unwinding.  If the count is the same during
construction as during destruction, the destruction was caused by a
regular scope exit, otherwise it is due to stack unwinding.
See also <a
href="https://github.com/panaseleus/stack_unwinding#d-style-scope-guardsactions">Evgeny Panasyuk's library</a>.

  Example:

<pre>
struct S {
  unsigned int count = std::uncaught_exception_count();
  ~S() { /* unwinding if count < std::uncaught_exception_count() */ }
};
</pre>

Features:
<ul>
<li>No core language syntax change required.</li>
<li>Forwarding to base and member destructors is implicit, if those
use the same pattern</li>
<li>Uses some memory to remember the count, redundantly with the C++
implementation</li>
<li>The pattern may be confused when an object of type S has dynamic
storage duration.</li>
<li>Implemented on MSVC, GCC, Clang.</li>
</ul>

The memory footprint can be made as small as one bit:
<pre>
struct S {
  bool latch : 1;
  S() : count(std::uncaught_exception_count() & 1) { }
  ~S() { /* unwinding if latch != std::uncaught_exception_count() & 1 */ }
};
</pre>




<h2>Discussion</h2>

<p>
Existing code will continue to work as before; only new code expressly
using the new feature will be affected.  No additional keyword is
consumed.  The "int" parameter for the unwinding destructor is
motivated by a roughly similar use for overloaded increment and
decrement operators; see 13.5.7 over.inc.
</p>

<p>
The ~S(int) idea seems to be inferior, because the presence of an
~S(int) destructor changes the possible call contexts and thus the
implied meaning of an already existing ~S() destructor.  Factoring
common code in the destructor becomes more involved.  Also, the
wording changes appear to be more involved.
</p>

<p>
The ~S(bool) idea seems to imply quite a few wording changes to a
feature only used in fringe cases.  The
std::uncaught_exception_count() approach seems to localize and
minimize the required changes.
</p>

<p>
The mechanisms presented above are only intended for objects with
automatic storage duration.  For static, thread, or dynamic storage
duration, the semantics should be well-defined, but no particular add-on
value should be expected from the new feature (regardless of
approach).
</p>


</body>
</html>

--------------020507080804090307090300--

.


Author: =?ISO-8859-1?Q?Daniel_Kr=FCgler?= <daniel.kruegler@gmail.com>
Date: Tue, 8 Jan 2013 23:39:10 +0100
Raw View
2013/1/8 Jens Maurer <Jens.Maurer@gmx.net>:
>
> Thanks for all the helpful input.  I've thought about the issue a bit more,
> and it seems that specifying a  std::uncaught_exception_count()  function
> in the standard would be the least disruptive way of providing the fringe
> feature I'm looking for.  Plus, it's already available in major
> implementations.
>
> Updated paper is attached, further feedback is welcome.

No concrete opinion yet, but a question:

Shouldn't "count" named in the default constructor in the example starting with:

"The memory footprint can be made as small as one bit:"

be "latch"?

Thanks,

- Daniel

> Jens
>
> --
>
>
>

--




.


Author: Ville Voutilainen <ville.voutilainen@gmail.com>
Date: Wed, 9 Jan 2013 01:02:18 +0200
Raw View
On 9 January 2013 00:29, Jens Maurer <Jens.Maurer@gmx.net> wrote:
> Thanks for all the helpful input.  I've thought about the issue a bit more,
> and it seems that specifying a  std::uncaught_exception_count()  function
> in the standard would be the least disruptive way of providing the fringe
> feature I'm looking for.  Plus, it's already available in major
> implementations.
> Updated paper is attached, further feedback is welcome.

I find it a very pleasant surprise that uncaught_exception_count() is
already available
on the aforementioned major implementations, and I like that option a lot.

--




.


Author: Beman Dawes <bdawes@acm.org>
Date: Tue, 8 Jan 2013 18:34:04 -0500
Raw View
On Tue, Jan 8, 2013 at 6:02 PM, Ville Voutilainen
<ville.voutilainen@gmail.com> wrote:
> On 9 January 2013 00:29, Jens Maurer <Jens.Maurer@gmx.net> wrote:
>> Thanks for all the helpful input.  I've thought about the issue a bit more,
>> and it seems that specifying a  std::uncaught_exception_count()  function
>> in the standard would be the least disruptive way of providing the fringe
>> feature I'm looking for.  Plus, it's already available in major
>> implementations.
>> Updated paper is attached, further feedback is welcome.
>
> I find it a very pleasant surprise that uncaught_exception_count() is
> already available
> on the aforementioned major implementations, and I like that option a lot.

When Jens says "Implemented on MSVC, GCC, Clang", I assume he mean "an
implementation has been tested with these compilers" rather than "The
libraries shipping with these compilers already implement
uncaught_exception_count()".  Jens?

Am I the only one OD'ed on new core language features, and willing to
accept even a pretty good library feature rather than a  complexifying
core language feature?

--Beman

--




.


Author: Nicol Bolas <jmckesson@gmail.com>
Date: Tue, 8 Jan 2013 16:10:02 -0800 (PST)
Raw View
------=_Part_2284_17205864.1357690202747
Content-Type: text/plain; charset=ISO-8859-1



On Tuesday, January 8, 2013 2:29:06 PM UTC-8, Jens Maurer wrote:
>
>
> Thanks for all the helpful input.  I've thought about the issue a bit
> more,
> and it seems that specifying a  std::uncaught_exception_count()  function
> in the standard would be the least disruptive way of providing the fringe
> feature I'm looking for.  Plus, it's already available in major
> implementations.
>
> Updated paper is attached, further feedback is welcome.
>
> Jens
>
>
Given `std::uncaught_exception_count` (BTW: what versions of those
compilers provide this extension? Just out of curiosity), is it possible to
have some object that makes this a bit more intuitive? Perhaps something
like this:

class SomeClass
{
public:
  ~SomeClass()
  {
    if(unwind)
    {
      //Special cleanup for exception.
    }
  }
private:
  std::stack_unwinding unwind;
};

This would make it more clear to users reading the code what is actually
going on. I'm not wedded to any of the names in this example; I just want
an object that makes it more obvious what the code is detecting.

--




------=_Part_2284_17205864.1357690202747
Content-Type: text/html; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable

<br><br>On Tuesday, January 8, 2013 2:29:06 PM UTC-8, Jens Maurer wrote:<bl=
ockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border=
-left: 1px #ccc solid;padding-left: 1ex;">
<br>Thanks for all the helpful input. &nbsp;I've thought about the issue a =
bit more,
<br>and it seems that specifying a &nbsp;std::uncaught_exception_<wbr>count=
() &nbsp;function
<br>in the standard would be the least disruptive way of providing the frin=
ge
<br>feature I'm looking for. &nbsp;Plus, it's already available in major
<br>implementations.
<br>
<br>Updated paper is attached, further feedback is welcome.
<br>
<br>Jens
<br>
<br></blockquote><div><br>Given `std::uncaught_exception_count` (BTW: what =
versions of those compilers provide this extension? Just out of curiosity),=
 is it possible to have some object that makes this a bit more intuitive? P=
erhaps something like this:<br><br><div class=3D"prettyprint" style=3D"back=
ground-color: rgb(250, 250, 250); border-color: rgb(187, 187, 187); border-=
style: solid; border-width: 1px; word-wrap: break-word;"><code class=3D"pre=
ttyprint"><div class=3D"subprettyprint"><span style=3D"color: #008;" class=
=3D"styled-by-prettify">class</span><span style=3D"color: #000;" class=3D"s=
tyled-by-prettify"> </span><span style=3D"color: #606;" class=3D"styled-by-=
prettify">SomeClass</span><span style=3D"color: #000;" class=3D"styled-by-p=
rettify"><br></span><span style=3D"color: #660;" class=3D"styled-by-prettif=
y">{</span><span style=3D"color: #000;" class=3D"styled-by-prettify"><br></=
span><span style=3D"color: #008;" class=3D"styled-by-prettify">public</span=
><span style=3D"color: #660;" class=3D"styled-by-prettify">:</span><span st=
yle=3D"color: #000;" class=3D"styled-by-prettify"><br>&nbsp; </span><span s=
tyle=3D"color: #660;" class=3D"styled-by-prettify">~</span><span style=3D"c=
olor: #606;" class=3D"styled-by-prettify">SomeClass</span><span style=3D"co=
lor: #660;" class=3D"styled-by-prettify">()</span><span style=3D"color: #00=
0;" class=3D"styled-by-prettify"><br>&nbsp; </span><span style=3D"color: #6=
60;" class=3D"styled-by-prettify">{</span><span style=3D"color: #000;" clas=
s=3D"styled-by-prettify"><br>&nbsp; &nbsp; </span><span style=3D"color: #00=
8;" class=3D"styled-by-prettify">if</span><span style=3D"color: #660;" clas=
s=3D"styled-by-prettify">(</span><span style=3D"color: #000;" class=3D"styl=
ed-by-prettify">unwind</span><span style=3D"color: #660;" class=3D"styled-b=
y-prettify">)</span><span style=3D"color: #000;" class=3D"styled-by-prettif=
y"><br>&nbsp; &nbsp; </span><span style=3D"color: #660;" class=3D"styled-by=
-prettify">{</span><span style=3D"color: #000;" class=3D"styled-by-prettify=
"><br>&nbsp; &nbsp; &nbsp; </span><span style=3D"color: #800;" class=3D"sty=
led-by-prettify">//Special cleanup for exception.</span><span style=3D"colo=
r: #000;" class=3D"styled-by-prettify"><br>&nbsp; &nbsp; </span><span style=
=3D"color: #660;" class=3D"styled-by-prettify">}</span><span style=3D"color=
: #000;" class=3D"styled-by-prettify"><br>&nbsp; </span><span style=3D"colo=
r: #660;" class=3D"styled-by-prettify">}</span><span style=3D"color: #000;"=
 class=3D"styled-by-prettify"><br></span><span style=3D"color: #008;" class=
=3D"styled-by-prettify">private</span><span style=3D"color: #660;" class=3D=
"styled-by-prettify">:</span><span style=3D"color: #000;" class=3D"styled-b=
y-prettify"><br>&nbsp; std</span><span style=3D"color: #660;" class=3D"styl=
ed-by-prettify">::</span><span style=3D"color: #000;" class=3D"styled-by-pr=
ettify">stack_unwinding unwind</span><span style=3D"color: #660;" class=3D"=
styled-by-prettify">;</span><span style=3D"color: #000;" class=3D"styled-by=
-prettify"><br></span><span style=3D"color: #660;" class=3D"styled-by-prett=
ify">};</span><span style=3D"color: #000;" class=3D"styled-by-prettify"><br=
></span></div></code></div><br>This would make it more clear to users readi=
ng the code what is actually going on. I'm not wedded to any of the names i=
n this example; I just want an object that makes it more obvious what the c=
ode is detecting.<br></div>

<p></p>

-- <br />
&nbsp;<br />
&nbsp;<br />
&nbsp;<br />

------=_Part_2284_17205864.1357690202747--

.


Author: Alberto Ganesh Barbati <albertobarbati@gmail.com>
Date: Wed, 9 Jan 2013 01:24:30 +0100
Raw View
Il giorno 09/gen/2013, alle ore 00:34, Beman Dawes <bdawes@acm.org> ha scri=
tto:

> On Tue, Jan 8, 2013 at 6:02 PM, Ville Voutilainen
> <ville.voutilainen@gmail.com> wrote:
>> On 9 January 2013 00:29, Jens Maurer <Jens.Maurer@gmx.net> wrote:
>>> Thanks for all the helpful input.  I've thought about the issue a bit m=
ore,
>>> and it seems that specifying a  std::uncaught_exception_count()  functi=
on
>>> in the standard would be the least disruptive way of providing the frin=
ge
>>> feature I'm looking for.  Plus, it's already available in major
>>> implementations.
>>> Updated paper is attached, further feedback is welcome.
>>=20
>> I find it a very pleasant surprise that uncaught_exception_count() is
>> already available
>> on the aforementioned major implementations, and I like that option a lo=
t.
>=20
> When Jens says "Implemented on MSVC, GCC, Clang", I assume he mean "an
> implementation has been tested with these compilers" rather than "The
> libraries shipping with these compilers already implement
> uncaught_exception_count()".  Jens?

As for Clang, function uncaught_exception() is eventually implemented as gl=
obals->uncaughtExceptions !=3D 0. Implementing uncaught_exception_count() c=
ould be implemented as easily as just returning globals->uncaughtExceptions=
, as its value happens to be exactly what is needed. It is my understanding=
 that GCC shows a similar situation.

> Am I the only one OD'ed on new core language features, and willing to
> accept even a pretty good library feature rather than a  complexifying
> core language feature?

Count me in.

Ganesh

--=20




.


Author: Jens Maurer <Jens.Maurer@gmx.net>
Date: Wed, 09 Jan 2013 07:45:21 +0100
Raw View
On 01/09/2013 12:34 AM, Beman Dawes wrote:
> On Tue, Jan 8, 2013 at 6:02 PM, Ville Voutilainen
> <ville.voutilainen@gmail.com> wrote:
>> On 9 January 2013 00:29, Jens Maurer <Jens.Maurer@gmx.net> wrote:
>>> Thanks for all the helpful input.  I've thought about the issue a bit more,
>>> and it seems that specifying a  std::uncaught_exception_count()  function
>>> in the standard would be the least disruptive way of providing the fringe
>>> feature I'm looking for.  Plus, it's already available in major
>>> implementations.
>>> Updated paper is attached, further feedback is welcome.
>>
>> I find it a very pleasant surprise that uncaught_exception_count() is
>> already available
>> on the aforementioned major implementations, and I like that option a lot.
>
> When Jens says "Implemented on MSVC, GCC, Clang", I assume he mean "an
> implementation has been tested with these compilers" rather than "The
> libraries shipping with these compilers already implement
> uncaught_exception_count()".  Jens?

Right, it's the former.  See  https://github.com/panaseleus/stack_unwinding
for the implementation.

> Am I the only one OD'ed on new core language features, and willing to
> accept even a pretty good library feature rather than a  complexifying
> core language feature?

Sorry, I'm lost in the acronyms here.  What's "OD"?  And the use with "even"
seems to imply that "pretty good" means "not stellar"?  Do you have
any specific suggestions for improvement of the library feature?

Thanks,
Jens

--




.


Author: Beman Dawes <bdawes@acm.org>
Date: Wed, 9 Jan 2013 08:30:31 -0500
Raw View
On Wed, Jan 9, 2013 at 1:45 AM, Jens Maurer <Jens.Maurer@gmx.net> wrote:
> On 01/09/2013 12:34 AM, Beman Dawes wrote:
....
>
>> Am I the only one OD'ed on new core language features, and willing to
>> accept even a pretty good library feature rather than a  complexifying
>> core language feature?
>
> Sorry, I'm lost in the acronyms here.  What's "OD"?

"Overdose". Apologies for using an obscure Americanism on a mailing
list with worldwide readership.

>  And the use with "even"
> seems to imply that "pretty good" means "not stellar"?

What I should have written was "Am I the only one overdosed on new
core language features, and willing to accept a pretty good library
feature rather than a stellar but compexifying core language feature?"
or even better "I like the library approach".

>  Do you have
> any specific suggestions for improvement of the library feature?

No, I leave that in your capable hands:-)

--Beman

--




.