// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
using System.Reflection;
using System.Threading;
using GFramework.Core.Abstractions.Bases;
using GFramework.Core.Abstractions.Ioc;
using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Abstractions.Systems;
using GFramework.Core.Rule;
using GFramework.Cqrs;
using GFramework.Cqrs.Abstractions.Cqrs;
namespace GFramework.Core.Ioc;
///
/// Microsoft.Extensions.DependencyInjection 适配器
/// 将 Microsoft DI 包装为 IIocContainer 接口实现
/// 提供线程安全的依赖注入容器功能
///
///
/// 该适配器负责维护服务注册表、冻结后的根 以及并发访问控制。
/// 容器释放后会阻止任何进一步访问,并统一抛出 ,
/// 以避免 benchmark、测试宿主或短生命周期架构误用失效的 DI 状态。
///
/// 可选的IServiceCollection实例,默认创建新的ServiceCollection
public class MicrosoftDiContainer(IServiceCollection? serviceCollection = null) : ContextAwareBase, IIocContainer
{
#region Helper Methods
///
/// 抛出统一的容器释放异常,避免并发路径泄露底层锁类型的实现细节。
///
/// 始终抛出,且对象名固定为当前容器类型。
private void ThrowDisposedException()
{
const string objectName = nameof(MicrosoftDiContainer);
_logger.Warn("Attempted to use a disposed MicrosoftDiContainer.");
throw new ObjectDisposedException(objectName);
}
///
/// 检查容器是否已释放,避免访问已经失效的服务提供者与同步原语。
///
/// 当容器已释放时抛出。
private void ThrowIfDisposed()
{
if (!_disposed) return;
ThrowDisposedException();
}
///
/// 进入读锁,并在获取锁前后都复核释放状态,确保等待中的线程也能稳定得到容器级异常。
///
/// 当容器已释放,或等待期间被其他线程释放时抛出。
private void EnterReadLockOrThrowDisposed()
{
var lockTaken = false;
try
{
_lock.EnterReadLock();
lockTaken = true;
}
catch (ObjectDisposedException) when (_disposed)
{
ThrowDisposedException();
}
if (!_disposed)
{
return;
}
if (lockTaken)
{
_lock.ExitReadLock();
}
ThrowDisposedException();
}
///
/// 进入写锁,并在获取锁前后都复核释放状态,确保等待中的线程不会泄露底层锁异常。
///
/// 当容器已释放,或等待期间被其他线程释放时抛出。
private void EnterWriteLockOrThrowDisposed()
{
var lockTaken = false;
try
{
_lock.EnterWriteLock();
lockTaken = true;
}
catch (ObjectDisposedException) when (_disposed)
{
ThrowDisposedException();
}
if (!_disposed)
{
return;
}
if (lockTaken)
{
_lock.ExitWriteLock();
}
ThrowDisposedException();
}
///
/// 在释放标志已经对外可见后,等待遗留 waiter 退场,再尝试释放底层锁。
///
///
/// 容器会先把 置为 并退出写锁,
/// 这样所有已在等待队列中的线程都能醒来并通过统一路径抛出容器级
/// 。只有当这些线程退场后,底层锁才可安全释放。
/// 该步骤只允许一个释放调用者执行,避免并发 重复销毁同一个
/// 并破坏幂等契约。
///
private void DisposeLockWhenQuiescent()
{
if (Interlocked.CompareExchange(ref _lockDisposalStarted, 1, 0) != 0)
{
return;
}
const int maxDisposeSpinAttempts = 512;
var spinWait = new SpinWait();
for (var attempt = 0; attempt < maxDisposeSpinAttempts; attempt++)
{
if (_lock.CurrentReadCount == 0 &&
_lock.WaitingReadCount == 0 &&
_lock.WaitingWriteCount == 0 &&
_lock.WaitingUpgradeCount == 0)
{
try
{
_lock.Dispose();
return;
}
catch (SynchronizationLockException)
{
// 等待中的线程刚好在本轮检查后切换状态;继续自旋直到锁真正静默。
}
}
spinWait.SpinOnce();
}
_logger.Warn("MicrosoftDiContainer lock disposal was skipped because waiters did not quiesce in time.");
}
///
/// 检查容器是否已冻结,如果已冻结则抛出异常
/// 用于保护注册操作的安全性
///
/// 当容器已冻结时抛出
private void ThrowIfFrozen()
{
if (!_frozen) return;
const string errorMsg = "MicrosoftDiContainer is frozen";
_logger.Error(errorMsg);
throw new InvalidOperationException(errorMsg);
}
#endregion
///
/// 记录某个实例在未冻结查询中可见的服务类型分组信息。
///
/// 当前分组对应的服务类型。
/// 该服务类型下的描述符数量。
/// 该服务类型首次出现的位置,用于稳定打破并列。
private sealed record VisibleServiceTypeGroup(Type ServiceType, int Count, int FirstIndex);
#region Fields
///
/// 服务提供者,在容器冻结后构建,用于解析服务实例
///
private IServiceProvider? _provider;
///
/// 冻结后可复用的服务类型可见性索引。
/// 容器冻结后注册集合不再变化,因此 可以安全复用该索引。
///
private FrozenServiceTypeIndex? _frozenServiceTypeIndex;
///
/// 容器冻结状态标志,true表示容器已冻结不可修改
///
private volatile bool _frozen;
///
/// 容器释放状态标志,true 表示容器已释放,不允许继续访问。
///
private volatile bool _disposed;
///
/// 标记底层读写锁的销毁流程是否已经启动,确保并发释放时最多只有一个线程尝试销毁锁实例。
///
private int _lockDisposalStarted;
///
/// 读写锁,确保多线程环境下的线程安全操作
///
private readonly ReaderWriterLockSlim _lock = new(LockRecursionPolicy.NoRecursion);
///
/// 已注册实例的集合,用于快速检查实例是否存在
///
private readonly HashSet