Make Components.InstanceContainer into generic Utils.ReferenceCountingContainer

- Make instance manager fields full types, not interfaces.
This commit is contained in:
Dominion
2023-04-24 09:39:35 -04:00
parent ca59f4dcf6
commit 7bccd6efbe
3 changed files with 106 additions and 92 deletions
@@ -1,86 +0,0 @@
using System;
using System.Threading.Tasks;
namespace Tgstation.Server.Host.Components
{
/// <summary>
/// Wrapper for managing <see cref="IInstance"/>s.
/// </summary>
sealed class InstanceContainer
{
/// <summary>
/// The <see cref="IInstance"/>.
/// </summary>
public IInstance Instance { get; }
/// <summary>
/// A <see cref="Task"/> that completes when there are no <see cref="IInstanceReference"/>s active for the <see cref="Instance"/>.
/// </summary>
public Task OnZeroReferences
{
get
{
lock (referenceCountLock)
{
if (referenceCount == 0)
return Task.CompletedTask;
return onZeroReferencesTcs.Task;
}
}
}
/// <summary>
/// <see langword="lock"/> <see cref="object"/> for <see cref="referenceCount"/>.
/// </summary>
readonly object referenceCountLock;
/// <summary>
/// Backing <see cref="TaskCompletionSource"/> for <see cref="OnZeroReferences"/>.
/// </summary>
TaskCompletionSource onZeroReferencesTcs;
/// <summary>
/// Count of active <see cref="IInstanceReference"/>s.
/// </summary>
ulong referenceCount;
/// <summary>
/// Initializes a new instance of the <see cref="InstanceContainer"/> class.
/// </summary>
/// <param name="instance">The value of <see cref="Instance"/>.</param>
public InstanceContainer(IInstance instance)
{
Instance = instance ?? throw new ArgumentNullException(nameof(instance));
referenceCountLock = new object();
}
/// <summary>
/// Create a new <see cref="IInstanceReference"/>.
/// </summary>
/// <returns>A new <see cref="IInstanceReference"/>.</returns>
public IInstanceReference AddReference()
{
lock (referenceCountLock)
{
if (referenceCount++ == 0)
onZeroReferencesTcs = new TaskCompletionSource();
try
{
return new InstanceWrapper(Instance, () =>
{
lock (referenceCountLock)
if (--referenceCount == 0)
onZeroReferencesTcs.SetResult();
});
}
catch
{
--referenceCount;
throw;
}
}
}
}
}
@@ -93,14 +93,14 @@ namespace Tgstation.Server.Host.Components
readonly ILogger<InstanceManager> logger;
/// <summary>
/// Map of instance <see cref="EntityId.Id"/>s to respective <see cref="InstanceContainer"/>s. Also used as a <see langword="lock"/> <see cref="object"/>.
/// Map of instance <see cref="EntityId.Id"/>s to the respective <see cref="ReferenceCountingContainer{TWrapped, TReference}"/> for <see cref="IInstance"/>s. Also used as a <see langword="lock"/> <see cref="object"/>.
/// </summary>
readonly IDictionary<long, InstanceContainer> instances;
readonly Dictionary<long, ReferenceCountingContainer<IInstance, IInstanceReference>> instances;
/// <summary>
/// Map of <see cref="DMApiParameters.AccessIdentifier"/>s to their respective <see cref="IBridgeHandler"/>s.
/// </summary>
readonly IDictionary<string, IBridgeHandler> bridgeHandlers;
readonly Dictionary<string, IBridgeHandler> bridgeHandlers;
/// <summary>
/// <see cref="SemaphoreSlim"/> used to guard calls to <see cref="OnlineInstance(Models.Instance, CancellationToken)"/> and <see cref="OfflineInstance(Models.Instance, Models.User, CancellationToken)"/>.
@@ -182,7 +182,7 @@ namespace Tgstation.Server.Host.Components
swarmConfiguration = swarmConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(swarmConfigurationOptions));
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
instances = new Dictionary<long, InstanceContainer>();
instances = new Dictionary<long, ReferenceCountingContainer<IInstance, IInstanceReference>>();
bridgeHandlers = new Dictionary<string, IBridgeHandler>();
readyTcs = new TaskCompletionSource();
instanceStateChangeSemaphore = new SemaphoreSlim(1);
@@ -308,7 +308,7 @@ namespace Tgstation.Server.Host.Components
using var lockContext = await SemaphoreSlimContext.Lock(instanceStateChangeSemaphore, cancellationToken);
logger.LogInformation("Offlining instance ID {instanceId}", metadata.Id);
InstanceContainer container;
ReferenceCountingContainer<IInstance, IInstanceReference> container;
lock (instances)
{
if (!instances.TryGetValue(metadata.Id.Value, out container))
@@ -375,7 +375,11 @@ namespace Tgstation.Server.Host.Components
try
{
lock (instances)
instances.Add(metadata.Id.Value, new InstanceContainer(instance));
instances.Add(
metadata.Id.Value,
new ReferenceCountingContainer<IInstance, IInstanceReference>(
instance,
(wrappedInstance, disposeAction) => new InstanceWrapper(wrappedInstance, disposeAction)));
}
catch (Exception ex)
{
@@ -0,0 +1,96 @@
using System;
using System.Threading.Tasks;
namespace Tgstation.Server.Host.Utils
{
/// <summary>
/// Wrapper for managing some <typeparamref name="TWrapped"/>.
/// </summary>
/// <typeparam name="TWrapped">The type being wrapped.</typeparam>
/// <typeparam name="TReference">The disposable reference type returned.</typeparam>
sealed class ReferenceCountingContainer<TWrapped, TReference>
where TReference : IDisposable
{
/// <summary>
/// The <typeparamref name="TWrapped"/>.
/// </summary>
public TWrapped Instance { get; }
/// <summary>
/// A <see cref="Task"/> that completes when there are no <typeparamref name="TReference"/>s active for the <see cref="Instance"/>.
/// </summary>
public Task OnZeroReferences
{
get
{
lock (referenceCountLock)
{
if (referenceCount == 0)
return Task.CompletedTask;
return onZeroReferencesTcs.Task;
}
}
}
/// <summary>
/// The factory <see cref="Func{T, TResult}"/> for generating <typeparamref name="TReference"/>s to the <see cref="Instance"/>.
/// </summary>
readonly Func<TWrapped, Action, TReference> referenceFactory;
/// <summary>
/// <see langword="lock"/> <see cref="object"/> for <see cref="referenceCount"/>.
/// </summary>
readonly object referenceCountLock;
/// <summary>
/// Backing <see cref="TaskCompletionSource"/> for <see cref="OnZeroReferences"/>.
/// </summary>
TaskCompletionSource onZeroReferencesTcs;
/// <summary>
/// Count of active <see cref="Instance"/>s.
/// </summary>
ulong referenceCount;
/// <summary>
/// Initializes a new instance of the <see cref="ReferenceCountingContainer{TWrapped, TReference}"/> class.
/// </summary>
/// <param name="instance">The value of <see cref="Instance"/>.</param>
/// <param name="referenceFactory">The value of <see cref="referenceFactory"/>.</param>
public ReferenceCountingContainer(TWrapped instance, Func<TWrapped, Action, TReference> referenceFactory)
{
Instance = instance ?? throw new ArgumentNullException(nameof(instance));
this.referenceFactory = referenceFactory ?? throw new ArgumentNullException(nameof(referenceFactory));
referenceCountLock = new object();
}
/// <summary>
/// Create a new <typeparamref name="TReference"/> to the <see cref="Instance"/>.
/// </summary>
/// <returns>A new <typeparamref name="TReference"/>.</returns>
public TReference AddReference()
{
lock (referenceCountLock)
{
if (referenceCount++ == 0)
onZeroReferencesTcs = new TaskCompletionSource();
try
{
return referenceFactory(Instance, () =>
{
lock (referenceCountLock)
if (--referenceCount == 0)
onZeroReferencesTcs.SetResult();
});
}
catch
{
--referenceCount;
throw;
}
}
}
}
}