Skip to content

Commit 525b723

Browse files
kjkclaude
andcommitted
Add gLifetimeArena for program-lifetime allocations
Add a global Arena* gLifetimeArena (GetLifetimeArena/DestroyLifetimeArena in TempAllocator) for allocations that live until the program exits, so they don't need per-allocation frees and can be tracked. On exit, log its lifetime allocation count and peak bytes (same format as the temp allocator). Route gAppDataDir through it as a first, clearly-safe conversion (allocated once, only replaced via -appdata); drop its explicit free in DeleteAppTools. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent d8bb029 commit 525b723

5 files changed

Lines changed: 39 additions & 144 deletions

File tree

src/AppTools.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,8 @@ bool IsInstallerOrUninstallerExe() {
116116
static char* gAppDataDir = nullptr;
117117

118118
void DeleteAppTools() {
119-
str::FreePtr(&gAppDataDir);
119+
// gAppDataDir is allocated from gLifetimeArena (freed wholesale on exit)
120+
gAppDataDir = nullptr;
120121
}
121122

122123
void SetAppDataDir(const char* dir) {
@@ -131,7 +132,10 @@ void SetAppDataDir(const char* dir) {
131132
ReportIf(true);
132133
}
133134
}
134-
str::ReplaceWithCopy(&gAppDataDir, dir);
135+
// lives for the whole program: allocate from the lifetime arena. SetAppDataDir
136+
// is called at most a couple of times (default + a -appdata override), so the
137+
// (rare) replaced value being retained until exit is negligible.
138+
gAppDataDir = str::Dup(GetLifetimeArena(), dir);
135139
}
136140

