/* * Copyright (c) 2024 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) { ArgumentNullException.ThrowIfNull(lazy); ArgumentNullException.ThrowIfNull(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 } }