Tweak how the Horizon class and service manager work

- Store IServiceObjects in the service manager that return the usable objects instead of storing the objects directly
- Register FS services in the service manager instead of giving them special treatment
- Give each created HorizonClient its own "process ID"
This commit is contained in:
Alex Barney 2020-08-23 14:37:08 -07:00
parent deaf111ac3
commit 071b608f5f
15 changed files with 164 additions and 111 deletions

View file

@ -34,7 +34,7 @@ namespace LibHac.Bcat
IServiceCreator service = GetServiceCreator(type);
Result rc = Hos.Sm.RegisterService(service, name);
Result rc = Hos.Sm.RegisterService(new BcatServiceObject(service), name);
if (rc.IsFailure())
{
throw new HorizonResultException(rc, "Abort");

View file

@ -0,0 +1,20 @@
using LibHac.Sm;
namespace LibHac.Bcat.Detail.Ipc
{
internal class BcatServiceObject : IServiceObject
{
private IServiceCreator _serviceCreator;
public BcatServiceObject(IServiceCreator serviceCreator)
{
_serviceCreator = serviceCreator;
}
public Result GetServiceObject(out object serviceObject)
{
serviceObject = _serviceCreator;
return Result.Success;
}
}
}

View file

@ -2,6 +2,7 @@
using System.Diagnostics;
using System.Runtime.InteropServices;
using LibHac.Common;
using LibHac.Diag;
using LibHac.Fs.Accessors;
using LibHac.Fs.Fsa;
using LibHac.FsSrv;
@ -11,7 +12,7 @@ namespace LibHac.Fs
{
public partial class FileSystemClient
{
private FileSystemServer FsSrv { get; }
private HorizonClient Hos { get; }
private IFileSystemProxy FsProxy { get; set; }
private readonly object _fspInitLocker = new object();
@ -26,22 +27,17 @@ namespace LibHac.Fs
Time = timer ?? new StopWatchTimeSpanGenerator();
}
public FileSystemClient(FileSystemServer fsServer, ITimeSpanGenerator timer)
public FileSystemClient(HorizonClient horizonClient)
{
FsSrv = fsServer;
Time = timer ?? new StopWatchTimeSpanGenerator();
}
Hos = horizonClient;
Time = horizonClient.Time;
internal FileSystemClient(FileSystemServer fsServer, IFileSystemProxy fsProxy, ITimeSpanGenerator timer)
{
FsSrv = fsServer;
FsProxy = fsProxy;
Time = timer ?? new StopWatchTimeSpanGenerator();
Assert.NotNull(Time);
}
public bool HasFileSystemServer()
{
return FsSrv != null;
return Hos != null;
}
public IFileSystemProxy GetFileSystemProxyServiceObject()
@ -57,8 +53,16 @@ namespace LibHac.Fs
throw new InvalidOperationException("Client was not initialized with a server object.");
}
FsProxy = FsSrv.CreateFileSystemProxyService();
Result rc = Hos.Sm.GetService(out IFileSystemProxy fsProxy, "fsp-srv");
if (rc.IsFailure())
{
throw new HorizonResultException(rc, "Failed to create file system proxy service object.");
}
fsProxy.SetCurrentProcess(Hos.Os.GetCurrentProcessId().Value).IgnoreResult();
FsProxy = fsProxy;
return FsProxy;
}
}

View file

