fix crashes during destruction of static objects #8
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Fixes #7.
this_thread
is defined here with thread storage durationrpp/rpp/profile.h
Line 163 in 29ad6ef
and used here, in a place ultimately reached by the destructor with the default allocator (
Mallocator
withLog == true
).rpp/rpp/impl/profile.cpp
Lines 193 to 196 in 29ad6ef
From [basic.start.term],
we know that the destructor for
this_thread
will be called before those of any objects of static storage duration. This explains the error message seen in #7 example 1, as after calling the destructor ofVec
the snippet above is trying to lock a mutex that has already been destroyed. SinceThead_Profile
has a non-trivial destructor, it is undefined behaviour to use it after destruction and I therefore added a boolean flag to track whether or not its destructor has been called, and to avoid using the object if it has. This should prevent crashes but will not allow for complete allocation tracking. A workaround might be to move theThread_Profile
out of TLS and instead store it directly within the map, e.g.but that also would require some other changes, so in this PR I implemented a simpler solution that only addresses crashing.
The second example reveals another issue with destruction order, this time with the static data contained in
impl/log.cpp
. In this case when the lock onthis_thread.frames_lock
fails and an attempt is made to write an error message fromdie
,rpp/rpp/pos/thread_pos.cpp
Lines 211 to 215 in 29ad6ef
the implementation of
output
tries to lock another mutex,g_log_data.lock
, which is an object of static storage duration.rpp/rpp/impl/log.cpp
Line 150 in 29ad6ef
Because there is no set order of initialization or destruction of static variables between TUs, it is possible that the destructor of
g_log_data
will be called before the destructor of the globalVec
object in the TU containingmain
, and that seems to have happened here. The stack trace shows that the attempt to lockg_log_data.lock
fails, anddie
is called again to report the failure, resulting in recursion until an eventual segfault.I made sure that
g_log_data
is constructed before its first use and destroyed after its last use by placing an inline variable inlog.h
which wraps the constructor and destructor ofStatic_Data
. This guarantees that any static object appearing in a TU after the inclusion oflog.h
will be constructed afterg_log_data
and destroyed before it.