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); IServiceCreator service = GetServiceCreator(type);
Result rc = Hos.Sm.RegisterService(service, name); Result rc = Hos.Sm.RegisterService(new BcatServiceObject(service), name);
if (rc.IsFailure()) if (rc.IsFailure())
{ {
throw new HorizonResultException(rc, "Abort"); 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.Diagnostics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using LibHac.Common; using LibHac.Common;
using LibHac.Diag;
using LibHac.Fs.Accessors; using LibHac.Fs.Accessors;
using LibHac.Fs.Fsa; using LibHac.Fs.Fsa;
using LibHac.FsSrv; using LibHac.FsSrv;
@ -11,7 +12,7 @@ namespace LibHac.Fs
{ {
public partial class FileSystemClient public partial class FileSystemClient
{ {
private FileSystemServer FsSrv { get; } private HorizonClient Hos { get; }
private IFileSystemProxy FsProxy { get; set; } private IFileSystemProxy FsProxy { get; set; }
private readonly object _fspInitLocker = new object(); private readonly object _fspInitLocker = new object();
@ -26,22 +27,17 @@ namespace LibHac.Fs
Time = timer ?? new StopWatchTimeSpanGenerator(); Time = timer ?? new StopWatchTimeSpanGenerator();
} }
public FileSystemClient(FileSystemServer fsServer, ITimeSpanGenerator timer) public FileSystemClient(HorizonClient horizonClient)
{ {
FsSrv = fsServer; Hos = horizonClient;
Time = timer ?? new StopWatchTimeSpanGenerator(); Time = horizonClient.Time;
}
internal FileSystemClient(FileSystemServer fsServer, IFileSystemProxy fsProxy, ITimeSpanGenerator timer) Assert.NotNull(Time);
{
FsSrv = fsServer;
FsProxy = fsProxy;
Time = timer ?? new StopWatchTimeSpanGenerator();
} }
public bool HasFileSystemServer() public bool HasFileSystemServer()
{ {
return FsSrv != null; return Hos != null;
} }
public IFileSystemProxy GetFileSystemProxyServiceObject() public IFileSystemProxy GetFileSystemProxyServiceObject()
@ -57,8 +53,16 @@ namespace LibHac.Fs
throw new InvalidOperationException("Client was not initialized with a server object."); 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; return FsProxy;
} }
} }

View file

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

View file

@ -3,6 +3,7 @@ using LibHac.Fs;
using LibHac.Fs.Impl; using LibHac.Fs.Impl;
using LibHac.Fs.Shim; using LibHac.Fs.Shim;
using LibHac.FsSrv.Creators; using LibHac.FsSrv.Creators;
using LibHac.Sm;
namespace LibHac.FsSrv namespace LibHac.FsSrv
{ {
@ -13,15 +14,17 @@ namespace LibHac.FsSrv
private FileSystemProxyCore FsProxyCore { get; } private FileSystemProxyCore FsProxyCore { get; }
/// <summary>The client instance to be used for internal operations like save indexer access.</summary> /// <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; } public bool IsDebugMode { get; }
private ITimeSpanGenerator Timer { get; } private ITimeSpanGenerator Timer { get; }
/// <summary> /// <summary>
/// Creates a new <see cref="FileSystemServer"/>. /// Creates a new <see cref="FileSystemServer"/> and registers its services using the provided HOS client.
/// </summary> /// </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> /// <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) if (config.FsCreators == null)
throw new ArgumentException("FsCreators must not be null"); throw new ArgumentException("FsCreators must not be null");
@ -29,6 +32,8 @@ namespace LibHac.FsSrv
if (config.DeviceOperator == null) if (config.DeviceOperator == null)
throw new ArgumentException("DeviceOperator must not be null"); throw new ArgumentException("DeviceOperator must not be null");
Hos = horizonClient;
IsDebugMode = false; IsDebugMode = false;
ExternalKeySet externalKeySet = config.ExternalKeySet ?? new ExternalKeySet(); ExternalKeySet externalKeySet = config.ExternalKeySet ?? new ExternalKeySet();
@ -41,17 +46,19 @@ namespace LibHac.FsSrv
}; };
FsProxyCore = new FileSystemProxyCore(fspConfig, externalKeySet, config.DeviceOperator); 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 FsProxyCore.SetSaveDataIndexerManager(new SaveDataIndexerManager(Hos.Fs, SaveIndexerId,
if (FsClient.IsSdCardInserted())
FsClient.SetSdCardAccessibility(true);
FsProxyCore.SetSaveDataIndexerManager(new SaveDataIndexerManager(FsClient, SaveIndexerId,
new ArrayPoolMemoryResource(), new SdHandleManager(), false)); new ArrayPoolMemoryResource(), new SdHandleManager(), false));
FileSystemProxy fsProxy = GetFileSystemProxyServiceObject();
fsProxy.SetCurrentProcess(Hos.Os.GetCurrentProcessId().Value).IgnoreResult();
fsProxy.CleanUpTemporaryStorage().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> /// <summary>
@ -69,12 +76,35 @@ namespace LibHac.FsSrv
/// <returns>The created <see cref="FileSystemClient"/>.</returns> /// <returns>The created <see cref="FileSystemClient"/>.</returns>
public FileSystemClient CreateFileSystemClient(ITimeSpanGenerator timer) 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; return rc;
} }
rc = FsProxy.FsServer.FsClient.CreateSystemSaveData(SaveDataId, SaveDataSize, SaveJournalSize, rc = FsProxy.Hos.Fs.CreateSystemSaveData(SaveDataId, SaveDataSize, SaveJournalSize,
SaveDataFlags.None); SaveDataFlags.None);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
} }

