landelare / ue5coro

A C++20 coroutine plugin offering seamless integration with Unreal Engine 5.
BSD 3-Clause Clear License
630 stars 58 forks source link

Critical error: Assertion failed: Index >= 0 (from LatentPromise.cpp:138) v1.10.3 #37

Open Recognized opened 1 week ago

Recognized commented 1 week ago

Application crashes in a cooked build (sometimes). Crash rate is ~80%. I might have done something wrong, though...

UE5Coro version: tag v1.10.3 UE: Version: 5.4.4-35576357+++UE5+Release-5.4 Platform: Windows 10 (22H2) [10.0.19045.5011] (x86_64)

[2024.10.11-17.48.19:877][430]LogWindows: Error: === Critical error: ===
[2024.10.11-17.48.19:877][430]LogWindows: Error: 
[2024.10.11-17.48.19:877][430]LogWindows: Error: Assertion failed: Index >= 0 [File:D:\build\++UE5\Sync\Engine\Source\Runtime\CoreUObject\Public\UObject\UObjectArray.h] [Line: 868] 
[2024.10.11-17.48.19:877][430]LogWindows: Error: 
[2024.10.11-17.48.19:877][430]LogWindows: Error: 
[2024.10.11-17.48.19:877][430]LogWindows: Error: 
[2024.10.11-17.48.19:877][430]LogWindows: Error: [Callstack] 0x00007ff663b8a4f8 game.exe!FUObjectArray::AllocateSerialNumber() []
[2024.10.11-17.48.19:877][430]LogWindows: Error: [Callstack] 0x00007ff663bcffaf game.exe!FWeakObjectPtr::operator=() []
[2024.10.11-17.48.19:877][430]LogWindows: Error: [Callstack] 0x00007ff66b69721f game.exe!`anonymous namespace'::FPendingLatentCoroutine::FinishNow() [E:\Unreal Projects\CatGame2\Plugins\UE5Coro\Plugins\UE5Coro\Source\UE5Coro\Private\LatentPromise.cpp:138]
[2024.10.11-17.48.19:877][430]LogWindows: Error: [Callstack] 0x00007ff66b6978ba game.exe!`anonymous namespace'::FPendingLatentCoroutine::UpdateOperation() [E:\Unreal Projects\CatGame2\Plugins\UE5Coro\Plugins\UE5Coro\Source\UE5Coro\Private\LatentPromise.cpp:111]
[2024.10.11-17.48.19:877][430]LogWindows: Error: [Callstack] 0x00007ff669486088 CatGame2.exe!FLatentActionManager::TickLatentActionForObject() []
[2024.10.11-17.48.19:877][430]LogWindows: Error: [Callstack] 0x00007ff6694641ff CatGame2.exe!FLatentActionManager::ProcessLatentActions() []
[2024.10.11-17.48.19:877][430]LogWindows: Error: [Callstack] 0x00007ff669482f1d game.exe!UWorld::Tick() []
[2024.10.11-17.48.19:877][430]LogWindows: Error: [Callstack] 0x00007ff6691d57b9 game.exe!UGameEngine::Tick() []
[2024.10.11-17.48.19:877][430]LogWindows: Error: [Callstack] 0x00007ff66a8363da game.exe!FEngineLoop::Tick() []
[2024.10.11-17.48.19:877][430]LogWindows: Error: [Callstack] 0x00007ff66a84e72c game.exe!GuardedMain() []
[2024.10.11-17.48.19:877][430]LogWindows: Error: [Callstack] 0x00007ff66a84e7fa game.exe!GuardedMainWrapper() []
[2024.10.11-17.48.19:877][430]LogWindows: Error: [Callstack] 0x00007ff66a8513c5 game.exe!LaunchWindowsStartup() []
[2024.10.11-17.48.19:877][430]LogWindows: Error: [Callstack] 0x00007ff66a8616a4 game.exe!WinMain() []
[2024.10.11-17.48.19:877][430]LogWindows: Error: [Callstack] 0x00007ff66f87bbd6 game.exe!__scrt_common_main_seh() [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288]
[2024.10.11-17.48.19:877][430]LogWindows: Error: [Callstack] 0x00007fffeafc7374 KERNEL32.DLL!UnknownFunction []

The reason is probably in code like this:

FAsyncCoroutine UMyGameInstance::ServerTravel_C(FString Map, bool bListen,
                                                FForceLatentCoroutine Latent) {
    if (UGameplayStatics::GetCurrentLevelName(GetWorld()) != Map) {
        co_await Async::MoveToGameThread();
        auto WorldLoaded = LaunchDelegateCoroutine(this, FCoreUObjectDelegates::PostLoadMapWithWorld);
        GetWorld()->ServerTravel(Map + (bListen ? "?listen" : ""), true);
        co_await WorldLoaded;
    }
    co_return;
}

This coroutine changes world and CallbackTarget becomes deleted. Though, there is no reason to register this coroutine with world context since game instance outlives the world.

Recognized commented 1 week ago

This seems to be addressed in v2.0, but I have to use 1.10.3 due to compatibility with clang.

landelare commented 6 days ago

Thank you for the bug report.

This coroutine changes world

Ouch. Yes, that's not handled well by UE5Coro 1 (see #22, #23), and fixing it required breaking changes that went far beyond the scope of a theoretical v1.11. Because of that, I'm inclined to treat this as a wontfix for v1 specifically, but I'd like your input before doing so. Maybe there's a low-hanging fruit somewhere, or this could be a separate, independent bug that I'm too hasty to consider a duplicate.

This seems to be addressed in v2.0

Thank you for testing this.

due to compatibility with clang.

There are a few things that you can try to work around your current situation. Your coroutine doesn't look particularly latent to me, so I'd first suggest not forcing it to latent mode. There's a good chance you'll be able to fix it right away. Since you'll be losing "latent this protection", the coroutine should look more like the left example here and work with a TWeakObjectPtr<UMyGameInstance> instead of this. This removes pretty much all world access by UE5Coro itself, and your code can get it right. Make sure to avoid latent awaiters in this case, too!

Alternatively, you can remove the LlvmTrap() from v2.0 at your own peril, if you're absolutely sure that none of your lambdas are affected by the bug that it refers to. Other than this, things are relatively stable with Clang; v2.0 was mainly developed on it before MSVC was fixed!

The trap is mostly there for user safety, because behavior will silently change between compilers. Ironically, the Clang bug provides a better user experience in this case than what C++20 mandates :)