Boost logo

Boost :

Subject: Re: [boost] [Stacktrace] review
From: Andrey Semashev (andrey.semashev_at_[hidden])
Date: 2016-12-16 04:20:35


On 12/16/16 03:00, Emil Dotchevski wrote:
>
> I am not proposing a dependency of throw_exception on Starktrace, but
> removing a minimal amount of code from Stacktrace (e.g. something that
> packs everything into a memory buffer) and putting it into throw_exception.
> I do think that logically, this code should be left in a separate header,
> but it has to be written without any Boost dependencies. If that is not
> possible, I agree that the integration is a bad idea.

I'm not sure what else Boost.Stacktrace does; it looks like it means
moving the bulk of Stacktrace into the throw_exception submodule. I
don't think that is a reasonable solution in terms of code management.

Also, I'm not sure what APIs Stacktrace uses behind the scene, does it
depend on additional third party libraries to obtain the backtrace?

>> I must say I'm not really buying the "automatic stacktrace from
>> exception" advantage. Really, the source file and line, combined with
>> sufficient logging, were quite enough for me for years; if there were
>> cases I wished to have a backtrace, they were rare enough I don't
>> remember one of them.
>>
>> So please, just define a separate macro with this new feature and
>> leave BOOST_THROW_EXCEPTION as is.
>
> First, let's again clarify: there is no reason to change the
> BOOST_THROW_EXCEPTION macro. The stack trace can be captured by the
> boost::throw_exception function template.

BOOST_THROW_EXCEPTION calls boost::throw_exception, so by changing the
latter you also change the former.

> The problem is that users who want take advantage of the stack trace (I
> understand you are not one of them) have no control over the library level
> code that uses boost::throw_exception.

True.

> That said, if you agree that ultimately it is the user who should be able
> to decide whether or not "good" Boost libraries embed stack trace in
> exceptions they emit,

Yes, it should be primarilly the user's decision. Although I can see
library developers also wishing to avoid this feature, at least
selectively. That is why I propose a new macro for this.

> the argument wouldn't be whether or not capturing the
> stack trace is integrated into boost::throw_exception, but whether or not
> it is enabled by default. Either way, the boost::exception class (defined
> in boost/exception/exception.hpp) should provide a hook for that
> integration in order to avoid coupling between boost::throw_exception and
> Stacktrace.

The way I see it, boost::exception already provides everything needed,
which is the ability to attach arbitrary data to the exception. I don't
think adding more special case capability to it is a wise solution in
the long term.

>> The config macro to disable the stacktrace doesn't satisfy me. First,
>> because it is a way to opt out whereas I believe such a feature should
>> be an opt in as it is expensive.
>
> I think that how expensive it is needs to be investigated and discussed.
> Either way it is a compromise.

Yes, I think some performance numbers should be presented before making
the change to throw_exception.

Also, if we go with the run-time hook approach (with no hook by
default), the performance issue won't be so pressing.

>> What I'm thinking is that there could be a hook that would allow a
>> user-defined function to be called on the exception that is about to
>> be thrown. By default no hook is installed, so throwing an exception
>> works exactly as BOOST_THROW_EXCEPTION currently works.
>> Boost.Stacktrace could provide its hook, which the user could install
>> to enable automatic stacktraces. The important part is that this is
>> user's choice and that it decouples throwing the exception from
>> Boost.Stacktrace.
>
> Do you mean a run-time hook or a compile-time hook?

A run-time hook. I described why a compile-time hook doesn't work for me
(the config macro is equivalent to a compile-time hook with regard to
the points I made previously).

> I think this should be compile-time hook in order to avoid the perils of
> dynamic initialization in complex multi-thread programs. But we already
> have that compile-time hook, it's called boost::throw_exception.

The run-time hook can be tricky to implement, that is true, but in the
simplest form it should be doable. The implementation details are of
course discussable, but below is something more concrete to get things
started.

Introduce a process-wide singleton pointer, which is a pointer to the
function that will augment the exception about to be thrown:

   typedef void (*exception_augmenter)(boost::exception&);

The augmenter will be able to use Boost.Exception API to add any data to
the exception.

The pointer can be updated atomically. If it deems necessary, a
thread-local and module-local copy can be maintained. The default value
of null means no augmenter and throwing the exception works as
BOOST_THROW_EXCEPTION currently does.

The tricky part is to maintain the singleton. On Windows one can use a
named semaphore to store the pointer as the semaphore state (see
https://github.com/boostorg/sync/blob/develop/include/boost/sync/detail/waitable_timer.hpp#L121
for an example, Boost.Interprocess also has something similar). On POSIX
systems it might be enough to just mark the pointer with default
visibility. A solution with shared memory (shmget/shmat/etc.) also seems
possible.

The user will be able to install his (or Boost.Stacktrace's) augmenter
through the API provided by throw_exception submodule and thus enable
stacktraces in exceptions.


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk