// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
using System.Collections.Concurrent;
using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Abstractions.Resource;
using GFramework.Core.Logging;
namespace GFramework.Core.Resource;
///
/// 资源管理器实现,提供资源加载、缓存和卸载功能
/// 线程安全:所有公共方法都是线程安全的
///
public class ResourceManager : IResourceManager
{
///
/// Path 参数验证错误消息常量
///
private const string PathCannotBeNullOrEmptyMessage = "Path cannot be null or whitespace.";
private readonly ResourceCache _cache = new();
private readonly ConcurrentDictionary _loaders = new();
#if NET9_0_OR_GREATER
// net9.0 及以上目标使用专用 Lock,以满足分析器对专用同步原语的建议。
private readonly System.Threading.Lock _loadLock = new();
#else
// net8.0 目标仍回退到 object 锁,以保持多目标编译兼容性。
private readonly object _loadLock = new();
#endif
private readonly ILogger _logger = LoggerFactoryResolver.Provider.CreateLogger(nameof(ResourceManager));
private IResourceReleaseStrategy _releaseStrategy;
///
/// 创建资源管理器
/// 默认使用手动释放策略
///
public ResourceManager()
{
_releaseStrategy = new ManualReleaseStrategy();
}
///
/// 获取已加载资源的数量
///
public int LoadedResourceCount => _cache.Count;
///
/// 同步加载资源
///
public T? Load(string path) where T : class
{
if (string.IsNullOrWhiteSpace(path))
throw new ArgumentException(PathCannotBeNullOrEmptyMessage, nameof(path));
// 检查缓存
var cached = _cache.Get(path);
if (cached != null)
{
return cached;
}
// 加载资源
lock (_loadLock)
{
// 双重检查
cached = _cache.Get(path);
if (cached != null)
{
return cached;
}
var loader = GetLoader();
if (loader == null)
{
throw new InvalidOperationException($"No loader registered for type {typeof(T).Name}");
}
try
{
var resource = loader.Load(path);
_cache.Add(path, resource);
return resource;
}
catch (Exception ex)
{
_logger.Error($"Failed to load resource '{path}'", ex);
return null;
}
}
}
///
/// 异步加载指定路径的资源,并在缓存中进行并发去重。
///
/// 资源类型
/// 资源路径,不能为空或空白。
/// 加载成功返回资源实例;加载失败返回 。
/// 当 为空或空白时抛出。
/// 当未注册对应资源加载器时抛出。
/// 内部使用 ConfigureAwait(false),后续延续不保证回到调用线程。
public async Task LoadAsync(string path) where T : class
{
if (string.IsNullOrWhiteSpace(path))
throw new ArgumentException(PathCannotBeNullOrEmptyMessage, nameof(path));
// 检查缓存
var cached = _cache.Get(path);
if (cached != null)
{
return cached;
}
var loader = GetLoader();
if (loader == null)
{
throw new InvalidOperationException($"No loader registered for type {typeof(T).Name}");
}
try
{
var resource = await loader.LoadAsync(path).ConfigureAwait(false);
lock (_loadLock)
{
// 双重检查
cached = _cache.Get(path);
if (cached != null)
{
// 已经被其他线程加载了,卸载当前加载的资源
loader.Unload(resource);
return cached;
}
_cache.Add(path, resource);
}
return resource;
}
catch (Exception ex)
{
_logger.Error($"Failed to load resource '{path}'", ex);
return null;
}
}
///
/// 获取资源句柄
///
public IResourceHandle? GetHandle(string path) where T : class
{
if (string.IsNullOrWhiteSpace(path))
throw new ArgumentException(PathCannotBeNullOrEmptyMessage, nameof(path));
var resource = _cache.Get(path);
if (resource == null)
return null;
_cache.AddReference(path);
return new ResourceHandle(resource, path, HandleDispose);
}
///
/// 卸载指定路径的资源
///
public bool Unload(string path)
{
if (string.IsNullOrWhiteSpace(path))
throw new ArgumentException(PathCannotBeNullOrEmptyMessage, nameof(path));
lock (_loadLock)
{
var resource = _cache.Get