From e3484c3c19b7c13eb58121affde55a6e2fb2e310 Mon Sep 17 00:00:00 2001 From: Rutherther Date: Tue, 3 Jan 2023 17:06:11 +0100 Subject: [PATCH] feat(shared): add part of shared lifetime --- .../EventArgs/ConflictEventArgs.cs | 60 +++++++++++++++++++ .../EventArgs/InstanceEventArgs.cs | 29 +++++++++ .../Extensions/ServiceCollectionExtensions.cs | 4 ++ .../Lifetime/DefaultInstanceLifetime.cs | 57 ++++++++++++++++++ .../Lifetime/IInstanceLifetime.cs | 20 +++++++ .../Lifetime/SharedInstanceInfo.cs | 11 ++++ .../Lifetime/SharedLifetime.cs | 53 ++++++++++++++++ .../SharedManager.cs | 8 +++ 8 files changed, 242 insertions(+) create mode 100644 src/Extensions/NosSmooth.Extensions.SharedBinding/EventArgs/ConflictEventArgs.cs create mode 100644 src/Extensions/NosSmooth.Extensions.SharedBinding/EventArgs/InstanceEventArgs.cs create mode 100644 src/Extensions/NosSmooth.Extensions.SharedBinding/Lifetime/DefaultInstanceLifetime.cs create mode 100644 src/Extensions/NosSmooth.Extensions.SharedBinding/Lifetime/IInstanceLifetime.cs create mode 100644 src/Extensions/NosSmooth.Extensions.SharedBinding/Lifetime/SharedInstanceInfo.cs create mode 100644 src/Extensions/NosSmooth.Extensions.SharedBinding/Lifetime/SharedLifetime.cs diff --git a/src/Extensions/NosSmooth.Extensions.SharedBinding/EventArgs/ConflictEventArgs.cs b/src/Extensions/NosSmooth.Extensions.SharedBinding/EventArgs/ConflictEventArgs.cs new file mode 100644 index 0000000..5e23334 --- /dev/null +++ b/src/Extensions/NosSmooth.Extensions.SharedBinding/EventArgs/ConflictEventArgs.cs @@ -0,0 +1,60 @@ +// +// ConflictEventArgs.cs +// +// Copyright (c) František Boháček. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using NosSmooth.Extensions.SharedBinding.Lifetime; + +namespace NosSmooth.Extensions.SharedBinding.EventArgs; + +/// +/// Arguments containing information about a shared instance. +/// The conflict may be resolved by setting the correct property. +/// +public class ConflictEventArgs : System.EventArgs +{ + /// + /// Initializes a new instance of the class. + /// + /// All of the conflicting instances. + /// The default method to resolve the conflict. + public ConflictEventArgs(IReadOnlyList conflictingInstances, ConflictResolution defaultResolution) + { + ConflictingInstances = conflictingInstances; + Resolve = defaultResolution; + } + + /// + /// Gets the instances that are in conflict. + /// + public IReadOnlyList ConflictingInstances { get; } + + /// + /// Gets or sets the method of resolution. + /// + public ConflictResolution Resolve { get; set; } + + /// + /// Possible methods of resolution. + /// + public enum ConflictResolution + { + /// + /// Allow the new instance, keep the old ones. + /// + Allow, + + /// + /// Do not allow the new instance, keep the old ones. + /// + Restrict, + + /// + /// Allow the instance, try to detach the old instance. + /// In case the old instance cannot be detached, fall back + /// to . + /// + DetachOriginal + } +} \ No newline at end of file diff --git a/src/Extensions/NosSmooth.Extensions.SharedBinding/EventArgs/InstanceEventArgs.cs b/src/Extensions/NosSmooth.Extensions.SharedBinding/EventArgs/InstanceEventArgs.cs new file mode 100644 index 0000000..dd0d0eb --- /dev/null +++ b/src/Extensions/NosSmooth.Extensions.SharedBinding/EventArgs/InstanceEventArgs.cs @@ -0,0 +1,29 @@ +// +// InstanceEventArgs.cs +// +// Copyright (c) František Boháček. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using NosSmooth.Extensions.SharedBinding.Lifetime; + +namespace NosSmooth.Extensions.SharedBinding.EventArgs; + +/// +/// Arguments containing information about a shared instance. +/// +public class InstanceEventArgs : System.EventArgs +{ + /// + /// Initializes a new instance of the class. + /// + /// The new instance. + public InstanceEventArgs(SharedInstanceInfo instanceInfo) + { + InstanceInfo = instanceInfo; + } + + /// + /// Gets the information about the new instance. + /// + public SharedInstanceInfo InstanceInfo { get; } +} \ No newline at end of file diff --git a/src/Extensions/NosSmooth.Extensions.SharedBinding/Extensions/ServiceCollectionExtensions.cs b/src/Extensions/NosSmooth.Extensions.SharedBinding/Extensions/ServiceCollectionExtensions.cs index a3760de..b08e1df 100644 --- a/src/Extensions/NosSmooth.Extensions.SharedBinding/Extensions/ServiceCollectionExtensions.cs +++ b/src/Extensions/NosSmooth.Extensions.SharedBinding/Extensions/ServiceCollectionExtensions.cs @@ -5,10 +5,12 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Diagnostics; +using System.Reflection; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using NosSmooth.Data.NOSFiles; using NosSmooth.Extensions.SharedBinding.Hooks; +using NosSmooth.Extensions.SharedBinding.Lifetime; using NosSmooth.LocalBinding; using NosSmooth.LocalBinding.Extensions; using NosSmooth.LocalBinding.Hooks; @@ -94,7 +96,9 @@ public static class ServiceCollectionExtensions /// The same collection. public static IServiceCollection ShareNosSmooth(this IServiceCollection serviceCollection) { + var assembly = Assembly.GetCallingAssembly(); return serviceCollection + .AddSingleton(_ => new SharedInstanceInfo(assembly.GetName().FullName, assembly.GetName().Version?.ToString(), assembly)) .AddSingleton(p => SharedManager.Instance) .ShareHooks() .TryShare() diff --git a/src/Extensions/NosSmooth.Extensions.SharedBinding/Lifetime/DefaultInstanceLifetime.cs b/src/Extensions/NosSmooth.Extensions.SharedBinding/Lifetime/DefaultInstanceLifetime.cs new file mode 100644 index 0000000..2a0eb1a --- /dev/null +++ b/src/Extensions/NosSmooth.Extensions.SharedBinding/Lifetime/DefaultInstanceLifetime.cs @@ -0,0 +1,57 @@ +// +// DefaultInstanceLifetime.cs +// +// Copyright (c) František Boháček. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Remora.Results; + +namespace NosSmooth.Extensions.SharedBinding.Lifetime; + +public class DefaultInstanceLifetime : IInstanceLifetime +{ + private readonly CancellationTokenSource _stoppingSource; + private bool _stoppingHooked; + + public DefaultInstanceLifetime() + { + _stoppingSource = new CancellationTokenSource(); + } + + /// + public SharedInstanceInfo Info { get; } + + /// + public CancellationToken InstanceStopping + { + get + { + _stoppingHooked = true; + return _stoppingSource.Token; + } + } + + /// + public Result RequestStop() + { + if (!_stoppingHooked) + { // There is no way anything was hooked to InstanceStopping, thus + // stop won't work for sure. + return new CannotRequestStop() + } + + try + { + _stoppingSource.Cancel(); + return Result.FromSuccess(); + } + catch (TaskCanceledException) + { + return Result.FromSuccess(); + } + catch (Exception e) + { + return e; + } + } +} \ No newline at end of file diff --git a/src/Extensions/NosSmooth.Extensions.SharedBinding/Lifetime/IInstanceLifetime.cs b/src/Extensions/NosSmooth.Extensions.SharedBinding/Lifetime/IInstanceLifetime.cs new file mode 100644 index 0000000..162453e --- /dev/null +++ b/src/Extensions/NosSmooth.Extensions.SharedBinding/Lifetime/IInstanceLifetime.cs @@ -0,0 +1,20 @@ +// +// IInstanceLifetime.cs +// +// Copyright (c) František Boháček. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Remora.Results; + +namespace NosSmooth.Extensions.SharedBinding.Lifetime; + +public interface IInstanceLifetime +{ + public SharedInstanceInfo Info { get; } + + public CancellationToken InstanceStopping { get; } + + public CancellationToken InstanceStopped { get; } + + public Task RequestStop(); +} \ No newline at end of file diff --git a/src/Extensions/NosSmooth.Extensions.SharedBinding/Lifetime/SharedInstanceInfo.cs b/src/Extensions/NosSmooth.Extensions.SharedBinding/Lifetime/SharedInstanceInfo.cs new file mode 100644 index 0000000..cf4cf80 --- /dev/null +++ b/src/Extensions/NosSmooth.Extensions.SharedBinding/Lifetime/SharedInstanceInfo.cs @@ -0,0 +1,11 @@ +// +// SharedInstanceInfo.cs +// +// Copyright (c) František Boháček. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Reflection; + +namespace NosSmooth.Extensions.SharedBinding.Lifetime; + +public record SharedInstanceInfo(string Name, string? Version, Assembly Assembly); diff --git a/src/Extensions/NosSmooth.Extensions.SharedBinding/Lifetime/SharedLifetime.cs b/src/Extensions/NosSmooth.Extensions.SharedBinding/Lifetime/SharedLifetime.cs new file mode 100644 index 0000000..25b69b0 --- /dev/null +++ b/src/Extensions/NosSmooth.Extensions.SharedBinding/Lifetime/SharedLifetime.cs @@ -0,0 +1,53 @@ +// +// SharedLifetime.cs +// +// Copyright (c) František Boháček. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using NosSmooth.Extensions.SharedBinding.EventArgs; +using Remora.Results; + +namespace NosSmooth.Extensions.SharedBinding.Lifetime; + +/// +/// Events for shared lifetime. +/// +public class SharedLifetime +{ + /// + /// A new instance has been attached. + /// + public event EventHandler? InstanceAttached; + + /// + /// A new instance has been attached and a conflict was detected, + /// the same instance type is already attached. + /// + public event EventHandler? ConflictDetected; + + /// + /// Initialize a new shared instance. + /// + /// The new instance. + /// A result, if errorful, the new instance should not start. + public Result Initialize(SharedInstanceInfo instanceInfo) + { + return Result.FromSuccess(new CancellationTokenSource()); + } + + /// + /// Initialize a new shared instance without its cooperation. + /// In case the instance is not allowed, an exception will be thrown. + /// + /// The shared instance. + /// Throws an exception in case the instance cannot be attached. + public void ForceInitialize(SharedInstanceInfo instanceInfo) + { + var result = Initialize(instanceInfo); + + if (!result.IsSuccess) + { + throw new Exception($"Initialization not allowed! {result.Error.Message}"); + } + } +} \ No newline at end of file diff --git a/src/Extensions/NosSmooth.Extensions.SharedBinding/SharedManager.cs b/src/Extensions/NosSmooth.Extensions.SharedBinding/SharedManager.cs index b48623b..3ff0876 100644 --- a/src/Extensions/NosSmooth.Extensions.SharedBinding/SharedManager.cs +++ b/src/Extensions/NosSmooth.Extensions.SharedBinding/SharedManager.cs @@ -7,6 +7,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using NosSmooth.Data.NOSFiles; +using NosSmooth.Extensions.SharedBinding.Lifetime; using NosSmooth.LocalBinding; using NosSmooth.PacketSerializer.Packets; @@ -41,8 +42,14 @@ public class SharedManager private SharedManager() { + Lifetime = new SharedLifetime(); } + /// + /// Gets the shared lifetime. + /// + public SharedLifetime Lifetime { get; } + /// /// Get shared equivalent of the given type. /// @@ -53,6 +60,7 @@ public class SharedManager public T GetShared(IServiceProvider services) where T : class { + Lifetime.ForceInitialize(services.GetRequiredService()); if (!_sharedData.ContainsKey(typeof(T))) { _sharedData[typeof(T)] = CreateShared(services); -- 2.48.1