137141
TempStr GetAppDataDirTemp() {

src/SumatraStartup.cpp

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -711,16 +711,18 @@ static void ResetTempAllocatorWithLogging() {
711711
ResetTempAllocator();
712712
}
713713

714-
// Logs the temp allocator's lifetime allocation count and peak bytes. Call on
715-
// exit, before logging is torn down.
716-
static void LogTempAllocatorLifetimeStats() {
717-
Arena* a = GetTempAllocator();
714+
// Logs an arena's lifetime allocation count and peak bytes. Call on exit, before
715+
// logging is torn down.
716+
static void LogArenaLifetimeStats(const char* what, Arena* a) {
717+
if (!a) {
718+
return;
719+
}
718720
u64 nAllocs = a->nAllocsLifetime;
719721
u64 peakBytes = a->peakBytesLifetime;
720722
char human[32];
721723
FormatSizeHumanIntoBuf(peakBytes, Str(human, (int)sizeof(human)));
722-
logf("temp allocator lifetime: %s allocations, peak %s bytes (%s)\n",
723-
str::FormatNumWithThousandSepTemp((i64)nAllocs), str::FormatNumWithThousandSepTemp((i64)peakBytes), human);
724+
logf("%s lifetime: %s allocations, peak %s bytes (%s)\n", what, str::FormatNumWithThousandSepTemp((i64)nAllocs),
725+
str::FormatNumWithThousandSepTemp((i64)peakBytes), human);
724726
}
725727

726728
static int RunMessageLoop() {
@@ -2231,9 +2233,11 @@ int APIENTRY WinMain(_In_ HINSTANCE /*hInstance*/, _In_opt_ HINSTANCE, _In_ LPST
22312233
UninstallCrashHandler();
22322234
}
22332235
DeleteAppTools();
2234-
LogTempAllocatorLifetimeStats();
2236+
LogArenaLifetimeStats("temp allocator", GetTempAllocator());
2237+
LogArenaLifetimeStats("lifetime arena", gLifetimeArena);
22352238
DestroyLogging();
22362239
DestroyTempAllocator();
2240+
DestroyLifetimeArena();
22372241

22382242
return exitCode;
22392243
}

src/scratch2.txt

Lines changed: 0 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
Ideas for refactorings:
32

43
- change all size_t => int
@@ -7,142 +6,8 @@ Ideas for refactorings:
76

87
------------------------------------------------------------------
98

10-
static void ClearPixmap(fz_pixmap* pixmap) {
11-
auto stride = pixmap->stride;
12-
size_t dx = (size_t)pixmap->w;
13-
size_t dy = (size_t)pixmap->h;
14-
u8* samples = pixmap->samples;
15-
ReportIf(pixmap->n != 3);
16-
for (size_t y = 0; y < dy; y++) {
17-
u8* d = samples + (stride * y);
18-
for (size_t x = 0; x < dx; x++) {
19-
d[0] = 255;
20-
d[1] = 0;
21-
d[2] = 0;
22-
d += pixmap->n;
23-
if (false) {
24-
if (x % 2 == 0) {
25-
*d++ = 255;
26-
*d++ = 0;
27-
*d++ = 0;
28-
} else {
29-
*d++ = 0;
30-
*d++ = 0;
31-
*d++ = 255;
32-
}
33-
}
34-
}
35-
}
36-
}
37-
38-
------------------------------------------------------------------
39-
40-
// Select random files to test. We want to test each file type equally, so
41-
// we first group them by file extension and then select up to maxPerType
42-
// for each extension, randomly, and inter-leave the files with different
43-
// extensions, so their testing is evenly distributed.
44-
// Returns result in <files>.
45-
static void RandomizeFiles(StrVec& files, int maxPerType) {
46-
StrVec fileExts;
47-
Vec<StrVec*> filesPerType;
48-
49-
for (int i = 0; i < files.Size(); i++) {
50-
char* file = files.at(i);
51-
char* ext = path::GetExtTemp(file);
52-
CrashAlwaysIf(!ext);
53-
int typeNo = fileExts.FindI(ext);
54-
if (-1 == typeNo) {
55-
fileExts.Append(ext);
56-
filesPerType.Append(new StrVec());
57-
typeNo = filesPerType.Size() - 1;
58-
}
59-
filesPerType.at(typeNo)->Append(file);
60-
}
61-
62-
for (size_t j = 0; j < filesPerType.size(); j++) {
63-
StrVec* all = filesPerType.at(j);
64-
StrVec* random = new StrVec();
65-
66-
for (int n = 0; n < maxPerType && all->Size() > 0; n++) {
67-
int idx = rand() % all->Size();
68-
char* file = all->at(idx);
69-
random->Append(file);
70-
all->RemoveAt(idx);
71-
}
72-
73-
filesPerType.at(j) = random;
74-
delete all;
75-
}
76-
77-
files.Reset();
78-
79-
bool gotAll = false;
80-
while (!gotAll) {
81-
gotAll = true;
82-
for (size_t j = 0; j < filesPerType.size(); j++) {
83-
StrVec* random = filesPerType.at(j);
84-
if (random->Size() > 0) {
85-
gotAll = false;
86-
char* file = random->at(0);
87-
files.Append(file);
88-
random->RemoveAt(0);
89-
}
90-
}
91-
}
92-
93-
for (size_t j = 0; j < filesPerType.size(); j++) {
94-
delete filesPerType.at(j);
95-
}
96-
}
97-
98-
------------------------------------------------------------------
99-
1009
StrVec2 ideas:
10110
- optimize for SetAt(): when removing last string from a page, free its memory (update currEnd)
10211
- implement StrVec2::InsertAt()
10312

10413
------------------------------------------------------------------
105-
106-
struct ClosureData;
107-
typedef void (*closureFunc)(ClosureData*);
108-
109-
struct ClosureData {
110-
closureFunc func;
111-
virtual ~ClosureData() = 0;
112-
virtual operator()() {
113-
func(this);
114-
}
115-
};
116-
117-
template <typename T>
118-
ClosureData* newClosure(void (*func)(T*)) {
119-
T* res = new T();
120-
res->func = (closureFunc)func;
121-
return res;
122-
}
123-
124-
struct UpdateCheckData : ClosureData {
125-
~UpdateCheckData() override {
126-
}
127-
};
128-
129-
void updateCheck(UpdateCheckData* d) {
130-
// ...
131-
delete d;
132-
}
133-
134-
auto res = newClosure<UpdateCheckData>(updateCheck);
135-
136-
------------------------------------------------------------------
137-
138-
Delegates ideas:
139-
https://github.com/rosbacke/delegate
140-
https://github.com/rosbacke/MCU-tools/tree/master/src/callback
141-
https://github.com/Naios/function2
142-
https://gist.github.com/twoscomplement/030818a6c38c5a983482dc3a385a3ab8
143-
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0792r3.html
144-
https://github.com/vittorioromeo/Experiments/blob/master/function_ref.cpp
145-
https://blog.stratifylabs.dev/device/2022-12-01-Callback-and-Lambdas-in-embedded-cpp/
146-
147-
CmdOpenAttachment,,Open Attachment,ver 3.6+
148-
CmdSaveAttachment,,Save Attachment,ver 3.6+

src/utils/TempAllocator.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,23 @@ void ResetTempAllocator() {
3737
}
3838
}
3939

40+
// Arena for allocations that live for the whole lifetime of the program (i.e.
41+
// never freed until exit). Allocating them here avoids per-allocation frees and
42+
// lets us track how much such memory we use (logged on exit). Never Reset().
43+
Arena* gLifetimeArena = nullptr;
44+
45+
Arena* GetLifetimeArena() {
46+
if (!gLifetimeArena) {
47+
gLifetimeArena = ArenaNew();
48+
}
49+
return gLifetimeArena;
50+
}
51+
52+
void DestroyLifetimeArena() {
53+
ArenaDelete(gLifetimeArena);
54+
gLifetimeArena = nullptr;
55+
}
56+
4057
namespace str {
4158
TempStr DupTemp(const char* s, size_t cb) {
4259
return str::Dup(GetTempAllocator(), s, cb);

src/utils/TempAllocator.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ Arena* GetTempAllocator();
77
void DestroyTempAllocator();
88
void ResetTempAllocator();
99

10+
// arena for allocations that live until the program exits (see TempAllocator.cpp)
11+
extern Arena* gLifetimeArena;
12+
Arena* GetLifetimeArena();
13+
void DestroyLifetimeArena();
14+
1015
template <typename T>
1116
FORCEINLINE T* AllocArrayTemp(size_t n) {
1217
if (!mulSafe<size_t>(&n, sizeof(T))) {

0 commit comments

Comments
 (0)