~ruther/NosSmooth.Local

e3484c3c19b7c13eb58121affde55a6e2fb2e310 — Rutherther 2 years ago 1499c7b feat/shared-binding
feat(shared): add part of shared lifetime
A src/Extensions/NosSmooth.Extensions.SharedBinding/EventArgs/ConflictEventArgs.cs => src/Extensions/NosSmooth.Extensions.SharedBinding/EventArgs/ConflictEventArgs.cs +60 -0
@@ 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;

/// <summary>
/// Arguments containing information about a shared instance.
/// The conflict may be resolved by setting the correct property.
/// </summary>
public class ConflictEventArgs : System.EventArgs
{
    /// <summary>
    /// Initializes a new instance of the <see cref="ConflictEventArgs"/> class.
    /// </summary>
    /// <param name="conflictingInstances">All of the conflicting instances.</param>
    /// <param name="defaultResolution">The default method to resolve the conflict.</param>
    public ConflictEventArgs(IReadOnlyList<SharedInstanceInfo> conflictingInstances, ConflictResolution defaultResolution)
    {
        ConflictingInstances = conflictingInstances;
        Resolve = defaultResolution;
    }

    /// <summary>
    /// Gets the instances that are in conflict.
    /// </summary>
    public IReadOnlyList<SharedInstanceInfo> ConflictingInstances { get; }

    /// <summary>
    /// Gets or sets the method of resolution.
    /// </summary>
    public ConflictResolution Resolve { get; set; }

    /// <summary>
    /// Possible methods of resolution.
    /// </summary>
    public enum ConflictResolution
    {
        /// <summary>
        /// Allow the new instance, keep the old ones.
        /// </summary>
        Allow,

        /// <summary>
        /// Do not allow the new instance, keep the old ones.
        /// </summary>
        Restrict,

        /// <summary>
        /// Allow the instance, try to detach the old instance.
        /// In case the old instance cannot be detached, fall back
        /// to <see cref="Restrict"/>.
        /// </summary>
        DetachOriginal
    }
}
\ No newline at end of file

A src/Extensions/NosSmooth.Extensions.SharedBinding/EventArgs/InstanceEventArgs.cs => src/Extensions/NosSmooth.Extensions.SharedBinding/EventArgs/InstanceEventArgs.cs +29 -0
@@ 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;

/// <summary>
/// Arguments containing information about a shared instance.
/// </summary>
public class InstanceEventArgs : System.EventArgs
{
    /// <summary>
    /// Initializes a new instance of the <see cref="InstanceEventArgs"/> class.
    /// </summary>
    /// <param name="instanceInfo">The new instance.</param>
    public InstanceEventArgs(SharedInstanceInfo instanceInfo)
    {
        InstanceInfo = instanceInfo;
    }

    /// <summary>
    /// Gets the information about the new instance.
    /// </summary>
    public SharedInstanceInfo InstanceInfo { get; }
}
\ No newline at end of file

M src/Extensions/NosSmooth.Extensions.SharedBinding/Extensions/ServiceCollectionExtensions.cs => src/Extensions/NosSmooth.Extensions.SharedBinding/Extensions/ServiceCollectionExtensions.cs +4 -0
@@ 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
    /// <returns>The same collection.</returns>
    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<SharedManager>(p => SharedManager.Instance)
            .ShareHooks()
            .TryShare<NosBrowserManager>()

A src/Extensions/NosSmooth.Extensions.SharedBinding/Lifetime/DefaultInstanceLifetime.cs => src/Extensions/NosSmooth.Extensions.SharedBinding/Lifetime/DefaultInstanceLifetime.cs +57 -0
@@ 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();
    }

    /// <inheritdoc/>
    public SharedInstanceInfo Info { get; }

    /// <inheritdoc />
    public CancellationToken InstanceStopping
    {
        get
        {
            _stoppingHooked = true;
            return _stoppingSource.Token;
        }
    }

    /// <inheritdoc />
    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

A src/Extensions/NosSmooth.Extensions.SharedBinding/Lifetime/IInstanceLifetime.cs => src/Extensions/NosSmooth.Extensions.SharedBinding/Lifetime/IInstanceLifetime.cs +20 -0
@@ 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<Result> RequestStop();
}
\ No newline at end of file

A src/Extensions/NosSmooth.Extensions.SharedBinding/Lifetime/SharedInstanceInfo.cs => src/Extensions/NosSmooth.Extensions.SharedBinding/Lifetime/SharedInstanceInfo.cs +11 -0
@@ 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);

A src/Extensions/NosSmooth.Extensions.SharedBinding/Lifetime/SharedLifetime.cs => src/Extensions/NosSmooth.Extensions.SharedBinding/Lifetime/SharedLifetime.cs +53 -0
@@ 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;

/// <summary>
/// Events for shared lifetime.
/// </summary>
public class SharedLifetime
{
    /// <summary>
    /// A new instance has been attached.
    /// </summary>
    public event EventHandler<InstanceEventArgs>? InstanceAttached;

    /// <summary>
    /// A new instance has been attached and a conflict was detected,
    /// the same instance type is already attached.
    /// </summary>
    public event EventHandler<ConflictEventArgs>? ConflictDetected;

    /// <summary>
    /// Initialize a new shared instance.
    /// </summary>
    /// <param name="instanceInfo">The new instance.</param>
    /// <returns>A result, if errorful, the new instance should not start.</returns>
    public Result<CancellationTokenSource> Initialize(SharedInstanceInfo instanceInfo)
    {
        return Result<CancellationTokenSource>.FromSuccess(new CancellationTokenSource());
    }

    /// <summary>
    /// Initialize a new shared instance without its cooperation.
    /// In case the instance is not allowed, an exception will be thrown.
    /// </summary>
    /// <param name="instanceInfo">The shared instance.</param>
    /// <exception cref="Exception">Throws an exception in case the instance cannot be attached.</exception>
    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

M src/Extensions/NosSmooth.Extensions.SharedBinding/SharedManager.cs => src/Extensions/NosSmooth.Extensions.SharedBinding/SharedManager.cs +8 -0
@@ 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,9 42,15 @@ public class SharedManager

    private SharedManager()
    {
        Lifetime = new SharedLifetime();
    }

    /// <summary>
    /// Gets the shared lifetime.
    /// </summary>
    public SharedLifetime Lifetime { get; }

    /// <summary>
    /// Get shared equivalent of the given type.
    /// </summary>
    /// <param name="services">The service provider.</param>


@@ 53,6 60,7 @@ public class SharedManager
    public T GetShared<T>(IServiceProvider services)
        where T : class
    {
        Lifetime.ForceInitialize(services.GetRequiredService<SharedInstanceInfo>());
        if (!_sharedData.ContainsKey(typeof(T)))
        {
            _sharedData[typeof(T)] = CreateShared<T>(services);

Do not follow this link