Possible thread stack size problem on macOS with deep recursion
Hello,
I am the co-developer of FET, a Qt-based timetabling application, and I am investigating an intermittent crash reported by a macOS user.
The application performs timetable generation in several worker threads. Currently these threads are created using std::thread, for example:
std::thread([t]{
timetablingThreads[t].startGenerating();
}).detach();
The generation algorithm contains a large recursive function, Generate::randomSwap(). The recursion depth is explicitly limited to 14.
The user reported that with multiple generation threads the application sometimes crashes on macOS, while single-thread generation seems stable. The original macOS crash report showed memory allocation functions such as QArrayData::allocate() / malloc(), which initially made me suspect heap corruption.
To investigate this I built FET with AddressSanitizer:
-O1
-g
-fsanitize=address
-fno-omit-frame-pointer
-fno-optimize-sibling-calls
and linked with:
-fsanitize=address
ASan is definitely active.
With ASan, the problem becomes reproducible almost immediately. ASan reports:
ERROR: AddressSanitizer: stack-overflow
The worker-thread stack reported by ASan is approximately:
size 0x83000
so roughly 524 KiB.
The stack trace contains many recursive calls like:
Generate::randomSwap(int, int) generate.cpp:38619
Generate::randomSwap(int, int) generate.cpp:38619
...
until ASan reports the stack overflow.
As a test, I replaced the std::thread creation with QThread::create() and explicitly set a larger stack:
QThread* thread = QThread::create([t]{
timetablingThreads[t].startGenerating();
});
thread->setStackSize(16 * 1024 * 1024);
connect(thread, &QThread::finished,
thread, &QObject::deleteLater);
thread->start();
With this change, the immediate ASan stack overflow disappears and generation continues normally for much longer.
So my questions are:
Does this look like a normal consequence of the relatively small default worker-thread stack on macOS, especially with ASan and a large recursive function?
Would explicitly increasing the thread stack size be the appropriate permanent fix for this kind of application?
Is there anything Qt-specific I should consider when using QThread::setStackSize() on macOS?
Could the original non-ASan crash, which appeared inside malloc() / QArrayData::allocate(), plausibly have been another manifestation of stack exhaustion, or should I continue looking for an independent heap corruption/data race?
Is 16 MiB a reasonable stack size here, or would you recommend another approach?
Environment:
macOS on Apple Silicon
Qt 6.11.x
Clang / Xcode
C++17
FET timetable generator
up to 4 generation threads in the reported case
recursion depth limited to 14
I would appreciate any advice on whether increasing the worker-thread stack is the correct solution, or whether there is something else I should investigate before making this change permanent.