Skip to content

Commit c07a29c

Browse files
committed
[core] rework ErrorHandler to avoid allocating memory when possible
1 parent 0050514 commit c07a29c

File tree

1 file changed

+41
-26
lines changed

1 file changed

+41
-26
lines changed

core/foundation/src/TError.cxx

Lines changed: 41 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,11 @@ to be replaced by the proper DefaultErrorHandler()
2222

2323
#include "TError.h"
2424

25+
#include <cassert>
2526
#include <cstdarg>
2627
#include <cstdio>
2728
#include <cstdlib>
29+
#include <cstring>
2830
#include <cerrno>
2931
#include <string>
3032

@@ -108,46 +110,59 @@ ErrorHandlerFunc_t GetErrorHandler()
108110

109111
void ErrorHandler(Int_t level, const char *location, const char *fmt, std::va_list ap)
110112
{
111-
thread_local Int_t buf_size(256);
112-
thread_local char *buf_storage(nullptr);
113+
if (!fmt)
114+
fmt = "no error message provided";
113115

114-
char small_buf[256];
115-
char *buf = buf_storage ? buf_storage : small_buf;
116+
char smallBuf[256];
116117

117-
std::va_list ap_copy;
118-
va_copy(ap_copy, ap);
118+
thread_local int bufSize = sizeof(smallBuf);
119+
thread_local char *bufDynStorage = nullptr;
119120

120-
if (!fmt)
121-
fmt = "no error message provided";
121+
char *buf = bufDynStorage ? bufDynStorage : smallBuf;
122122

123-
Int_t n = vsnprintf(buf, buf_size, fmt, ap_copy);
124-
if (n >= buf_size) {
125-
va_end(ap_copy);
123+
std::va_list apCopy;
124+
va_copy(apCopy, ap);
125+
126+
// Figure out exactly how many bytes we need
127+
int nRequired = vsnprintf(nullptr, 0, fmt, ap) + 1;
128+
if (level >= kSysError && level < kFatal) {
129+
auto sysHandler = GetErrorSystemMsgHandlerRef();
130+
if (sysHandler)
131+
nRequired += strlen(sysHandler()) + 1; // +1 for the whitespace
132+
else {
133+
nRequired += snprintf(nullptr, 0, " (errno: %d)", errno) + 1;
134+
}
135+
}
126136

127-
buf_size = n + 1;
128-
if (buf != &(small_buf[0]))
137+
if (nRequired >= bufSize) {
138+
// Not enough space: allocate more space on the heap to fit the string
139+
if (buf != smallBuf)
129140
delete[] buf;
130-
buf_storage = buf = new char[buf_size];
131141

132-
// Try again with a sufficiently large buffer
133-
va_copy(ap_copy, ap);
134-
vsnprintf(buf, buf_size, fmt, ap_copy);
142+
bufSize = std::max(bufSize * 2, nRequired + 1);
143+
buf = bufDynStorage = new char[bufSize];
135144
}
136-
va_end(ap_copy);
137145

138-
std::string bp = buf;
146+
// Actually write the string
147+
int nWrittenPre = vsnprintf(buf, bufSize, fmt, apCopy);
148+
int nWrittenPost = 0;
139149
if (level >= kSysError && level < kFatal) {
140-
bp.push_back(' ');
141-
if (GetErrorSystemMsgHandlerRef())
142-
bp += GetErrorSystemMsgHandlerRef()();
143-
else
144-
bp += std::string("(errno: ") + std::to_string(errno) + ")";
150+
auto sysHandler = GetErrorSystemMsgHandlerRef();
151+
if (sysHandler) {
152+
// NOTE: overwriting the null byte written by the previous vsnprintf
153+
nWrittenPost = snprintf(buf + nWrittenPre, bufSize - nWrittenPre, " %s", sysHandler());
154+
} else {
155+
// NOTE: overwriting the null byte written by the previous vsnprintf
156+
nWrittenPost = snprintf(buf + nWrittenPre, bufSize - nWrittenPre, " (errno: %d)", errno);
157+
}
145158
}
159+
assert(nWrittenPre + nWrittenPost + 1 <= nRequired);
160+
va_end(apCopy);
146161

147162
if (level != kFatal)
148-
gErrorHandler(level, level >= gErrorAbortLevel, location, bp.c_str());
163+
gErrorHandler(level, level >= gErrorAbortLevel, location, buf);
149164
else
150-
gErrorHandler(level, kTRUE, location, bp.c_str());
165+
gErrorHandler(level, kTRUE, location, buf);
151166
}
152167

153168
////////////////////////////////////////////////////////////////////////////////

0 commit comments

Comments
 (0)