Skip to content

Commit

Permalink
Add TArray<TCoroutine<>> overloads to WhenAny and WhenAll
Browse files Browse the repository at this point in the history
  • Loading branch information
landelare committed Oct 15, 2023
1 parent e265d3f commit 1ac8f98
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 1 deletion.
26 changes: 26 additions & 0 deletions Plugins/UE5Coro/Source/UE5Coro/Private/AggregateAwaiters.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,18 @@ int FAggregateAwaiter::GetResumerIndex() const
return Data->Index;
}

template<typename T>
FAggregateAwaiter::FAggregateAwaiter(T All, const TArray<TCoroutine<>>& Coroutines)
: Data(std::make_shared<FData>(All.value ? Coroutines.Num() : !!Coroutines.Num()))
{
for (int i = 0; i < Coroutines.Num(); ++i)
Consume(Data, i, Coroutines[i]);
}
template UE5CORO_API FAggregateAwaiter::FAggregateAwaiter(
std::false_type, const TArray<TCoroutine<>>&);
template UE5CORO_API FAggregateAwaiter::FAggregateAwaiter(
std::true_type, const TArray<TCoroutine<>>&);

bool FAggregateAwaiter::await_ready()
{
checkf(Data, TEXT("Attempting to await moved-from aggregate awaiter"));
Expand All @@ -64,11 +76,25 @@ void FAggregateAwaiter::Suspend(FPromise& Promise)
Data->Lock.unlock();
}

#if UE5CORO_CPP20
FAnyAwaiter UE5Coro::WhenAny(const TArray<TCoroutine<>>& Coroutines)
{
return FAnyAwaiter(std::false_type(), Coroutines);
}
#endif

FRaceAwaiter UE5Coro::Race(TArray<TCoroutine<>> Array)
{
return FRaceAwaiter(std::move(Array));
}

#if UE5CORO_CPP20
FAllAwaiter UE5Coro::WhenAll(const TArray<TCoroutine<>>& Coroutines)
{
return FAllAwaiter(std::true_type(), Coroutines);
}
#endif

FRaceAwaiter::FRaceAwaiter(TArray<TCoroutine<>>&& Array)
: Data(std::make_shared<FData>(std::move(Array)))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include "UE5Coro/Definitions.h"
#include <memory>
#include "UE5Coro/AsyncCoroutine.h"
#include "UE5Coro/CoroutineAwaiters.h"
#include "UE5Coro/Private.h"

namespace UE5Coro::Private
Expand All @@ -61,12 +62,19 @@ concept TAggregateAwaitable =
namespace UE5Coro
{
/** co_awaits all parameters, resumes its own awaiting coroutine when the first
* one of them finishes.
* one of them finishes.<br>
* The result of the co_await expression is the index of the parameter that
* finished first. */
template<UE5CORO_PRIVATE_AWAITABLE... T>
Private::FAnyAwaiter WhenAny(T&&...);

#if UE5CORO_CPP20
/** Resumes the awaiting coroutine when all other coroutines have completed.<br>
* The result of the co_await expression is the index of the parameter that
* finished first. */
UE5CORO_API Private::FAnyAwaiter WhenAny(const TArray<TCoroutine<>>&);
#endif

/** co_awaits all coroutines in the array.
* The first one to finish cancels the others and resumes the caller.
* The result of the co_await expression is the array index of the coroutine
Expand All @@ -84,6 +92,11 @@ Private::FRaceAwaiter Race(TCoroutine<T>... Args);
* of them finish. */
template<UE5CORO_PRIVATE_AWAITABLE... T>
Private::FAllAwaiter WhenAll(T&&...);

#if UE5CORO_CPP20
/** Resumes the awaiting coroutine when all other coroutines have completed. */
UE5CORO_API Private::FAllAwaiter WhenAll(const TArray<TCoroutine<>>&);
#endif
}

namespace UE5Coro::Private
Expand Down Expand Up @@ -118,6 +131,9 @@ class [[nodiscard]] UE5CORO_API FAggregateAwaiter
(Consume(Data, Idx++, std::forward<T>(Awaiters)), ...);
}

template<typename T>
explicit FAggregateAwaiter(T, const TArray<TCoroutine<>>& Coroutines);

bool await_ready();
void Suspend(FPromise&);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include "Misc/AutomationTest.h"
#include "UE5Coro/AggregateAwaiters.h"
#include "UE5Coro/CoroutineAwaiters.h"
#include "UE5Coro/Threading.h"

using namespace UE5Coro;
using namespace UE5Coro::Private::Test;
Expand Down Expand Up @@ -232,6 +233,57 @@ void DoTest(FAutomationTestBase& Test)
Test.TestEqual(TEXT("State"), State, 2);
Test.TestEqual(TEXT("Return value"), Coro.GetResult(), 1);
}

#if UE5CORO_CPP20
{
int State = 0;
FAwaitableEvent Event(EEventMode::ManualReset);
World.Run(CORO
{
TArray<TCoroutine<>> Coros;
for (int i = 0; i < 10; ++i)
Coros.Add(World.Run(CORO
{
++State;
co_await Event;
++State;
}));
Test.TestEqual(TEXT("Initial state inside"), State, 10);
co_await WhenAll(Coros);
Test.TestEqual(TEXT("Final state inside"), State, 20);
++State;
});
Test.TestEqual(TEXT("Initial state outside"), State, 10);
Event.Trigger();
Test.TestEqual(TEXT("Final state outside"), State, 21);
}

{
int State = 0;
FAwaitableEvent Event(EEventMode::AutoReset);
World.Run(CORO
{
TArray<TCoroutine<>> Coros;
for (int i = 0; i < 10; ++i)
Coros.Add(World.Run(CORO
{
++State;
co_await Event;
++State;
}));
Test.TestEqual(TEXT("Initial state inside"), State, 10);
co_await WhenAny(Coros);
Test.TestEqual(TEXT("Final state inside"), State, 11);
++State;
});
Test.TestEqual(TEXT("Initial state outside"), State, 10);
for (int i = 0; i < 10; ++i)
{
Event.Trigger();
Test.TestEqual(TEXT("State outside"), State, i + 12);
}
}
#endif
}
}

Expand Down

0 comments on commit 1ac8f98

Please sign in to comment.