@ -15,7 +15,7 @@ namespace LibHac.FsSrv
public class FileSystemProxy : IFileSystemProxy
{
private FileSystemProxyCore FsProxyCore { get; }
internal FileSystemServer FsServer { get; }
internal HorizonClient Hos { get; }
public ulong CurrentProcess { get; private set; }
@ -24,10 +24,10 @@ namespace LibHac.FsSrv
public FsPath SaveDataRootPath { get; }
public bool AutoCreateSaveData { get; private set; }
internal FileSystemProxy(FileSystemProxyCore fsProxyCore, FileSystemServer fsServer)
internal FileSystemProxy(HorizonClient horizonClient, FileSystemProxyCore fsProxyCore)
{
FsProxyCore = fsProxyCore;
FsServer = fsServer;
Hos = horizonClient;
CurrentProcess = ulong.MaxValue;
SaveDataSize = 0x2000000;

View file

@ -3,6 +3,7 @@ using LibHac.Fs;
using LibHac.Fs.Impl;
using LibHac.Fs.Shim;
using LibHac.FsSrv.Creators;
using LibHac.Sm;
namespace LibHac.FsSrv
{
@ -13,15 +14,17 @@ namespace LibHac.FsSrv
private FileSystemProxyCore FsProxyCore { get; }
/// <summary>The client instance to be used for internal operations like save indexer access.</summary>
public FileSystemClient FsClient { get; }
public HorizonClient Hos { get; }
public bool IsDebugMode { get; }
private ITimeSpanGenerator Timer { get; }
/// <summary>
/// Creates a new <see cref="FileSystemServer"/>.
/// Creates a new <see cref="FileSystemServer"/> and registers its services using the provided HOS client.
/// </summary>
/// <param name="horizonClient">The <see cref="HorizonClient"/> that will be used by this server.</param>
/// <param name="config">The configuration for the created <see cref="FileSystemServer"/>.</param>
public FileSystemServer(FileSystemServerConfig config)
public FileSystemServer(HorizonClient horizonClient, FileSystemServerConfig config)
{
if (config.FsCreators == null)
throw new ArgumentException("FsCreators must not be null");
@ -29,6 +32,8 @@ namespace LibHac.FsSrv
if (config.DeviceOperator == null)
throw new ArgumentException("DeviceOperator must not be null");
Hos = horizonClient;
IsDebugMode = false;
ExternalKeySet externalKeySet = config.ExternalKeySet ?? new ExternalKeySet();
@ -41,17 +46,19 @@ namespace LibHac.FsSrv
};
FsProxyCore = new FileSystemProxyCore(fspConfig, externalKeySet, config.DeviceOperator);
var fsProxy = new FileSystemProxy(FsProxyCore, this);
FsClient = new FileSystemClient(this, fsProxy, Timer);
// NS usually takes care of this
if (FsClient.IsSdCardInserted())
FsClient.SetSdCardAccessibility(true);
FsProxyCore.SetSaveDataIndexerManager(new SaveDataIndexerManager(FsClient, SaveIndexerId,
FsProxyCore.SetSaveDataIndexerManager(new SaveDataIndexerManager(Hos.Fs, SaveIndexerId,
new ArrayPoolMemoryResource(), new SdHandleManager(), false));
FileSystemProxy fsProxy = GetFileSystemProxyServiceObject();
fsProxy.SetCurrentProcess(Hos.Os.GetCurrentProcessId().Value).IgnoreResult();
fsProxy.CleanUpTemporaryStorage().IgnoreResult();
Hos.Sm.RegisterService(new FileSystemProxyService(this), "fsp-srv").IgnoreResult();
// NS usually takes care of this
if (Hos.Fs.IsSdCardInserted())
Hos.Fs.SetSdCardAccessibility(true);
}
/// <summary>
@ -69,12 +76,35 @@ namespace LibHac.FsSrv
/// <returns>The created <see cref="FileSystemClient"/>.</returns>
public FileSystemClient CreateFileSystemClient(ITimeSpanGenerator timer)
{
return new FileSystemClient(this, timer);
return new FileSystemClient(Hos);
}
public IFileSystemProxy CreateFileSystemProxyService()
private FileSystemProxy GetFileSystemProxyServiceObject()
{
return new FileSystemProxy(FsProxyCore, this);
return new FileSystemProxy(Hos, FsProxyCore);
}
internal bool IsCurrentProcess(ulong processId)
{
ulong currentId = Hos.Os.GetCurrentProcessId().Value;
return processId == currentId;
}
private class FileSystemProxyService : IServiceObject
{
private readonly FileSystemServer _server;
public FileSystemProxyService(FileSystemServer server)
{
_server = server;
}
public Result GetServiceObject(out object serviceObject)
{
serviceObject = _server.GetFileSystemProxyServiceObject();
return Result.Success;
}
}
}

View file

@ -109,7 +109,7 @@ namespace LibHac.FsSrv.Impl
return rc;
}
rc = FsProxy.FsServer.FsClient.CreateSystemSaveData(SaveDataId, SaveDataSize, SaveJournalSize,
rc = FsProxy.Hos.Fs.CreateSystemSaveData(SaveDataId, SaveDataSize, SaveJournalSize,
SaveDataFlags.None);
if (rc.IsFailure()) return rc;
}

View file

@ -183,12 +183,6 @@ namespace LibHac.FsSrv.Impl
return initialProcessIdLowerBound >= processId && processId <= initialProcessIdUpperBound;
}
public static bool IsCurrentProcess(ulong processId)
{
// Todo: Don't use hardcoded value
return true;
}
internal static ProgramInfo CreateProgramInfoForInitialProcess(FileSystemServer fsServer)
{
return new ProgramInfo(fsServer, InitialProcessAccessControlDataHeader,

View file

@ -1,7 +1,7 @@
using LibHac.Common;
using LibHac.Fs;
using System.Threading;
using LibHac.Bcat;
using LibHac.FsSrv;
using LibHac.FsSrv.Creators;
using LibHac.Os;
using LibHac.Sm;
namespace LibHac
@ -9,63 +9,32 @@ namespace LibHac
public class Horizon
{
internal ITimeSpanGenerator Time { get; }
private FileSystemServer FileSystemServer { get; set; }
internal ServiceManager ServiceManager { get; }
private readonly object _initLocker = new object();
// long instead of ulong because the ulong Interlocked.Increment overload
// wasn't added until .NET 5
private long _currentProcessId;
public Horizon(ITimeSpanGenerator timer)
public Horizon(ITimeSpanGenerator timer, FileSystemServerConfig fsServerConfig)
{
_currentProcessId = 0;
Time = timer ?? new StopWatchTimeSpanGenerator();
ServiceManager = new ServiceManager(this);
ServiceManager = new ServiceManager();
// ReSharper disable ObjectCreationAsStatement
new FileSystemServer(CreateHorizonClient(), fsServerConfig);
new BcatServer(CreateHorizonClient());
// ReSharper restore ObjectCreationAsStatement
}
public Horizon(ITimeSpanGenerator timer, FileSystemServer fsServer)
public HorizonClient CreateHorizonClient()
{
Time = timer ?? new StopWatchTimeSpanGenerator();
FileSystemServer = fsServer;
ServiceManager = new ServiceManager(this);
}
ulong processId = (ulong)Interlocked.Increment(ref _currentProcessId);
private Result OpenFileSystemClient(out FileSystemClient client)
{
if (FileSystemServer is null)
{
client = default;
return ResultLibHac.ServiceNotInitialized.Log();
}
// Todo: Register process with FS
client = FileSystemServer.CreateFileSystemClient();
return Result.Success;
}
public Result CreateHorizonClient(out HorizonClient client)
{
Result rc = OpenFileSystemClient(out FileSystemClient fsClient);
if (rc.IsFailure())
{
client = default;
return rc;
}
client = new HorizonClient(this, fsClient);
return Result.Success;
}
public void InitializeFileSystemServer(FileSystemCreators fsCreators, IDeviceOperator deviceOperator)
{
if (FileSystemServer != null) return;
lock (_initLocker)
{
if (FileSystemServer != null) return;
var config = new FileSystemServerConfig();
config.FsCreators = fsCreators;
config.DeviceOperator = deviceOperator;
FileSystemServer = new FileSystemServer(config);
}
return new HorizonClient(this, new ProcessId(processId));
}
}
}

View file

@ -1,6 +1,7 @@
using System;
using LibHac.Arp;
using LibHac.Fs;
using LibHac.Os;
using LibHac.Sm;
namespace LibHac
@ -9,19 +10,25 @@ namespace LibHac
{
// ReSharper disable once UnusedAutoPropertyAccessor.Local
private Horizon Horizon { get; }
internal ProcessId ProcessId { get; }
private Lazy<ArpClient> ArpLazy { get; }
public FileSystemClient Fs { get; }
public ServiceManagerClient Sm { get; }
public OsClient Os { get; }
public ArpClient Arp => ArpLazy.Value;
internal HorizonClient(Horizon horizon, FileSystemClient fsClient)
public ITimeSpanGenerator Time => Horizon.Time;
internal HorizonClient(Horizon horizon, ProcessId processId)
{
Horizon = horizon;
ProcessId = processId;
Fs = fsClient;
Fs = new FileSystemClient(this);
Sm = new ServiceManagerClient(horizon.ServiceManager);
Os = new OsClient(this);
ArpLazy = new Lazy<ArpClient>(InitArpClient, true);
}

17
src/LibHac/Os/OsClient.cs Normal file
View file

@ -0,0 +1,17 @@
namespace LibHac.Os
{
public class OsClient
{
private HorizonClient Hos { get; }
internal OsClient(HorizonClient horizonClient)
{
Hos = horizonClient;
}
public ProcessId GetCurrentProcessId()
{
return Hos.ProcessId;
}
}
}

View file

@ -0,0 +1,11 @@
namespace LibHac.Os
{
public readonly struct ProcessId
{
public static ProcessId InvalidId => new ProcessId(ulong.MaxValue);
public readonly ulong Value;
public ProcessId(ulong value) => Value = value;
}
}

View file

@ -0,0 +1,9 @@
namespace LibHac.Sm
{
// This interface is being used as a stop-gap solution so we can
// have at least some sort of service system for now
public interface IServiceObject
{
Result GetServiceObject(out object serviceObject);
}
}

View file

@ -11,14 +11,7 @@ namespace LibHac.Sm
// isn't blocked waiting for something better.
internal class ServiceManager
{
// ReSharper disable once UnusedAutoPropertyAccessor.Local
private Horizon Horizon { get; }
private Dictionary<ServiceName, object> Services { get; } = new Dictionary<ServiceName, object>();
public ServiceManager(Horizon horizon)
{
Horizon = horizon;
}
private Dictionary<ServiceName, IServiceObject> Services { get; } = new Dictionary<ServiceName, IServiceObject>();
internal Result GetService(out object serviceObject, ServiceName serviceName)
{
@ -27,20 +20,20 @@ namespace LibHac.Sm
Result rc = ValidateServiceName(serviceName);
if (rc.IsFailure()) return rc;
if (!Services.TryGetValue(serviceName, out serviceObject))
if (!Services.TryGetValue(serviceName, out IServiceObject service))
{
return ResultSf.RequestDeferredByUser.Log();
}
return Result.Success;
return service.GetServiceObject(out serviceObject);
}
internal Result RegisterService(object serviceObject, ServiceName serviceName)
internal Result RegisterService(IServiceObject service, ServiceName serviceName)
{
Result rc = ValidateServiceName(serviceName);
if (rc.IsFailure()) return rc;
if (!Services.TryAdd(serviceName, serviceObject))
if (!Services.TryAdd(serviceName, service))
{
return ResultSm.AlreadyRegistered.Log();
}
@ -53,11 +46,12 @@ namespace LibHac.Sm
Result rc = ValidateServiceName(serviceName);
if (rc.IsFailure()) return rc;
if (!Services.Remove(serviceName, out object service))
if (!Services.Remove(serviceName, out IServiceObject service))
{
return ResultSm.NotRegistered.Log();
}
// ReSharper disable once SuspiciousTypeConversion.Global
if (service is IDisposable disposable)
{
disposable.Dispose();

View file

@ -29,7 +29,7 @@ namespace LibHac.Sm
throw new InvalidCastException("The service object is not of the specified type.");
}
public Result RegisterService(object serviceObject, ReadOnlySpan<char> name)
public Result RegisterService(IServiceObject serviceObject, ReadOnlySpan<char> name)
{
return Server.RegisterService(serviceObject, ServiceName.Encode(name));
}

View file

@ -6,7 +6,7 @@ namespace LibHac.Tests.Fs.FileSystemClientTests
{
public static class FileSystemServerFactory
{
public static FileSystemServer CreateServer(bool sdCardInserted, out IFileSystem rootFs)
private static FileSystemClient CreateClientImpl(bool sdCardInserted, out IFileSystem rootFs)
{
rootFs = new InMemoryFileSystem();
@ -19,23 +19,21 @@ namespace LibHac.Tests.Fs.FileSystemClientTests
config.DeviceOperator = defaultObjects.DeviceOperator;
config.ExternalKeySet = new ExternalKeySet();
var fsServer = new FileSystemServer(config);
var horizon = new Horizon(new StopWatchTimeSpanGenerator(), config);
return fsServer;
HorizonClient horizonClient = horizon.CreateHorizonClient();
return horizonClient.Fs;
}
public static FileSystemClient CreateClient(bool sdCardInserted)
{
FileSystemServer fsServer = CreateServer(sdCardInserted, out _);
return fsServer.CreateFileSystemClient();
return CreateClientImpl(sdCardInserted, out _);
}
public static FileSystemClient CreateClient(out IFileSystem rootFs)
{
FileSystemServer fsServer = CreateServer(false, out rootFs);
return fsServer.CreateFileSystemClient();
return CreateClientImpl(false, out rootFs);
}
}
}