cry0genic
cry0genic

Reputation: 594

Mysterious C++ threading crash

The following code strangely crashes when entering the run function. None of the printfs trigger, and single-stepping into the function will cause a crash. Is it undefined behaviour, or a compiler bug? I'm on MacOS compiling with Apple clang 12.0.0. It crashes with EXC_BAD_ACCESS (code = 2).

#include <iostream>
#include <thread>
#include <vector>

struct Chunk {
    // the array must be this big
    // other array sizes don't trigger the bug consistently
    uint8_t array[524288];
};

std::thread generator_thread;
std::mutex generator_lock;
std::vector<Chunk*> to_generate;
std::condition_variable data_ready;
std::atomic<bool> running;

void begin();
void run();

void begin() {
    running = true;
    auto func = [] {
        run();
    };
    generator_thread = std::thread(func);
}

void run() {
    printf("Running in generator\n");
    while (running) {
        printf("Running in loop\n");
        Chunk *task;

        // take a chunk from the queue
        {
            std::unique_lock<std::mutex> lock(generator_lock);
            data_ready.wait(lock, [] { return to_generate.size() > 0 || !running; });
            if (!running) break;

            task = to_generate.back();
            to_generate.pop_back();
        }

        printf("deref chunk\n");

        // Despite never being executed in this example, this line merely existing
        // will cause a crash when entering the run_generator function.
        Chunk chunk = *task;

        // *task.chunk;
        // ^^^ Only having the line above will not trigger the bug; it must be assigned
    }
}

int main(int argc, const char *argv[]) {
    begin();
    while (true) {
        printf("run\n");
    }
    return 0;
}

Upvotes: 0

Views: 419

Answers (1)

Useless
Useless

Reputation: 67733

So, when you change your function to pre-reserve a stack frame with space for a half-megabyte object ... it crashes right at the start of the function when setting up that stack frame?

That's probably because you made sizeof Chunk equal to the entire default OSX thread stack size of 512Kb. If you single-step into the function, you should be able to see the instruction that triggers the fault, and it will likely be part of the stack frame setup/function preamble.

All of this is implementation-specific (the stack frames, the per-thread stack size), but putting really big things on the stack is generally a bad idea.

Upvotes: 1

Related Questions