landelare / ue5coro

A C++20 coroutine implementation for Unreal Engine 5 that feels almost native.
BSD 3-Clause Clear License
543 stars 48 forks source link

Build crashes with "Exceptions are not supported" #26

Open soraphis opened 5 months ago

soraphis commented 5 months ago

No Stacktrace informations, and it does not happen nor log anything in the editor. I have no idea how to debug it.

I just have a simple FAsyncCoroutine with a single co_await UE5Coro::Latent::UnpausedSeconds(TimerDelay); line in it.

it also does not crash EVERY time, but across multiple machines it happens often enough to consider removing ue5coro, but I really like the convencience it provides.

Attaching a debugger at a published build also does not give me that much more information. last stackframes are "ResumeCoro$1::catch", 3 unknown stackframes and then "ResumeCoro".

I changed the method to use the UE TimerManager and hadn't had the issue afterwards (or at least could not force enforce it).

UE5.3 | using currently version UE5coro 1.10.1

landelare commented 4 months ago

Exceptions are indeed not supported. If you search for this message, it comes from UE5Coro::Private::FPromise::unhandled_exception() and controlled by the usual PLATFORM_EXCEPTIONS_DISABLED macro from Unreal. Hitting that check means you've gone too far.

Even if enabled, exception "support" is very rudimentary and acts only as a throw; passthrough. You're expected to catch them, engine code is generally not exception safe. Of course, you're also free to patch that function to, e.g., remove that check at your own peril.

UE5Coro itself never throws exceptions, the problem is likely somewhere else in your project that I can't see or debug.

soraphis commented 4 months ago

• In the editor, I'm unable to reproduce it. It never runs into the function. • When I change the function to use the TImerManager with a callback, the error disappears completely. no exception happens and obviously no crash. • I'm not raising any exceptions in my code either. • I don't see what removing the check would give me? wouldn't it still crash?

I also could not really explain why it would come from ue5coro, but it happend with 2 completely un-related functions in my code and stopped completely when removing ue5coro.

and even if it is not the fault of ue5coro, I feel like it a bit lacking in terms of debuggability...

landelare commented 4 months ago

The check is a deliberate crash or break into the debugger indicating that you've gone too far and things are bound to corrupt if code would run further (the macro is from Unreal). You can 1:1 match the message from this issue's title to the string that goes into it to warn you.

It looks like you ended up in unhandled_exception(). The only thing that normally calls that function is the compiler-generated code for coroutines; you having ResumeCoro$1::catch on the call stack supports this. It's technically possible you're seeing a compiler bug, but I cannot debug that given the scarce information provided here, and I definitely cannot fix it... or debuggers for that matter.

soraphis commented 4 months ago

The check is a deliberate crash or break into the debugger indicating that you've gone too far and things are bound to corrupt if code would run further (the macro is from Unreal). You can 1:1 match the message from this issue's title to the string that goes into it to warn you.

I know that, I found unhandled_exception() before creating this issue, i had a debugger running inside this method to see where the exception has been thrown.

I'm also not really asking you to "fix it", I'm asking more for knowledge or help in how to better find out WHY this is happening. Where does the exception come from? why does it only happen when using co_await UE5Coro::Latent::UnpausedSeconds but not when using things like callback functions.

welp... thanks, I guess, so far. Will see if i find time to try to reproduce it in an empty project. Sure could be a compiler bug, but something tells me that this is more on the unlikely side of things.

landelare commented 1 week ago

I've received information from a studio using UE5Coro that helped me track down one situation where unhandled_exception can reliably get called: it happens if C++ exceptions are disabled and the coroutine raises or does not handle a SEH exception.

By default, exceptions are ON in the editor, and a SEH exception can be caused by something as simple as accessing nullptr.

Assuming your issue is the same, this would explain why you only saw the issue in packaged. In the case of a timer, your code "worked" because SEH exceptions pass through regular function calls without doing anything special. It's just a different way that undefined behavior can play out.

I'm not entirely sure what to do with that check. Based on my current assumptions, your code (or something surrounding it) did invoke undefined behavior, so reducing it to an ensure would amount to sweeping the problem under the rug. On the other hand, there's nothing I can do (I tried) if the coroutine genuinely wants to raise a SEH exception. In this instance, removing the check entirely would be the correct thing to do.

I'm reopening this until a decision is made.

soraphis commented 1 week ago

I can somewhat resolve my specific issue:

we first got rid of ue5coro in our project and that worked out ... at first. but a few weeks later we encountered other random crashes in a build. It turned out that we had references to UObjects in non UPROPERTIES so they got GCed in a build (not in the editor due to different asset management).

ue5coro was most likely just "hiding" the inner issue. So yeah, we resolved the issue on our side, but it required getting rid of ue5coro to identify the issue first.