higan-emu/libco

Exceptions cannot be handled when using amd64.c or x86.c on Windows

Opened this issue · 2 comments

Hello!

When C++ code on a cothread throws an exception the stack unwinding thinks it is from a noexcept method and calls terminate instead of correctly unwinding the stack within the cothread. This seems to be a bug in how the new stack is prepared by libco in the amd64.c and x86.c backends.

Repro:

#include <stdexcept>
#include <cstdio>
#include <libco.h>

cothread_t parent;

void will_throw()
{
    throw std::runtime_error("Catch me!");
}

void entry()
{
    try
    {
        will_throw();
    }
    catch (const std::exception& e)
    {
        std::printf("%s\n", e.what());
        co_switch(parent);
    }
}

int main()
{
    parent = co_active();
    auto cothread = co_create(262144 * sizeof(void*), &entry);
    co_switch(cothread);
    return 0;
}

Edit:
Correction: The statement "the stack unwinding thinks it is from a noexcept method and calls terminate" is only true when the exception is thrown from a function called by the entry function from a try block and only in the Debug configuration. If the exception is thrown directly from the try block then the exception object won't be caught properly and the executable tries to read from invalid memory.

This seems like something that would be nice to have for correctness. libco was extracted from a C++ codebase (higan), but that did not use exceptions (at least for anything running in a co_thread) so the issue never came up.

Unfortunately, I don't know enough about how C++ exceptions work on the various platforms that libco supports, or what makes a C++ compiler think a function is noexcept. If there's a solution that is simple and Just Works, that would be nice; if it's complex and requires different variants for every combination of architecture, OS, and C++ compiler, it might be better to just document "no exceptions in coroutines".

Exceptions work on Linux and on Windows they work with the fiber.c backend. So it's not a case of they don't work, it's just Windows that is affected, and only the backends that use raw assembly.