[M] new call back
[wip]
This commit is contained in:
210
sdk-intergration/Packages/tysdk/Runtime/TYSDKCallbackManager.cs
Normal file
210
sdk-intergration/Packages/tysdk/Runtime/TYSDKCallbackManager.cs
Normal file
@@ -0,0 +1,210 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace tysdk
|
||||
{
|
||||
/// <summary>
|
||||
/// 线程安全的异步Callback管理器
|
||||
/// 解决原有callback系统的线程安全、内存泄漏和超时问题
|
||||
/// </summary>
|
||||
public class TYSDKCallbackManager : IDisposable
|
||||
{
|
||||
private static readonly Lazy<TYSDKCallbackManager> _instance =
|
||||
new Lazy<TYSDKCallbackManager>(() => new TYSDKCallbackManager());
|
||||
|
||||
public static TYSDKCallbackManager Instance => _instance.Value;
|
||||
|
||||
private readonly ConcurrentDictionary<string, CallbackEntry> _callbacks = new();
|
||||
private readonly object _lock = new();
|
||||
private bool _disposed = false;
|
||||
|
||||
private TYSDKCallbackManager() { }
|
||||
|
||||
/// <summary>
|
||||
/// 注册一个callback并返回Task
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Callback数据类型</typeparam>
|
||||
/// <param name="callbackType">Callback类型标识</param>
|
||||
/// <param name="timeout">超时时间,默认15 sec</param>
|
||||
/// <returns>Task用于等待callback结果</returns>
|
||||
public Task<T> RegisterCallback<T>(TimeSpan? timeout = null) where T : class
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException(nameof(TYSDKCallbackManager));
|
||||
|
||||
var callbackId = typeof(T).Name;
|
||||
|
||||
if (_callbacks.TryGetValue(callbackId, out var entry))
|
||||
{
|
||||
UnityEngine.Debug.Log($"[TYSDKCallbackManager] Registered callback exists {callbackId}");
|
||||
return entry.Task as Task<T>;
|
||||
}
|
||||
|
||||
var tcs = new TaskCompletionSource<T>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
var actualTimeout = timeout ?? TimeSpan.FromSeconds(15);
|
||||
var cts = new CancellationTokenSource(actualTimeout);
|
||||
|
||||
entry = new CallbackEntry<T>
|
||||
{
|
||||
TaskCompletionSource = tcs,
|
||||
CancellationTokenSource = cts,
|
||||
};
|
||||
|
||||
// 注册到字典
|
||||
_callbacks[callbackId] = entry;
|
||||
|
||||
// 设置超时取消
|
||||
cts.Token.Register(() =>
|
||||
{
|
||||
if (tcs.TrySetCanceled())
|
||||
{
|
||||
UnityEngine.Debug.LogWarning($"[TYSDKCallbackManager] Callback {callbackId} timed out after {actualTimeout.TotalSeconds}s");
|
||||
}
|
||||
_callbacks.TryRemove(callbackId, out _);
|
||||
});
|
||||
|
||||
UnityEngine.Debug.Log($"[TYSDKCallbackManager] Registered callback {callbackId}");
|
||||
return tcs.Task;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 触发callback
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Callback数据类型</typeparam>
|
||||
/// <param name="callbackType">Callback类型标识</param>
|
||||
/// <param name="callbackData">Callback数据</param>
|
||||
/// <returns>是否成功触发callback</returns>
|
||||
public bool TryTriggerCallback<T>(T callbackData) where T : class
|
||||
{
|
||||
if (_disposed || callbackData == null)
|
||||
return false;
|
||||
|
||||
var key = typeof(T).Name;
|
||||
if (!_callbacks.TryGetValue(key, out var entry))
|
||||
{
|
||||
UnityEngine.Debug.LogWarning($"[TYSDKCallbackManager] No pending callback found for type: {key}");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (entry == null)
|
||||
{
|
||||
_callbacks.TryRemove(key, out _);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (entry.Task.IsCompleted)
|
||||
{
|
||||
entry.CancellationTokenSource?.Cancel();
|
||||
_callbacks.TryRemove(key, out _);
|
||||
return false;
|
||||
}
|
||||
|
||||
var actualEntry = entry as CallbackEntry<T>;
|
||||
|
||||
if (actualEntry == null)
|
||||
{
|
||||
UnityEngine.Debug.LogError($"[TYSDKCallbackManager] Callback type mismatch: Expected {typeof(T).Name}, but got {entry.GetType().Name}");
|
||||
entry.CancellationTokenSource?.Cancel();
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// 尝试设置结果
|
||||
if (actualEntry.TaskCompletionSource.TrySetResult(callbackData))
|
||||
{
|
||||
actualEntry.CancellationTokenSource?.Cancel();
|
||||
UnityEngine.Debug.Log($"[TYSDKCallbackManager] Successfully triggered callback {key}");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
UnityEngine.Debug.LogError($"[TYSDKCallbackManager] Error triggering callback {key}: {ex.Message}\n{ex.StackTrace}");
|
||||
actualEntry.TaskCompletionSource.TrySetException(ex);
|
||||
actualEntry.CancellationTokenSource?.Cancel();
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 取消指定类型的所有callback
|
||||
/// </summary>
|
||||
/// <param name="callbackType">Callback类型</param>
|
||||
/// <param name="reason">取消原因</param>
|
||||
public void CancelCallbacks<T>()
|
||||
{
|
||||
var key = typeof(T).Name;
|
||||
|
||||
if (!_callbacks.TryGetValue(key, out var entryToRemove))
|
||||
{
|
||||
UnityEngine.Debug.LogWarning($"[TYSDKCallbackManager] No pending callback found for type: {key}");
|
||||
return;
|
||||
}
|
||||
|
||||
entryToRemove.Cancel();
|
||||
}
|
||||
|
||||
public int GetCallbackNum()
|
||||
{
|
||||
return _callbacks?.Count ?? 0;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed) return;
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
if (_disposed) return;
|
||||
_disposed = true;
|
||||
|
||||
// 取消所有pending callbacks
|
||||
foreach (var entry in _callbacks.Values)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!entry.Task.IsCompleted)
|
||||
{
|
||||
entry.Cancel();
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 忽略取消时的异常
|
||||
}
|
||||
|
||||
entry.CancellationTokenSource?.Dispose();
|
||||
}
|
||||
|
||||
_callbacks.Clear();
|
||||
UnityEngine.Debug.Log("[TYSDKCallbackManager] Disposed");
|
||||
}
|
||||
}
|
||||
|
||||
private abstract class CallbackEntry
|
||||
{
|
||||
public CancellationTokenSource CancellationTokenSource { get; set; } = null!;
|
||||
public abstract Task Task { get; }
|
||||
public abstract void Cancel();
|
||||
}
|
||||
|
||||
private class CallbackEntry<T> : CallbackEntry where T : class
|
||||
{
|
||||
public TaskCompletionSource<T> TaskCompletionSource { get; set; } = null!;
|
||||
public override Task Task => TaskCompletionSource.Task;
|
||||
|
||||
public override void Cancel()
|
||||
{
|
||||
TaskCompletionSource.TrySetCanceled();
|
||||
CancellationTokenSource?.Cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user