Skip to content

Commit

Permalink
Merge branch 'dev' into client
Browse files Browse the repository at this point in the history
# Conflicts:
#	Hast.Core
  • Loading branch information
Piedone committed Oct 27, 2021
2 parents 42eb2fa + 29f268b commit d2cb473
Show file tree
Hide file tree
Showing 13 changed files with 236 additions and 93 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ publish/
# While there are files in the Container folder that are in the repo, a set up container's files shouldn't be.
/Hast.Abstractions/Hast.Vitis.Abstractions/Container/
TestResults/*.trx
/global.json
5 changes: 5 additions & 0 deletions Docs/ReleaseNotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
Note that the Hardware Framework projects have their own release cycle and release notes.


## v1.2.1, 27.10.2021

Bugfix release to make Hastlayer Remote Services reliably work. If you tried to connect to Hastlayer recently, and didn't get the transformation completed, this fixes it.


## v1.2, 04.10.2021

- Added support for the high-end [Xilinx Alveo U50, U200, U250 or U280 Data Center Accelerator Cards](https://www.xilinx.com/products/boards-and-kits/alveo.html). These are suitable hardware for any kind of demanding production-level workload. Apart from using such devices on-premise they're also available in the cloud.
Expand Down
17 changes: 14 additions & 3 deletions Hast.Common/Services/AppDataFolder.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.IO;
using System.Reflection;

Expand Down Expand Up @@ -39,8 +39,19 @@ public AppDataFolder(string appDataFolderPath)
public string Combine(params string[] parts) => MapPath(Path.Combine(parts));

public bool FileExists(string fileName) => File.Exists(MapPath(fileName));
public FileStream CreateFile(string fileName) => File.Create(MapPath(fileName));

public FileStream CreateFile(string fileName)
{
var directoryName = Path.GetDirectoryName(fileName);
if (!Directory.Exists(directoryName)) Directory.CreateDirectory(directoryName);
return File.Create(MapPath(fileName));
}

public FileStream OpenFile(string fileName) => File.OpenRead(MapPath(fileName));
public void DeleteFile(string fileName) => File.Delete(MapPath(fileName));

public void DeleteFile(string fileName)
{
if (File.Exists(fileName)) File.Delete(MapPath(fileName));
}
}
}
47 changes: 37 additions & 10 deletions Hast.Common/Services/DisposableContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,58 @@
namespace Hast.Common.Services
{
/// <summary>
/// Contains a value of T that is attached to an <see cref="IDisposable"/> context while manages their lifecylce together.
/// Contains a values that are attached to an <see cref="IDisposable"/> context while manages their lifecycle
/// together.
/// </summary>
/// <typeparam name="T">The type of the value wich is exposed.</typeparam>
public sealed class DisposableContainer<T> : IDisposable
public class DisposableContainer : IDisposable
{
private bool _disposed = false;
private bool _disposed;
private IDisposable _context;

public T Value { get; }
public object[] Values { get; }


public DisposableContainer(IDisposable context, T value)
public DisposableContainer(IDisposable context, params object[] values)
{
_context = context;
Value = value;
Values = values;
}

public void Dispose()
{
if (_disposed) return;

_context?.Dispose();
_context = null;
if (Value is IDisposable disposableValue) disposableValue.Dispose();

foreach (var value in Values)
{
if (value is IDisposable disposableValue)
{
disposableValue.Dispose();
}
}

_disposed = true;
}
}

/// <inheritdoc cref="DisposableContainer"/>
public sealed class DisposableContainer<T> : DisposableContainer
{
public T Value => (T)Values[0];

public DisposableContainer(IDisposable context, T value) : base(context, value) { }
}

/// <inheritdoc cref="DisposableContainer"/>
public sealed class DisposableContainer<T1, T2> : DisposableContainer
{
public DisposableContainer(IDisposable context, T1 value1, T2 value2) : base(context, value1, value2) { }

public void Deconstruct(out T1 first, out T2 second)
{
first = (T1)Values[0];
second = (T2)Values[1];
}
}
}
21 changes: 21 additions & 0 deletions Hast.Layer/Extensibility/Events/ITransformationEvents.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using Hast.Transformer.Abstractions;
using System.Threading.Tasks;

namespace Hast.Layer.Extensibility.Events
{
/// <summary>
/// Events to be executed before and after transformation inside <see cref="Hastlayer.GenerateHardware"/>.
/// </summary>
public interface ITransformationEvents
{
/// <summary>
/// Executes before <see cref="ITransformer.Transform"/> may be called.
/// </summary>
Task BeforeTransformAsync() => Task.CompletedTask;

/// <summary>
/// Executes after <see cref="ITransformer.Transform"/> may be called.
/// </summary>
Task AfterTransformAsync(IHardwareDescription hardwareDescription) => Task.CompletedTask;
}
}
112 changes: 72 additions & 40 deletions Hast.Layer/Hastlayer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,31 @@
using Hast.Common.Services;
using Hast.Common.Validation;
using Hast.Communication;
using Hast.Communication.Services;
using Hast.Layer.EmptyRepresentationFactories;
using Hast.Layer.Extensibility.Events;
using Hast.Layer.Models;
using Hast.Synthesis.Abstractions;
using Hast.Transformer.Abstractions;
using Hast.Transformer.Abstractions.SimpleMemory;
using Hast.Xilinx.Abstractions.ManifestProviders;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Linq;
using NLog.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Hast.Transformer.Abstractions.SimpleMemory;
using Newtonsoft.Json.Linq;

namespace Hast.Layer
{
public sealed class Hastlayer : IHastlayer
{
public const string AppsettingsJsonFileName = "appsettings.json";

private readonly IHastlayerConfiguration _configuration;
private readonly ServiceProvider _serviceProvider;
private readonly HashSet<string> _serviceNames;
Expand All @@ -49,7 +50,7 @@ private Hastlayer(IHastlayerConfiguration configuration)
typeof(IHardwareImplementationComposer).Assembly,
typeof(ITransformer).Assembly,
typeof(NexysA7ManifestProvider).Assembly,
typeof(CatapultManifestProvider).Assembly
typeof(CatapultManifestProvider).Assembly,
});
assemblies.AddRange(GetHastLibraries());

Expand All @@ -61,18 +62,7 @@ private Hastlayer(IHastlayerConfiguration configuration)
services.AddScoped<IHardwareGenerationConfigurationAccessor, HardwareGenerationConfigurationAccessor>();
services.AddIDependencyContainer(assemblies);

services.AddSingleton(LoggerFactory.Create(builder =>
{
if (configuration.ConfigureLogging is null)
{
builder.AddNLog("NLog.config");
}
else
{
configuration.ConfigureLogging(builder);
}
}));
services.AddSingleton(provider => provider.GetService<ILoggerFactory>().CreateLogger("hastlayer"));
ConfigureLogging(services, configuration.ConfigureLogging);

configuration.OnServiceRegistration?.Invoke(configuration, services);

Expand Down Expand Up @@ -109,6 +99,22 @@ private Hastlayer(IHastlayerConfiguration configuration)
}
}

public static void ConfigureLogging(IServiceCollection services, Action<ILoggingBuilder> configureLogging = null)
{
services.AddSingleton(LoggerFactory.Create(builder =>
{
if (configureLogging is null)
{
builder.AddNLog("NLog.config");
}
else
{
configureLogging(builder);
}
}));

services.AddSingleton(provider => provider.GetService<ILoggerFactory>().CreateLogger("hastlayer"));
}

public static IHastlayer Create() => Create(HastlayerConfiguration.Default);

Expand All @@ -133,8 +139,9 @@ public static IHastlayer Create(IHastlayerConfiguration configuration)

public static IConfiguration BuildConfiguration() =>
new ConfigurationBuilder()
.AddJsonFile("appsettings.json", true, true)
.AddJsonFile(AppsettingsJsonFileName, optional: true, reloadOnChange: false)
.AddEnvironmentVariables()
.AddUserSecrets(Assembly.GetEntryAssembly(), optional: true)
.AddCommandLine(Environment.GetCommandLineArgs())
.Build();

Expand Down Expand Up @@ -167,13 +174,15 @@ public async Task<IHardwareRepresentation> GenerateHardware(
// This is fine because IHardwareRepresentation doesn't contain anything that relies on the scope.
using (var scope = _serviceProvider.CreateScope())
{
scope.ServiceProvider.GetRequiredService<IHardwareGenerationConfigurationAccessor>()
var provider = scope.ServiceProvider;
provider.GetRequiredService<IHardwareGenerationConfigurationAccessor>()
.Value = configuration;

var transformer = scope.ServiceProvider.GetRequiredService<ITransformer>();
var deviceManifestSelector = scope.ServiceProvider.GetRequiredService<IDeviceManifestSelector>();
var loggerService = scope.ServiceProvider.GetRequiredService<ILogger<Hastlayer>>();
var appConfiguration = scope.ServiceProvider.GetRequiredService<IConfiguration>();
var transformer = provider.GetRequiredService<ITransformer>();
var deviceManifestSelector = provider.GetRequiredService<IDeviceManifestSelector>();
var loggerService = provider.GetRequiredService<ILogger<Hastlayer>>();
var appConfiguration = provider.GetRequiredService<IConfiguration>();
var transformationEvents = provider.GetRequiredService<IEnumerable<ITransformationEvents>>().ToList();

// Load any not-yet-populated configuration with appsettings > HardwareGenerationConfiguration >
// CustomConfiguration into the current hardware generation configuration.
Expand All @@ -190,10 +199,20 @@ public async Task<IHardwareRepresentation> GenerateHardware(
[item.Key];
}

foreach (var transformationEvent in transformationEvents)
{
await transformationEvent.BeforeTransformAsync();
}

var hardwareDescription = configuration.EnableHardwareTransformation ?
await transformer.Transform(assembliesPaths, configuration) :
EmptyHardwareDescriptionFactory.Create(configuration);

foreach (var transformationEvent in transformationEvents)
{
await transformationEvent.AfterTransformAsync(hardwareDescription);
}

foreach (var warning in hardwareDescription.Warnings)
{
loggerService.LogWarning(
Expand All @@ -213,7 +232,7 @@ await transformer.Transform(assembliesPaths, configuration) :
}

var hardwareImplementationComposerSelector =
scope.ServiceProvider.GetRequiredService<IHardwareImplementationComposerSelector>();
provider.GetRequiredService<IHardwareImplementationComposerSelector>();

IHardwareImplementation hardwareImplementation;
if (configuration.EnableHardwareImplementationComposition && configuration.EnableHardwareTransformation)
Expand All @@ -222,7 +241,7 @@ await transformer.Transform(assembliesPaths, configuration) :
{
Configuration = configuration,
HardwareDescription = hardwareDescription,
DeviceManifest = deviceManifest
DeviceManifest = deviceManifest,
};

var hardwareImplementationComposer = hardwareImplementationComposerSelector
Expand All @@ -240,7 +259,7 @@ await transformer.Transform(assembliesPaths, configuration) :
HardwareDescription = hardwareDescription,
HardwareImplementation = hardwareImplementation,
DeviceManifest = deviceManifest,
HardwareGenerationConfiguration = configuration
HardwareGenerationConfiguration = configuration,
};
}
}
Expand Down Expand Up @@ -282,16 +301,31 @@ public async Task<T> GenerateProxy<T>(
}
}

public DisposableContainer<ICommunicationService> GetCommunicationService(string communicationChannelName)
public DisposableContainer<TServiceInterface> GetService<TServiceInterface>()
{
IServiceScope scope = null;
try
{
scope = _serviceProvider.CreateScope();
var service = scope.ServiceProvider.GetService<TServiceInterface>();
return new DisposableContainer<TServiceInterface>(scope, service);
}
catch
{
scope?.Dispose();
throw;
}
}

public DisposableContainer<TServiceInterface1, TServiceInterface2> GetServices<TServiceInterface1, TServiceInterface2>()
{
IServiceScope scope = null;
try
{
scope = _serviceProvider.CreateScope();
var communicationService = scope.ServiceProvider
.GetService<ICommunicationServiceSelector>()
.GetCommunicationService(communicationChannelName);
return new DisposableContainer<ICommunicationService>(scope, communicationService);
var service1 = scope.ServiceProvider.GetService<TServiceInterface1>();
var service2 = scope.ServiceProvider.GetService<TServiceInterface2>();
return new DisposableContainer<TServiceInterface1, TServiceInterface2>(scope, service1, service2);
}
catch
{
Expand Down Expand Up @@ -322,30 +356,28 @@ public IMemoryConfiguration CreateMemoryConfiguration(IHardwareRepresentation ha
public IEnumerable<IDeviceManifest> GetSupportedDevices()
{
// This is fine because IDeviceManifest doesn't contain anything that relies on the scope.
using (var scope = _serviceProvider.CreateScope())
{
return scope.ServiceProvider.GetService<IDeviceManifestSelector>().GetSupportedDevices();
}
using var scope = _serviceProvider.CreateScope();
return scope.ServiceProvider.GetService<IDeviceManifestSelector>().GetSupportedDevices();
}

public async Task RunAsync<T>(Func<T, Task> process)
{
using (var scope = _serviceProvider.CreateScope())
await process(scope.ServiceProvider.GetRequiredService<T>());
using var scope = _serviceProvider.CreateScope();
await process(scope.ServiceProvider.GetRequiredService<T>());
}

public async Task<TOut> RunGetAsync<TOut>(Func<IServiceProvider, Task<TOut>> process)
{
ThrowIfService<TOut>();
using (var scope = _serviceProvider.CreateScope())
return await process(scope.ServiceProvider);
using var scope = _serviceProvider.CreateScope();
return await process(scope.ServiceProvider);
}

public TOut RunGet<TOut>(Func<IServiceProvider, TOut> process)
{
ThrowIfService<TOut>();
using (var scope = _serviceProvider.CreateScope())
return process(scope.ServiceProvider);
using var scope = _serviceProvider.CreateScope();
return process(scope.ServiceProvider);
}

public ILogger<T> GetLogger<T>() => _serviceProvider.GetService<ILogger<T>>();
Expand Down
12 changes: 9 additions & 3 deletions Hast.Layer/IHastLayer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,17 @@ Task<T> GenerateProxy<T>(
IProxyGenerationConfiguration configuration = null) where T : class;

/// <summary>
/// Gets the <see cref="ICommunicationService"/> based on the channel name.
/// Gets a registered service for the interface <typeparamref name="TServiceInterface"/> inside a disposable
/// container that maintains an independent dependency injection scope as long as the host <see
/// cref="IHastlayer"/> object isn't disposed.
/// </summary>
/// <param name="communicationChannelName">The <see cref="ICommunicationService.ChannelName"/> value.</param>
/// <returns>The matching communication service.</returns>
DisposableContainer<ICommunicationService> GetCommunicationService(string communicationChannelName);
/// <remarks>
/// <para>
/// If you need a communication service, use this to request an <see cref="ICommunicationServiceSelector"/>.
/// </para>
/// </remarks>
DisposableContainer<TServiceInterface> GetService<TServiceInterface>();

/// <summary>
/// Constructs a new <see cref="SimpleMemory"/> object that represents a simplified memory model available on
Expand Down
Loading

0 comments on commit d2cb473

Please sign in to comment.