View file

@ -183,12 +183,6 @@ namespace LibHac.FsSrv.Impl
return initialProcessIdLowerBound >= processId && processId <= initialProcessIdUpperBound; 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) internal static ProgramInfo CreateProgramInfoForInitialProcess(FileSystemServer fsServer)
{ {
return new ProgramInfo(fsServer, InitialProcessAccessControlDataHeader, return new ProgramInfo(fsServer, InitialProcessAccessControlDataHeader,

View file

@ -1,7 +1,7 @@
using LibHac.Common; using System.Threading;
using LibHac.Fs; using LibHac.Bcat;
using LibHac.FsSrv; using LibHac.FsSrv;
using LibHac.FsSrv.Creators; using LibHac.Os;
using LibHac.Sm; using LibHac.Sm;
namespace LibHac namespace LibHac
@ -9,63 +9,32 @@ namespace LibHac
public class Horizon public class Horizon
{ {
internal ITimeSpanGenerator Time { get; } internal ITimeSpanGenerator Time { get; }
private FileSystemServer FileSystemServer { get; set; }
internal ServiceManager ServiceManager { get; } 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(); 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(); ulong processId = (ulong)Interlocked.Increment(ref _currentProcessId);
FileSystemServer = fsServer;
ServiceManager = new ServiceManager(this);
}
private Result OpenFileSystemClient(out FileSystemClient client) // Todo: Register process with FS
{
if (FileSystemServer is null)
{
client = default;
return ResultLibHac.ServiceNotInitialized.Log();
}
client = FileSystemServer.CreateFileSystemClient(); return new HorizonClient(this, new ProcessId(processId));
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);
}
} }
} }
} }

View file

