You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
* Frequency can be configured via the GcFrequencyHrs setting for the namespace (defaults to every 2 hours). * Reachable set of nodes is stored in memory, but queue of nodes to process are paged out to the database to allow other instances to resume an existing GC cycle if a server shuts down. * Import metadata is also cached in the database, to avoid having to read header data for previously visited nodes. * Still WIP: does not synchronize newly uploaded nodes with capturing of root set. #preflight none [CL 22735220 by Ben Marsh in ue5-main branch]
204 lines
5.9 KiB
C#
204 lines
5.9 KiB
C#
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace EpicGames.Core
|
|
{
|
|
/// <summary>
|
|
/// Utility functions for manipulating async tasks
|
|
/// </summary>
|
|
public static class AsyncUtils
|
|
{
|
|
/// <summary>
|
|
/// Converts a cancellation token to a waitable task
|
|
/// </summary>
|
|
/// <param name="token">Cancellation token</param>
|
|
/// <returns></returns>
|
|
public static Task AsTask(this CancellationToken token)
|
|
{
|
|
return Task.Delay(-1, token).ContinueWith(x => { }, TaskScheduler.Default);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts a cancellation token to a waitable task
|
|
/// </summary>
|
|
/// <param name="token">Cancellation token</param>
|
|
/// <returns></returns>
|
|
public static Task<T> AsTask<T>(this CancellationToken token)
|
|
{
|
|
return Task.Delay(-1, token).ContinueWith(_ => Task.FromCanceled<T>(token), TaskScheduler.Default).Unwrap();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a task that will be abandoned if a cancellation token is activated. This differs from the normal cancellation pattern in that the task will run to completion, but waiting for it can be cancelled.
|
|
/// </summary>
|
|
/// <param name="task">Task to wait for</param>
|
|
/// <param name="cancellationToken">Cancellation token for the operation</param>
|
|
/// <returns>Wrapped task</returns>
|
|
public static async Task<T> AbandonOnCancel<T>(this Task<T> task, CancellationToken cancellationToken)
|
|
{
|
|
if (cancellationToken.CanBeCanceled)
|
|
{
|
|
await await Task.WhenAny(task, Task.Delay(-1, cancellationToken)); // Double await to ensure cancellation exception is rethrown if returned
|
|
}
|
|
return await task;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Attempts to get the result of a task, if it has finished
|
|
/// </summary>
|
|
/// <typeparam name="T"></typeparam>
|
|
/// <param name="task"></param>
|
|
/// <param name="result"></param>
|
|
/// <returns></returns>
|
|
public static bool TryGetResult<T>(this Task<T> task, out T result)
|
|
{
|
|
if (task.IsCompleted)
|
|
{
|
|
result = task.Result;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
result = default!;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Waits for a time period to elapse or the task to be cancelled, without throwing an cancellation exception
|
|
/// </summary>
|
|
/// <param name="time">Time to wait</param>
|
|
/// <param name="token">Cancellation token</param>
|
|
/// <returns></returns>
|
|
public static Task DelayNoThrow(TimeSpan time, CancellationToken token)
|
|
{
|
|
return Task.Delay(time, token).ContinueWith(x => { }, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.Default);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes all the complete tasks from a list, allowing each to throw exceptions as necessary
|
|
/// </summary>
|
|
/// <param name="tasks">List of tasks to remove tasks from</param>
|
|
public static void RemoveCompleteTasks(this List<Task> tasks)
|
|
{
|
|
List<Exception> exceptions = new List<Exception>();
|
|
|
|
int outIdx = 0;
|
|
for (int idx = 0; idx < tasks.Count; idx++)
|
|
{
|
|
if (tasks[idx].IsCompleted)
|
|
{
|
|
AggregateException? exception = tasks[idx].Exception;
|
|
if (exception != null)
|
|
{
|
|
exceptions.AddRange(exception.InnerExceptions);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (idx != outIdx)
|
|
{
|
|
tasks[outIdx] = tasks[idx];
|
|
}
|
|
outIdx++;
|
|
}
|
|
}
|
|
tasks.RemoveRange(outIdx, tasks.Count - outIdx);
|
|
|
|
if (exceptions.Count > 0)
|
|
{
|
|
throw new AggregateException(exceptions);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes all the complete tasks from a list, allowing each to throw exceptions as necessary
|
|
/// </summary>
|
|
/// <param name="tasks">List of tasks to remove tasks from</param>
|
|
/// <returns>Return values from the completed tasks</returns>
|
|
public static List<T> RemoveCompleteTasks<T>(this List<Task<T>> tasks)
|
|
{
|
|
List<T> results = new List<T>();
|
|
|
|
int outIdx = 0;
|
|
for (int idx = 0; idx < tasks.Count; idx++)
|
|
{
|
|
if (tasks[idx].IsCompleted)
|
|
{
|
|
results.Add(tasks[idx].Result);
|
|
}
|
|
else if (idx != outIdx)
|
|
{
|
|
tasks[outIdx++] = tasks[idx];
|
|
}
|
|
}
|
|
tasks.RemoveRange(outIdx, tasks.Count - outIdx);
|
|
|
|
return results;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Starts prefetching the next item from an async enumerator while the current one is being processes
|
|
/// </summary>
|
|
/// <typeparam name="T">Value type</typeparam>
|
|
/// <param name="source">Sequence to enumerate</param>
|
|
/// <param name="cancellationToken">Cancellation token for the operation</param>
|
|
/// <returns></returns>
|
|
public static async IAsyncEnumerable<T> Prefetch<T>(this IAsyncEnumerable<T> source, [EnumeratorCancellation] CancellationToken cancellationToken)
|
|
{
|
|
await using IAsyncEnumerator<T> enumerator = source.GetAsyncEnumerator(cancellationToken);
|
|
if (await enumerator.MoveNextAsync())
|
|
{
|
|
T value = enumerator.Current;
|
|
|
|
for (; ; )
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
|
|
Task<bool> task = enumerator.MoveNextAsync().AsTask();
|
|
try
|
|
{
|
|
yield return value;
|
|
}
|
|
finally
|
|
{
|
|
await task; // Async state machine throws a NotSupportedException if disposed before awaiting this task
|
|
}
|
|
|
|
if (!await task)
|
|
{
|
|
break;
|
|
}
|
|
|
|
value = enumerator.Current;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Starts prefetching a number of items from an async enumerator while the current one is being processes
|
|
/// </summary>
|
|
/// <typeparam name="T">Value type</typeparam>
|
|
/// <param name="source">Sequence to enumerate</param>
|
|
/// <param name="count">Number of items to prefetch</param>
|
|
/// <param name="cancellationToken">Cancellation token for the operation</param>
|
|
/// <returns></returns>
|
|
public static IAsyncEnumerable<T> Prefetch<T>(this IAsyncEnumerable<T> source, int count, CancellationToken cancellationToken = default)
|
|
{
|
|
if (count == 0)
|
|
{
|
|
return source;
|
|
}
|
|
else
|
|
{
|
|
return Prefetch(source, count - 1, cancellationToken);
|
|
}
|
|
}
|
|
}
|
|
}
|