mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
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:
parent
deaf111ac3
commit
071b608f5f
15 changed files with 164 additions and 111 deletions
|
@ -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");
|
||||
|
|
20
src/LibHac/Bcat/Detail/Ipc/BcatServiceObject.cs
Normal file
20
src/LibHac/Bcat/Detail/Ipc/BcatServiceObject.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
17
src/LibHac/Os/OsClient.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
11
src/LibHac/Os/ProcessId.cs
Normal file
11
src/LibHac/Os/ProcessId.cs
Normal 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;
|
||||
}
|
||||
}
|
9
src/LibHac/Sm/IServiceObject.cs
Normal file
9
src/LibHac/Sm/IServiceObject.cs
Normal 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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue