Browse Source

awaitable async calls

flabbet 1 year ago
parent
commit
6fe8fc4231

+ 7 - 6
src/PixiEditor.AvaloniaUI/Models/ExtensionServices/PaletteProvider.cs

@@ -2,8 +2,11 @@
 using System.Collections.ObjectModel;
 using System.Collections.ObjectModel;
 using System.Linq;
 using System.Linq;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
+using PixiEditor.AvaloniaUI.Models.Palettes;
+using PixiEditor.Extensions.CommonApi.Async;
 using PixiEditor.Extensions.CommonApi.Palettes;
 using PixiEditor.Extensions.CommonApi.Palettes;
 using PixiEditor.Extensions.CommonApi.Palettes.Parsers;
 using PixiEditor.Extensions.CommonApi.Palettes.Parsers;
+using PixiEditor.Platform;
 
 
 namespace PixiEditor.AvaloniaUI.Models.ExtensionServices;
 namespace PixiEditor.AvaloniaUI.Models.ExtensionServices;
 
 
@@ -18,7 +21,7 @@ internal sealed class PaletteProvider : IPaletteProvider
         dataSources = new ObservableCollection<PaletteListDataSource>();
         dataSources = new ObservableCollection<PaletteListDataSource>();
     }
     }
 
 
-    public async Task<List<IPalette>> FetchPalettes(int startIndex, int items, FilteringSettings filtering)
+    public async AsyncCall<List<IPalette>> FetchPalettes(int startIndex, int items, FilteringSettings filtering)
     {
     {
         List<IPalette> allPalettes = new();
         List<IPalette> allPalettes = new();
         foreach (PaletteListDataSource dataSource in dataSources)
         foreach (PaletteListDataSource dataSource in dataSources)
@@ -30,10 +33,9 @@ internal sealed class PaletteProvider : IPaletteProvider
         return allPalettes;
         return allPalettes;
     }
     }
 
 
-    public async Task<bool> AddPalette(IPalette palette, bool overwrite = false)
+    public async AsyncCall<bool> AddPalette(IPalette palette, bool overwrite = false)
     {
     {
-        //TODO: Implement
-        /*LocalPalettesFetcher localPalettesFetcher = dataSources.OfType<LocalPalettesFetcher>().FirstOrDefault();
+        LocalPalettesFetcher localPalettesFetcher = dataSources.OfType<LocalPalettesFetcher>().FirstOrDefault();
         if(localPalettesFetcher == null)
         if(localPalettesFetcher == null)
         {
         {
             return false;
             return false;
@@ -51,12 +53,11 @@ internal sealed class PaletteProvider : IPaletteProvider
             }
             }
         }
         }
 
 
-
         string finalName = LocalPalettesFetcher.GetNonExistingName(palette.Name, true);
         string finalName = LocalPalettesFetcher.GetNonExistingName(palette.Name, true);
 
 
         await localPalettesFetcher.SavePalette(
         await localPalettesFetcher.SavePalette(
             finalName,
             finalName,
-            palette.Colors.ToArray());*/
+            palette.Colors.ToArray());
 
 
         return true;
         return true;
     }
     }

+ 2 - 1
src/PixiEditor.AvaloniaUI/Models/Palettes/LocalPalettesFetcher.cs

@@ -5,6 +5,7 @@ using System.Threading.Tasks;
 using PixiEditor.AvaloniaUI.Helpers.Extensions;
 using PixiEditor.AvaloniaUI.Helpers.Extensions;
 using PixiEditor.AvaloniaUI.Models.IO;
 using PixiEditor.AvaloniaUI.Models.IO;
 using PixiEditor.AvaloniaUI.Models.IO.PaletteParsers.JascPalFile;
 using PixiEditor.AvaloniaUI.Models.IO.PaletteParsers.JascPalFile;
+using PixiEditor.Extensions.CommonApi.Async;
 using PixiEditor.Extensions.CommonApi.Palettes;
 using PixiEditor.Extensions.CommonApi.Palettes;
 using PixiEditor.Extensions.CommonApi.Palettes.Parsers;
 using PixiEditor.Extensions.CommonApi.Palettes.Parsers;
 using PixiEditor.Extensions.CommonApi.UserPreferences;
 using PixiEditor.Extensions.CommonApi.UserPreferences;
@@ -47,7 +48,7 @@ internal class LocalPalettesFetcher : PaletteListDataSource
         });
         });
     }
     }
 
 
