Is there any way to prevent main thread from crashing?
I want the main thread to keep running after this memory access violation exception happens.
std::thread {
[]() {
for (auto i = 0; i < 10; i) {
std::cout << "Hello World from detached thread!\n";
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
std::cout << "Boom!\n";
*(int*)(0) = 42;
}
}.detach();
while (true) {
std::cout << "Hello World!\n";
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
CodePudding user response:
Not in general.
One major causes of access violations is memory corruption - a pointer to a data element gets overwritten, or the size of some object gets wiped. There's only one memory address space in a process. One of the most likely places for that to happen is in the heap of memory doled out by new
, malloc()
, et al. And there's only one heap per process.
So any thread that corrupts memory corrupts it for all threads.
Note that in your contrived example, you know you're deferencing a null pointer. The problem in real code is that you'll never know why you're dereferencing a null pointer. And if the memory you access illicitly isn't at address zero, or some value very close to it, some "kill that thread so the rest can survive" logic can never really know what happened.
You can try to keep running, just like you can try running across a minefield.
And if you do keep running, how do you know your results are valid? You will almost certainly never have tested with whatever exact memory corruption your process is trying to run with. Because if you had, you would have known about that bug and fixed it, right?
And what are you going to do if your thread dies while it's holding a lock of some kind?
CodePudding user response:
You could use sigaction
to install a handler for SIGSEGV that kept that thread busy while main kept running.
static void handler(int sig, siginfo_t* si, void* unused)
{
sleep(100000); // or something similar that's async-signal-safe
// on your operating system
}
int main(int argc, char *argv[])
{
struct sigaction sa;
sa.sa_flags = SA_SIGINFO;
sigemptyset(&sa.sa_mask);
sa.sa_sigaction = handler;
if (sigaction(SIGSEGV, &sa, NULL) == -1)
perror("sigaction");
...
This is of course incredible fragile. For example, if the thread that generated the SIGSEGV was holding a lock, then it won't be released so other threads can acquire it.
As Ulrich points out, the usual approach to be tolerant to such things is to spawn child processes. Google Chrome famously started doing this for it's browser tabs years back, helping it have a higher level of resiliency than other browsers at the time (who then had to catch up themselves). Modern Operating Systems make it very efficient to have multiple processes, whereas in older ones threads may have used massively less resources....