Skip to content

Commit e96b01c

Browse files
the-be-toTheNumbat
andauthored
fix crashes during destruction of static objects (#8)
* prevent TLS being used after destruction * prevent log static data from being destroyed prematurely * fix alignment of g_log_data * fix re-entrancy in alloc logging, add static test * use storage for static data * add test * fix release build --------- Co-authored-by: TheNumbat <[email protected]>
1 parent d9d7ef8 commit e96b01c

File tree

7 files changed

+122
-35
lines changed

7 files changed

+122
-35
lines changed

rpp/impl/log.cpp

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,21 @@ struct Static_Data {
4040
}
4141
};
4242

43-
static Static_Data g_log_data;
43+
static Storage<Static_Data> g_log_data;
4444
static thread_local u64 g_log_indent = 0;
4545

46+
namespace detail {
47+
48+
Static_Init::Static_Init() noexcept {
49+
g_log_data.construct();
50+
}
51+
52+
Static_Init::~Static_Init() noexcept {
53+
g_log_data.destruct();
54+
}
55+
56+
} // namespace detail
57+
4658
#ifdef RPP_OS_WINDOWS
4759

4860
[[nodiscard]] String_View sys_error() noexcept {
@@ -147,23 +159,25 @@ void output(Level level, const Location& loc, String_View msg) noexcept {
147159
Thread::Id thread = Thread::this_id();
148160
::time_t timer = ::time(null);
149161

150-
Thread::Lock lock(g_log_data.lock);
162+
{
163+
Thread::Lock lock(g_log_data->lock);
151164

152-
String_View time = sys_time_string(timer);
165+
String_View time = sys_time_string(timer);
153166

154-
printf(format_str, time.length(), time.data(), level_str, thread, loc.file.length(),
155-
loc.file.data(), loc.line, g_log_indent * INDENT_SIZE, "", msg.length(), msg.data());
156-
fflush(stdout);
167+
printf(format_str, time.length(), time.data(), level_str, thread, loc.file.length(),
168+
loc.file.data(), loc.line, g_log_indent * INDENT_SIZE, "", msg.length(), msg.data());
169+
fflush(stdout);
157170

158-
for(auto& [_, callback] : g_log_data.callbacks) {
159-
callback(level, thread, timer, loc, msg);
171+
if(g_log_data->file) {
172+
fprintf(g_log_data->file, format_str, time.length(), time.data(), level_str, thread,
173+
loc.file.length(), loc.file.data(), loc.line, g_log_indent * INDENT_SIZE, "",
174+
msg.length(), msg.data());
175+
fflush(g_log_data->file);
176+
}
160177
}
161178

162-
if(g_log_data.file) {
163-
fprintf(g_log_data.file, format_str, time.length(), time.data(), level_str, thread,
164-
loc.file.length(), loc.file.data(), loc.line, g_log_indent * INDENT_SIZE, "",
165-
msg.length(), msg.data());
166-
fflush(g_log_data.file);
179+
for(auto& [_, callback] : g_log_data->callbacks) {
180+
callback(level, thread, timer, loc, msg);
167181
}
168182
}
169183

@@ -178,17 +192,17 @@ Scope::~Scope() noexcept {
178192

179193
[[nodiscard]] Token
180194
subscribe(Function<void(Level, Thread::Id, Time, Location, String_View)> f) noexcept {
181-
Thread::Lock lock(g_log_data.lock);
182-
Token t = g_log_data.next++;
183-
g_log_data.callbacks.insert(t, rpp::move(f));
195+
Thread::Lock lock(g_log_data->lock);
196+
Token t = g_log_data->next++;
197+
g_log_data->callbacks.insert(t, rpp::move(f));
184198
return t;
185199
}
186200

187201
void unsubscribe(Token token) noexcept {
188-
Thread::Lock lock(g_log_data.lock);
189-
g_log_data.callbacks.erase(token);
190-
if(g_log_data.callbacks.empty()) {
191-
g_log_data.callbacks.~Map();
202+
Thread::Lock lock(g_log_data->lock);
203+
g_log_data->callbacks.erase(token);
204+
if(g_log_data->callbacks.empty()) {
205+
g_log_data->callbacks.~Map();
192206
}
193207
}
194208

rpp/impl/profile.cpp

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -159,14 +159,16 @@ void Profile::Frame_Profile::exit() noexcept {
159159

160160
void Profile::alloc(Alloc a) noexcept {
161161
if constexpr(DO_PROFILE) {
162+
bool replaced = false;
163+
bool no_entry = false;
162164
{
163165
Thread::Lock lock(allocs_lock);
164166
Alloc_Profile& prof = allocs.get_or_insert(a.name);
165167

166168
if(a.size) {
167169

168170
if(prof.current_set.contains(a.address)) {
169-
warn("Profile: % reallocated %!", a.name, a.address);
171+
replaced = true;
170172
}
171173
prof.current_set.insert(a.address, a.size);
172174

@@ -179,7 +181,7 @@ void Profile::alloc(Alloc a) noexcept {
179181

180182
Opt<Ref<u64>> sz = prof.current_set.try_get(a.address);
181183
if(!sz.ok()) {
182-
warn("Profile: % freed % with no entry!", a.name, a.address);
184+
no_entry = true;
183185
} else {
184186
i64 size = **sz;
185187
prof.current_set.erase(a.address);
@@ -189,10 +191,16 @@ void Profile::alloc(Alloc a) noexcept {
189191
}
190192
}
191193
}
194+
195+
if(replaced) warn("Profile: % reallocated %!", a.name, a.address);
196+
if(no_entry) warn("Profile: % freed % with no entry!", a.name, a.address);
197+
192198
{
193-
Thread::Lock lock(this_thread.frames_lock);
194-
if(this_thread.during_frame) {
195-
this_thread.frames.back().allocations.push(rpp::move(a));
199+
if(!this_thread_destroyed) {
200+
Thread::Lock lock(this_thread.frames_lock);
201+
if(this_thread.during_frame) {
202+
this_thread.frames.back().allocations.push(rpp::move(a));
203+
}
196204
}
197205
}
198206
}
@@ -248,10 +256,6 @@ void Profile::finalize() noexcept {
248256
} else {
249257
info("No regions leaked.");
250258
}
251-
if(net != 0) {
252-
warn("Memory leaked, shutting down now...");
253-
Libc::exit(1);
254-
}
255259
}
256260

257261
} // namespace rpp

rpp/log.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,18 @@ void log(Level level, const Location& loc, String_View fmt, const Ts&... args) n
9292
Region(R) output(level, loc, format<Mregion<R>>(fmt, args...).view());
9393
}
9494

95+
namespace detail {
96+
97+
struct Static_Init {
98+
Static_Init() noexcept;
99+
~Static_Init() noexcept;
100+
};
101+
102+
// Constructed before subsequent globals and destructed after them.
103+
inline Static_Init g_initializer;
104+
105+
}; // namespace detail
106+
95107
} // namespace Log
96108

97109
RPP_NAMED_ENUM(Log::Level, "Level", info, RPP_CASE(info), RPP_CASE(warn), RPP_CASE(fatal));

rpp/profile.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ struct Profile {
138138
}
139139
~Thread_Profile() noexcept {
140140
finalize();
141+
this_thread_destroyed = true;
141142
}
142143

143144
void finalize() noexcept {
@@ -161,6 +162,7 @@ struct Profile {
161162
static inline Thread::Mutex allocs_lock;
162163
static inline Thread::Mutex finalizers_lock;
163164
static inline thread_local Thread_Profile this_thread;
165+
static inline thread_local bool this_thread_destroyed = false;
164166
static inline Map<Thread::Id, Ref<Thread_Profile>, Mhidden> threads;
165167
static inline Map<String_View, Alloc_Profile, Mhidden> allocs;
166168
static inline Vec<Function<void()>, Mhidden> finalizers;

test/static.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
2+
#include "test.h"
3+
4+
#include <rpp/base.h>
5+
#include <rpp/thread.h>
6+
7+
using Alloc = Mallocator<"Alloc">;
8+
9+
struct Destroy {
10+
~Destroy() {
11+
info("Log at static destruction.");
12+
Profile::finalize();
13+
}
14+
};
15+
Destroy at_exit;
16+
17+
Test test{"static"_v};
18+
19+
Vec<i32, Alloc> g_vec0;
20+
Vec<i32, Alloc> g_vec1{1, 2, 3};
21+
22+
i32 main() {
23+
Profile::begin_frame();
24+
25+
static Vec<i32, Alloc> vec{1, 2, 3};
26+
static Box<i32, Alloc> box{5};
27+
28+
static thread_local Vec<i32, Alloc> tls_vec{1, 2, 3};
29+
static thread_local Box<i32, Alloc> tls_box{5};
30+
31+
info("g_vec0: %", g_vec0);
32+
info("g_vec1: %", g_vec1);
33+
info("vec: %", vec);
34+
info("box: %", box);
35+
info("tls_vec: %", tls_vec);
36+
info("tls_box: %", tls_box);
37+
38+
auto v = Thread::spawn([]() {
39+
tls_vec = Vec<i32, Alloc>{1, 2, 3, 4, 5};
40+
info("tls_vec in thread: %", tls_vec);
41+
info("tls_box in thread: %", tls_box);
42+
return move(tls_vec);
43+
});
44+
info("Thread returned: %", v->block());
45+
46+
Profile::end_frame();
47+
return 0;
48+
}

test/static.expect

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[Level::info] g_vec0: Vec[]
2+
[Level::info] g_vec1: Vec[1, 2, 3]
3+
[Level::info] vec: Vec[1, 2, 3]
4+
[Level::info] box: Box{5}
5+
[Level::info] tls_vec: Vec[1, 2, 3]
6+
[Level::info] tls_box: Box{5}
7+
[Level::info] tls_vec in thread: Vec[1, 2, 3, 4, 5]
8+
[Level::info] tls_box in thread: Box{null}
9+
[Level::info] Thread returned: Vec[1, 2, 3, 4, 5]

test/test.h

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,10 @@
66
using namespace rpp;
77

88
struct Test {
9-
using Alloc = Mallocator<"Test">;
10-
119
explicit Test(String_View name) : name(name) {
1210
token = Log::subscribe(
1311
[&](Log::Level lvl, Thread::Id, Log::Time, Log::Location, String_View msg) {
14-
auto m = format<Alloc>("[%] %\n"_v, lvl, msg);
12+
auto m = format<Mhidden>("[%] %\n"_v, lvl, msg);
1513
for(u8 c : m) {
1614
result.push(c);
1715
}
@@ -20,7 +18,7 @@ struct Test {
2018
~Test() {
2119
Log::unsubscribe(token);
2220

23-
auto expect = name.append<Alloc>(".expect"_v);
21+
auto expect = name.append<Mdefault>(".expect"_v);
2422
expected = move(*Files::read(expect.view()));
2523

2624
bool differs = false;
@@ -36,14 +34,14 @@ struct Test {
3634
}
3735

3836
if(differs) {
39-
auto corrected = name.append<Alloc>(".corrected"_v);
37+
auto corrected = name.append<Mdefault>(".corrected"_v);
4038
static_cast<void>(Files::write(corrected.view(), result.slice()));
4139
Libc::exit(1);
4240
}
4341
}
4442

4543
String_View name;
46-
Vec<u8, Alloc> result;
44+
Vec<u8, Mhidden> result;
4745
Vec<u8, Files::Alloc> expected;
4846
Log::Token token;
4947
};

0 commit comments

Comments
 (0)