C++ Logo

std-discussion

Advanced search

Some feedback on scope guards

From: Andrey Semashev <andrey.semashev_at_[hidden]>
Date: Fri, 7 Apr 2023 20:31:46 +0300
Hi,

I hope this is the right place to report feedback on C++ Extensions for
Library Fundamentals TS v3.

On boost-dev mailing list, there was this discussion that highlighted
some issues with scope_success/scope_fail scope guards that are defined
in the Library Fundamentals TS:

https://lists.boost.org/Archives/boost/2023/04/254418.php
https://lists.boost.org/Archives/boost/2023/04/254420.php

(Sorry, the thread got split for some reason.)

In short, Andrzej Krzemienski points out that using
unhandled_exceptions() as a way of detecting whether an exception has
been thrown doesn't work with coroutines because after creating the
scope guard and capturing the result of unhandled_exceptions() the
coroutine may be suspended and resumed in a different context where the
number of unhandled exceptions is different. He then goes on to
demonstrate the problem with Boost.Scope, an implementation of the scope
guards proposed for review in Boost.

I think this issue needs to be addressed in the TS before inclusion into
the main standard. As a possible solution, I proposed the following idea:

- When entering a coroutine, capture the current result of
unhandled_exceptions() in the coroutine state. Let this captured counter
be accessible via co_unhandled_exceptions().
- When an exception is thrown or caught within a coroutine, in addition
to updating the thread-specific uncaught exception counter, the one
stored in the coroutine state is also similarly updated.
- When a thrown exception propagates out of a coroutine, and the caller
is also a coroutine, the parent's unhandled exception counter is updated
before unwinding the parent stack begins.
- In scope_success/scope_fail, use co_unhandled_exceptions() instead of
unhandled_exceptions() to detect if an exception has been thrown.

I admit this is just a rough idea of what could be done to fix this, and
it likely needs more thought or may be entirely wrong. This is just food
for thought.


Also, unrelated to the above, here are some other comments regarding
scope guards that I have based on my experience with scope guards (not
necessarily the ones defined in the TS) and comments I received on
Boost.Scope:

1. I think, there should be a way to activate a deactivated scope guard.
Also, there should be a way to construct an initially inactive scope
guard. This is important in cases when the scope guard needs to be
created earlier (possible in an outer scope) compared to when it should
become active. Notice that the point of creation of the scope guard
corresponds to the point when the scope guard action is executed when
the scope is left, and that point may be different from when it is
discovered that the scope guard needs to be activated.

2. For scope_success/scope_fail, it would be useful to be able to
specify a user-defined condition function for detecting the failure
condition. This is useful in cases when the failure is indicated not via
an exception but via other means, such as returning an error code. The
idea was implemented in Boost.Scope, where checking for an exception is
merely a default failure condition, with the user being able to specify
his own.

3. There is room for a scope guard that is more lightweight than
scope_exit. Specifically, a scope guard that unconditionally calls its
action, is not moveable and cannot be deactivated. Think of it as
lock_guard compared to unique_lock. In my experience, the majority of
scope guards don't need to be able to be (de)activated or moved, so this
optimization would be useful. Furthermore, such unconditional scope
guards don't need a name as you won't interact with them after
construction. Boost.Scope solves the by introducing scope_final guard,
and a macro to avoid having a name of the scope guard. I realize that
such a macro is unlikely to get into the standard, but still I would
like to note that such a feature would be nice. Regardless, a more
lightweight scope guards such as scope_final would be useful in its own
right.

For reference, here is Boost.Scope and its docs:

https://github.com/lastique/scope
https://lastique.github.io/scope/libs/scope/doc/html/index.html

Thanks.

Received on 2023-04-07 17:31:49