/*
* Copyright (c) 2023 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Plugins.Extensions.Loading
* File: IAsyncLazy.cs
*
* IAsyncLazy.cs is part of VNLib.Plugins.Extensions.Loading which is part of the larger
* VNLib collection of libraries and utilities.
*
* VNLib.Plugins.Extensions.Loading is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* VNLib.Plugins.Extensions.Loading is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
using System;
using System.Threading.Tasks;
using System.Runtime.CompilerServices;
namespace VNLib.Plugins.Extensions.Loading
{
///
/// Represents an asynchronous lazy operation. with non-blocking access to the target value.
///
/// The result type
public interface IAsyncLazy
{
///
/// Gets a value indicating whether the asynchronous operation has completed.
///
bool Completed { get; }
///
/// Gets a task that represents the asynchronous operation.
///
///
TaskAwaiter GetAwaiter();
///
/// Gets the target value of the asynchronous operation without blocking.
/// If the operation failed, throws an exception that caused the failure.
/// If the operation has not completed, throws an exception.
///
T Value { get; }
}
///
/// Extension methods for
///
public static class AsyncLazyExtensions
{
///
/// Gets an wrapper for the specified
///
///
///
/// The async operation task wrapper
public static IAsyncLazy AsLazy(this Task task) => new AsyncLazy(task);
///
/// Tranforms one lazy operation into another using the specified handler
///
///
/// The resultant type
///
/// The function that will peform the transformation of the lazy result
/// A new that returns the transformed type
public static IAsyncLazy Transform(this IAsyncLazy lazy, Func handler)
{
_ = lazy ?? throw new ArgumentNullException(nameof(lazy));
_ = handler ?? throw new ArgumentNullException(nameof(handler));
//Await the lazy task, then pass the result to the handler
static async Task OnResult(IAsyncLazy lazy, Func cb)
{
T result = await lazy;
return cb(result);
}
return OnResult(lazy, handler).AsLazy();
}
#nullable disable
private sealed class AsyncLazy : IAsyncLazy
{
private readonly Task _task;
private T _result;
public AsyncLazy(Task task)
{
_task = task ?? throw new ArgumentNullException(nameof(task));
_ = task.ContinueWith(SetResult, TaskScheduler.Default);
}
///
public bool Completed => _task.IsCompleted;
///
public T Value
{
get
{
if (_task.IsCompletedSuccessfully)
{
return _result;
}
else if(_task.IsFaulted)
{
//Compress and raise exception from result
return _task.GetAwaiter().GetResult();
}
else
{
throw new InvalidOperationException("The asynchronous operation has not completed.");
}
}
}
/*
* Only set the result if the task completed successfully.
*/
private void SetResult(Task task)
{
if (task.IsCompletedSuccessfully)
{
_result = task.Result;
}
}
///
public TaskAwaiter GetAwaiter() => _task.GetAwaiter();
}
#nullable enable
}
}