-    public override async Task<List<IPalette>> FetchPaletteList(int startIndex, int count, FilteringSettings filtering)
+    public override async AsyncCall<List<IPalette>> FetchPaletteList(int startIndex, int count, FilteringSettings filtering)
     {
     {
         if (cachedPalettes == null)
         if (cachedPalettes == null)
         {
         {

+ 42 - 1
src/PixiEditor.Extensions.CommonApi/Async/AsyncCall.cs

@@ -1,12 +1,16 @@
-namespace PixiEditor.Extensions.CommonApi.Async;
+using System.Runtime.CompilerServices;
+
+namespace PixiEditor.Extensions.CommonApi.Async;
 
 
 public delegate void AsyncCallCompleted();
 public delegate void AsyncCallCompleted();
 public delegate void AsyncCallCompleted<T>(T result);
 public delegate void AsyncCallCompleted<T>(T result);
 public delegate void AsyncCallFailed(Exception exception);
 public delegate void AsyncCallFailed(Exception exception);
     
     
+[AsyncMethodBuilder(typeof(AsyncCallAsyncMethodBuilder))]
 public class AsyncCall
 public class AsyncCall
 {
 {
     private object? _result;
     private object? _result;
+    private Action continuation;
     public AsyncCallState State { get; protected set; } = AsyncCallState.Pending;
     public AsyncCallState State { get; protected set; } = AsyncCallState.Pending;
     public bool IsCompleted => State != AsyncCallState.Pending;
     public bool IsCompleted => State != AsyncCallState.Pending;
     public Exception? Exception { get; protected set; }
     public Exception? Exception { get; protected set; }
@@ -35,6 +39,7 @@ public class AsyncCall
         
         
         State = AsyncCallState.Failed;
         State = AsyncCallState.Failed;
         Exception = exception;
         Exception = exception;
+        this.continuation?.Invoke();
         Failed?.Invoke(exception);
         Failed?.Invoke(exception);
     }
     }
     
     
@@ -47,15 +52,46 @@ public class AsyncCall
         
         
         State = AsyncCallState.Completed;
         State = AsyncCallState.Completed;
         Result = result;
         Result = result;
+        this.continuation?.Invoke();
         Completed?.Invoke();
         Completed?.Invoke();
     }
     }
     
     
+    public AsyncCallAwaiter GetAwaiter()
+    {
+        return new AsyncCallAwaiter(this);
+    }
+    
     protected virtual object SetResultValue(object? result)
     protected virtual object SetResultValue(object? result)
     {
     {
         return result;
         return result;
     }
     }
+    
+    internal void RegisterContinuation(Action cont)
+    {
+        if (State == AsyncCallState.Pending)
+        {
+            if (this.continuation is null)
+            {
+                this.continuation = cont;  
+            }
+            else
+            {
+                var prev = this.continuation;
+                this.continuation = () =>
+                {
+                    prev();
+                    cont();
+                };
+            }
+        }
+        else
+        {
+            cont();
+        }
+    }
 }
 }
 
 
+[AsyncMethodBuilder(typeof(AsyncCallAsyncMethodBuilder<>))]
 public class AsyncCall<TResult> : AsyncCall
 public class AsyncCall<TResult> : AsyncCall
 {
 {
     public new TResult Result
     public new TResult Result
@@ -71,6 +107,11 @@ public class AsyncCall<TResult> : AsyncCall
         base.Completed += () => Completed?.Invoke(Result);
         base.Completed += () => Completed?.Invoke(Result);
     }
     }
     
     
