/* * SPDX-License-Identifier: AGPL-3.0-or-later * Copyright (C) 2025 Sergej Görzen * This file is part of OmiLAXR. */ using System; using System.Collections.Generic; using System.Threading.Tasks; using OmiLAXR.Composers; using UnityEngine; namespace OmiLAXR.Endpoints { /// /// Base class for endpoints with async/await support. /// Ignores the synchronous HandleSending() method and processes everything asynchronously. /// public abstract class AsyncEndpoint : Endpoint { // Always use coroutines instead of threads in async endpoint protected override bool useThreads => false; /// /// Enqueues and starts async sending for a single statement. /// Return value only indicates that processing has been scheduled. /// protected sealed override TransferCode HandleSending(IStatement statement) { _ = HandleSendingInternalAsync(statement); return TransferCode.Queued; } /// /// Internal handler for processing single statements asynchronously. /// Wraps error handling and event triggering on the main thread. /// private async Task HandleSendingInternalAsync(IStatement statement) { try { await HandleSendingAsync(statement); Dispatch(() => TriggerSentStatement(statement)); } catch (Exception ex) { DebugLog.OmiLAXR?.Error($"[AsyncEndpoint] Failed to send: {ex.Message}"); Dispatch(() => TriggerFailedStatement(statement)); } } /// /// Must be implemented by derived classes to send a statement asynchronously. /// protected abstract Task HandleSendingAsync(IStatement statement); /// /// Default batch handling: process all statements in parallel using Task.WhenAll. /// Can be overridden for native batch support. /// protected override TransferCode HandleSending(List batch) { _ = HandleSendingBatchAsync(batch); return TransferCode.Queued; } /// /// Default asynchronous batch handler using parallel statement sending. /// Includes event triggers and error fallback. /// protected virtual async Task HandleSendingBatchAsync(List batch) { try { BeforeHandleSendingBatch(batch); var tasks = new List(); foreach (var s in batch) { TriggerSendingStatement(s); tasks.Add(HandleSendingAsync(s)); } await Task.WhenAll(tasks); AfterHandleSendingBatch(batch); Dispatch(() => TriggerSentBatch(batch)); } catch (Exception ex) { Debug.LogException(ex); foreach (var s in batch) { Dispatch(() => TriggerFailedStatement(s)); QueuedStatements.Enqueue(s); // Optional requeue } Dispatch(() => TriggerFailedBatch(batch)); } } /// /// Runs the given action on the Unity main thread. /// Useful for creating Unity objects or accessing Unity APIs. /// protected Task MainThreadAsync(Action action) { var tcs = new TaskCompletionSource(); Dispatch(() => { try { action(); tcs.SetResult(true); } catch (Exception ex) { tcs.SetException(ex); } }); return tcs.Task; } /// /// Runs the given function on the Unity main thread and returns the result. /// protected Task MainThreadAsync(Func func) { var tcs = new TaskCompletionSource(); Dispatch(() => { try { tcs.SetResult(func()); } catch (Exception ex) { tcs.SetException(ex); } }); return tcs.Task; } } }