@ -1,6 +1,7 @@
using System; using System;
using LibHac.Arp; using LibHac.Arp;
using LibHac.Fs; using LibHac.Fs;
using LibHac.Os;
using LibHac.Sm; using LibHac.Sm;
namespace LibHac namespace LibHac
@ -9,19 +10,25 @@ namespace LibHac
{ {
// ReSharper disable once UnusedAutoPropertyAccessor.Local // ReSharper disable once UnusedAutoPropertyAccessor.Local
private Horizon Horizon { get; } private Horizon Horizon { get; }
internal ProcessId ProcessId { get; }
private Lazy<ArpClient> ArpLazy { get; } private Lazy<ArpClient> ArpLazy { get; }
public FileSystemClient Fs { get; } public FileSystemClient Fs { get; }
public ServiceManagerClient Sm { get; } public ServiceManagerClient Sm { get; }
public OsClient Os { get; }
public ArpClient Arp => ArpLazy.Value; public ArpClient Arp => ArpLazy.Value;
internal HorizonClient(Horizon horizon, FileSystemClient fsClient) public ITimeSpanGenerator Time => Horizon.Time;
internal HorizonClient(Horizon horizon, ProcessId processId)
{ {
Horizon = horizon; Horizon = horizon;
ProcessId = processId;
Fs = fsClient; Fs = new FileSystemClient(this);
Sm = new ServiceManagerClient(horizon.ServiceManager); Sm = new ServiceManagerClient(horizon.ServiceManager);
Os = new OsClient(this);
ArpLazy = new Lazy<ArpClient>(InitArpClient, true); 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. // isn't blocked waiting for something better.
internal class ServiceManager internal class ServiceManager
{ {
// ReSharper disable once UnusedAutoPropertyAccessor.Local private Dictionary<ServiceName, IServiceObject> Services { get; } = new Dictionary<ServiceName, IServiceObject>();
private Horizon Horizon { get; }
private Dictionary<ServiceName, object> Services { get; } = new Dictionary<ServiceName, object>();
public ServiceManager(Horizon horizon)
{
Horizon = horizon;
}
internal Result GetService(out object serviceObject, ServiceName serviceName) internal Result GetService(out object serviceObject, ServiceName serviceName)
{ {
@ -27,20 +20,20 @@ namespace LibHac.Sm
Result rc = ValidateServiceName(serviceName); Result rc = ValidateServiceName(serviceName);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
if (!Services.TryGetValue(serviceName, out serviceObject)) if (!Services.TryGetValue(serviceName, out IServiceObject service))
{ {
return ResultSf.RequestDeferredByUser.Log(); 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); Result rc = ValidateServiceName(serviceName);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
if (!Services.TryAdd(serviceName, serviceObject)) if (!Services.TryAdd(serviceName, service))
{ {
return ResultSm.AlreadyRegistered.Log(); return ResultSm.AlreadyRegistered.Log();
} }
@ -53,11 +46,12 @@ namespace LibHac.Sm
Result rc = ValidateServiceName(serviceName); Result rc = ValidateServiceName(serviceName);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
if (!Services.Remove(serviceName, out object service)) if (!Services.Remove(serviceName, out IServiceObject service))
{ {
return ResultSm.NotRegistered.Log(); return ResultSm.NotRegistered.Log();
} }
// ReSharper disable once SuspiciousTypeConversion.Global
if (service is IDisposable disposable) if (service is IDisposable disposable)
{ {
disposable.Dispose(); disposable.Dispose();

View file

@ -29,7 +29,7 @@ namespace LibHac.Sm
throw new InvalidCastException("The service object is not of the specified type."); 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)); return Server.RegisterService(serviceObject, ServiceName.Encode(name));
} }

View file

@ -6,7 +6,7 @@ namespace LibHac.Tests.Fs.FileSystemClientTests
{ {
public static class FileSystemServerFactory 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(); rootFs = new InMemoryFileSystem();
@ -19,23 +19,21 @@ namespace LibHac.Tests.Fs.FileSystemClientTests
config.DeviceOperator = defaultObjects.DeviceOperator; config.DeviceOperator = defaultObjects.DeviceOperator;
config.ExternalKeySet = new ExternalKeySet(); 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) public static FileSystemClient CreateClient(bool sdCardInserted)
{ {
FileSystemServer fsServer = CreateServer(sdCardInserted, out _); return CreateClientImpl(sdCardInserted, out _);
return fsServer.CreateFileSystemClient();
} }
public static FileSystemClient CreateClient(out IFileSystem rootFs) public static FileSystemClient CreateClient(out IFileSystem rootFs)
{ {
FileSystemServer fsServer = CreateServer(false, out rootFs); return CreateClientImpl(false, out rootFs);
return fsServer.CreateFileSystemClient();
} }
} }
} }