+    public AsyncCallAwaiter<TResult> GetAwaiter()
+    {
+        return new AsyncCallAwaiter<TResult>(this);
+    }
+    
     public Task<TResult> AsTask()
     public Task<TResult> AsTask()
     {
     {
         TaskCompletionSource<TResult> tcs = new TaskCompletionSource<TResult>();
         TaskCompletionSource<TResult> tcs = new TaskCompletionSource<TResult>();

+ 95 - 0
src/PixiEditor.Extensions.CommonApi/Async/AsyncCallAsyncMethodBuilder.cs

@@ -0,0 +1,95 @@
+using System.Runtime.CompilerServices;
+
+namespace PixiEditor.Extensions.CommonApi.Async;
+
+public struct AsyncCallAsyncMethodBuilder
+{
+    private AsyncCall _task;
+    
+    public AsyncCall Task => _task;
+
+    public static AsyncCallAsyncMethodBuilder Create()
+    {
+        return new AsyncCallAsyncMethodBuilder
+        {
+            _task = new AsyncCall()
+        };
+    }
+    
+    public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
+        where TAwaiter : INotifyCompletion
+        where TStateMachine : IAsyncStateMachine
+    {
+        awaiter.OnCompleted(stateMachine.MoveNext);
+    }
+        
+    public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
+        where TAwaiter : ICriticalNotifyCompletion
+        where TStateMachine : IAsyncStateMachine
+    {
+        awaiter.UnsafeOnCompleted(stateMachine.MoveNext);
+    }
+
+    public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine
+    {
+        Action move = stateMachine.MoveNext;
+        ThreadPool.QueueUserWorkItem(_ =>
+        {
+            move();
+        });
+    }
+
+    public void SetStateMachine(IAsyncStateMachine stateMachine)
+    {
+    }
+
+    public void SetResult() => _task.SetResult(null);
+
+    public void SetException(Exception exception) => _task.SetException(exception);
+}
+
+public struct AsyncCallAsyncMethodBuilder<T>
+{
+    private AsyncCall<T> _task;
+    
+    public AsyncCall<T> Task => _task;
+
+    public static AsyncCallAsyncMethodBuilder<T> Create()
+    {
+        return new AsyncCallAsyncMethodBuilder<T>
+        {
+            _task = new AsyncCall<T>()
+        };
+    }
+    
+    public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
+        where TAwaiter : INotifyCompletion
+        where TStateMachine : IAsyncStateMachine
+    {
+        awaiter.OnCompleted(stateMachine.MoveNext);
+    }
+        
+    public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
+        where TAwaiter : ICriticalNotifyCompletion
+        where TStateMachine : IAsyncStateMachine
+    {
+        awaiter.UnsafeOnCompleted(stateMachine.MoveNext);
+    }
+
+    public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine
+    {
+        Action move = stateMachine.MoveNext;
+        ThreadPool.QueueUserWorkItem(_ =>
+        {
+            move();
+        });
+    }
+
+    public void SetStateMachine(IAsyncStateMachine stateMachine)
+    {
+    }
+
+    public void SetResult(T result) => _task.SetResult(result);
+
+    public void SetException(Exception exception) => _task.SetException(exception);
+}

+ 33 - 0
src/PixiEditor.Extensions.CommonApi/Async/AsyncCallAwaiter.cs

@@ -0,0 +1,33 @@
+using System.Runtime.CompilerServices;
+
+namespace PixiEditor.Extensions.CommonApi.Async;
+
+public class AsyncCallAwaiter : INotifyCompletion
+{
+    private readonly AsyncCall _task;
+    
+    public AsyncCallAwaiter(AsyncCall task)
+    {
+        _task = task;
+    }
+    
+    public bool IsCompleted => _task.IsCompleted;
+    
+    public void OnCompleted(Action continuation) => _task.RegisterContinuation(continuation);
+}
+
+public class AsyncCallAwaiter<T> : INotifyCompletion
+{
+    private readonly AsyncCall<T> _task;
+    
+    public AsyncCallAwaiter(AsyncCall<T> task)
+    {
+        _task = task;
+    }
+    
+    public bool IsCompleted => _task.IsCompleted;
+    
+    public void OnCompleted(Action continuation) => _task.RegisterContinuation(continuation);
+    
+    public T GetResult() => _task.Result;
+}

+ 5 - 3
src/PixiEditor.Extensions.CommonApi/Palettes/IPaletteProvider.cs

@@ -1,4 +1,6 @@
-namespace PixiEditor.Extensions.CommonApi.Palettes;
+using PixiEditor.Extensions.CommonApi.Async;
+
+namespace PixiEditor.Extensions.CommonApi.Palettes;
 
 
 public interface IPaletteProvider
 public interface IPaletteProvider
 {
 {
@@ -9,7 +11,7 @@ public interface IPaletteProvider
     /// <param name="items">Max amount of palettes to fetch.</param>
     /// <param name="items">Max amount of palettes to fetch.</param>
     /// <param name="filtering">Filtering settings for fetching.</param>
     /// <param name="filtering">Filtering settings for fetching.</param>
     /// <returns>List of palettes.</returns>
     /// <returns>List of palettes.</returns>
-    public Task<List<IPalette>> FetchPalettes(int startIndex, int items, FilteringSettings filtering);
+    public AsyncCall<List<IPalette>> FetchPalettes(int startIndex, int items, FilteringSettings filtering);
 
 
     /// <summary>
     /// <summary>
     ///     Adds a palette to the provider. This means that the palette will be saved in local storage.
     ///     Adds a palette to the provider. This means that the palette will be saved in local storage.
@@ -17,7 +19,7 @@ public interface IPaletteProvider
     /// <param name="palette">Palette to save.</param>
     /// <param name="palette">Palette to save.</param>
     /// <param name="overwrite">If true and palette with the same name exists, it will be overwritten. If false and palette with the same name exists, it will not be added.</param>
     /// <param name="overwrite">If true and palette with the same name exists, it will be overwritten. If false and palette with the same name exists, it will not be added.</param>
     /// <returns>True if adding palette was successful.</returns>
     /// <returns>True if adding palette was successful.</returns>
-    public Task<bool> AddPalette(IPalette palette, bool overwrite = false);
+    public AsyncCall<bool> AddPalette(IPalette palette, bool overwrite = false);
 
 
     /// <summary>
     /// <summary>
     ///     Registers a palette list data source. This means that the provider will use the data source to fetch palettes.
     ///     Registers a palette list data source. This means that the provider will use the data source to fetch palettes.

+ 3 - 2
src/PixiEditor.Extensions.CommonApi/Palettes/PaletteListDataSource.cs

@@ -1,4 +1,5 @@
-using PixiEditor.Extensions.CommonApi.Palettes.Parsers;
+using PixiEditor.Extensions.CommonApi.Async;
+using PixiEditor.Extensions.CommonApi.Palettes.Parsers;
 
 
 namespace PixiEditor.Extensions.CommonApi.Palettes;
 namespace PixiEditor.Extensions.CommonApi.Palettes;
 
 
@@ -21,6 +22,6 @@ public abstract class PaletteListDataSource
     /// <param name="items">Max amount of palettes to fetch.</param>
     /// <param name="items">Max amount of palettes to fetch.</param>
     /// <param name="filtering">Filtering settings for fetching.</param>
     /// <param name="filtering">Filtering settings for fetching.</param>
     /// <returns>A List of palettes. Null if fetch wasn't successful.</returns>
     /// <returns>A List of palettes. Null if fetch wasn't successful.</returns>
-    public abstract Task<List<IPalette>> FetchPaletteList(int startIndex, int items, FilteringSettings filtering);
+    public abstract AsyncCall<List<IPalette>> FetchPaletteList(int startIndex, int items, FilteringSettings filtering);
     public List<PaletteFileParser> AvailableParsers { get; set; }
     public List<PaletteFileParser> AvailableParsers { get; set; }
 }
 }

+ 22 - 0
src/PixiEditor.Extensions.Wasm/Api/Palettes/PaletteProvider.cs

@@ -0,0 +1,22 @@
+using PixiEditor.Extensions.CommonApi.Async;
+using PixiEditor.Extensions.CommonApi.Palettes;
+
+namespace PixiEditor.Extensions.Wasm.Api.Palettes;
+
+public class PaletteProvider : IPaletteProvider
+{
+    public AsyncCall<List<IPalette>> FetchPalettes(int startIndex, int items, FilteringSettings filtering)
+    {
+        throw new NotImplementedException();
+    }
+
+    public AsyncCall<bool> AddPalette(IPalette palette, bool overwrite = false)
+    {
+        throw new NotImplementedException();
+    }
+
+    public void RegisterDataSource(PaletteListDataSource dataSource)
+    {
+        throw new NotImplementedException();
+    }
+}

+ 0 - 4
src/PixiEditor.Extensions.Wasm/PixiEditor.Extensions.Wasm.csproj

@@ -25,10 +25,6 @@
     <PackageReference Include="Microsoft.Build.Utilities.Core" Version="17.8.3"/>
     <PackageReference Include="Microsoft.Build.Utilities.Core" Version="17.8.3"/>
   </ItemGroup>
   </ItemGroup>
 
 
-  <ItemGroup>
-    <Folder Include="Api\Palettes\" />
-  </ItemGroup>
-
   <Target Name="PackTaskDependencies" BeforeTargets="GenerateNuspec">
   <Target Name="PackTaskDependencies" BeforeTargets="GenerateNuspec">
     <ItemGroup>
     <ItemGroup>
       <_PackageFiles Include="build\**" BuildAction="Content" PackagePath="build"/>
       <_PackageFiles Include="build\**" BuildAction="Content" PackagePath="build"/>