mirror of
https://github.com/tgstation/tgstation-server.git
synced 2026-08-26 14:37:44 +01:00
Make Components.InstanceContainer into generic Utils.ReferenceCountingContainer
- Make instance manager fields full types, not interfaces.
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user