IPC refactor part 3+4: New server HIPC message processor (#4188)
* IPC refactor part 3 + 4: New server HIPC message processor with source generator based serialization * Make types match on calls to AlignUp/AlignDown * Formatting * Address some PR feedback * Move BitfieldExtensions to Ryujinx.Common.Utilities and consolidate implementations * Rename Reader/Writer to SpanReader/SpanWriter and move to Ryujinx.Common.Memory * Implement EventType * Address more PR feedback * Log request processing errors since they are not normal * Rename waitable to multiwait and add missing lock * PR feedback * Ac_K PR feedback
This commit is contained in:
parent
c6a139a6e7
commit
08831eecf7
213 changed files with 9762 additions and 1010 deletions
12
Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainInHeader.cs
Normal file
12
Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainInHeader.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
namespace Ryujinx.Horizon.Sdk.Sf.Cmif
|
||||
{
|
||||
struct CmifDomainInHeader
|
||||
{
|
||||
public CmifDomainRequestType Type;
|
||||
public byte ObjectsCount;
|
||||
public ushort DataSize;
|
||||
public int ObjectId;
|
||||
public uint Padding;
|
||||
public uint Token;
|
||||
}
|
||||
}
|
12
Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainOutHeader.cs
Normal file
12
Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainOutHeader.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
namespace Ryujinx.Horizon.Sdk.Sf.Cmif
|
||||
{
|
||||
struct CmifDomainOutHeader
|
||||
{
|
||||
#pragma warning disable CS0649
|
||||
public uint ObjectsCount;
|
||||
public uint Padding;
|
||||
public uint Padding2;
|
||||
public uint Padding3;
|
||||
#pragma warning restore CS0649
|
||||
}
|
||||
}
|
9
Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainRequestType.cs
Normal file
9
Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainRequestType.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
namespace Ryujinx.Horizon.Sdk.Sf.Cmif
|
||||
{
|
||||
enum CmifDomainRequestType : byte
|
||||
{
|
||||
Invalid = 0,
|
||||
SendMessage = 1,
|
||||
Close = 2
|
||||
}
|
||||
}
|
10
Ryujinx.Horizon/Sdk/Sf/Cmif/CmifInHeader.cs
Normal file
10
Ryujinx.Horizon/Sdk/Sf/Cmif/CmifInHeader.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace Ryujinx.Horizon.Sdk.Sf.Cmif
|
||||
{
|
||||
struct CmifInHeader
|
||||
{
|
||||
public uint Magic;
|
||||
public uint Version;
|
||||
public uint CommandId;
|
||||
public uint Token;
|
||||
}
|
||||
}
|
128
Ryujinx.Horizon/Sdk/Sf/Cmif/CmifMessage.cs
Normal file
128
Ryujinx.Horizon/Sdk/Sf/Cmif/CmifMessage.cs
Normal file
|
@ -0,0 +1,128 @@
|
|||
using Ryujinx.Horizon.Common;
|
||||
using Ryujinx.Horizon.Sdk.Sf.Hipc;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.Sf.Cmif
|
||||
{
|
||||
static class CmifMessage
|
||||
{
|
||||
public const uint CmifInHeaderMagic = 0x49434653; // SFCI
|
||||
public const uint CmifOutHeaderMagic = 0x4f434653; // SFCO
|
||||
|
||||
public static CmifRequest CreateRequest(Span<byte> output, CmifRequestFormat format)
|
||||
{
|
||||
int totalSize = 16;
|
||||
|
||||
if (format.ObjectId != 0)
|
||||
{
|
||||
totalSize += Unsafe.SizeOf<CmifDomainInHeader>() + format.ObjectsCount * sizeof(int);
|
||||
}
|
||||
|
||||
totalSize += Unsafe.SizeOf<CmifInHeader>() + format.DataSize;
|
||||
totalSize = (totalSize + 1) & ~1;
|
||||
int outPointerSizeTableOffset = totalSize;
|
||||
int outPointerSizeTableSize = format.OutAutoBuffersCount + format.OutPointersCount;
|
||||
totalSize += sizeof(ushort) * outPointerSizeTableSize;
|
||||
int rawDataSizeInWords = (totalSize + sizeof(uint) - 1) / sizeof(uint);
|
||||
|
||||
CmifRequest request = new CmifRequest();
|
||||
|
||||
request.Hipc = HipcMessage.WriteMessage(output, new HipcMetadata()
|
||||
{
|
||||
Type = format.Context != 0 ? (int)CommandType.RequestWithContext : (int)CommandType.Request,
|
||||
SendStaticsCount = format.InAutoBuffersCount + format.InPointersCount,
|
||||
SendBuffersCount = format.InAutoBuffersCount + format.InBuffersCount,
|
||||
ReceiveBuffersCount = format.OutAutoBuffersCount + format.OutBuffersCount,
|
||||
ExchangeBuffersCount = format.InOutBuffersCount,
|
||||
DataWordsCount = rawDataSizeInWords,
|
||||
ReceiveStaticsCount = outPointerSizeTableSize + format.OutFixedPointersCount,
|
||||
SendPid = format.SendPid,
|
||||
CopyHandlesCount = format.HandlesCount,
|
||||
MoveHandlesCount = 0
|
||||
});
|
||||
|
||||
Span<uint> data = request.Hipc.DataWords;
|
||||
|
||||
if (format.ObjectId != 0)
|
||||
{
|
||||
ref CmifDomainInHeader domainHeader = ref MemoryMarshal.Cast<uint, CmifDomainInHeader>(data)[0];
|
||||
|
||||
int payloadSize = Unsafe.SizeOf<CmifInHeader>() + format.DataSize;
|
||||
|
||||
domainHeader = new CmifDomainInHeader()
|
||||
{
|
||||
Type = CmifDomainRequestType.SendMessage,
|
||||
ObjectsCount = (byte)format.ObjectsCount,
|
||||
DataSize = (ushort)payloadSize,
|
||||
ObjectId = format.ObjectId,
|
||||
Padding = 0,
|
||||
Token = format.Context
|
||||
};
|
||||
|
||||
data = data.Slice(Unsafe.SizeOf<CmifDomainInHeader>() / sizeof(uint));
|
||||
|
||||
request.Objects = data.Slice((payloadSize + sizeof(uint) - 1) / sizeof(uint));
|
||||
}
|
||||
|
||||
ref CmifInHeader header = ref MemoryMarshal.Cast<uint, CmifInHeader>(data)[0];
|
||||
|
||||
header = new CmifInHeader()
|
||||
{
|
||||
Magic = CmifInHeaderMagic,
|
||||
Version = format.Context != 0 ? 1u : 0u,
|
||||
CommandId = format.RequestId,
|
||||
Token = format.ObjectId != 0 ? 0u : format.Context
|
||||
};
|
||||
|
||||
request.Data = MemoryMarshal.Cast<uint, byte>(data).Slice(Unsafe.SizeOf<CmifInHeader>());
|
||||
|
||||
int paddingSizeBefore = (rawDataSizeInWords - request.Hipc.DataWords.Length) * sizeof(uint);
|
||||
|
||||
Span<byte> outPointerTable = MemoryMarshal.Cast<uint, byte>(request.Hipc.DataWords).Slice(outPointerSizeTableOffset - paddingSizeBefore);
|
||||
request.OutPointerSizes = MemoryMarshal.Cast<byte, ushort>(outPointerTable);
|
||||
request.ServerPointerSize = format.ServerPointerSize;
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
public static Result ParseResponse(out CmifResponse response, Span<byte> input, bool isDomain, int size)
|
||||
{
|
||||
HipcMessage responseMessage = new HipcMessage(input);
|
||||
|
||||
Span<byte> data = MemoryMarshal.Cast<uint, byte>(responseMessage.Data.DataWords);
|
||||
Span<uint> objects = Span<uint>.Empty;
|
||||
|
||||
if (isDomain)
|
||||
{
|
||||
data = data.Slice(Unsafe.SizeOf<CmifDomainOutHeader>());
|
||||
objects = MemoryMarshal.Cast<byte, uint>(data.Slice(Unsafe.SizeOf<CmifOutHeader>() + size));
|
||||
}
|
||||
|
||||
CmifOutHeader header = MemoryMarshal.Cast<byte, CmifOutHeader>(data)[0];
|
||||
|
||||
if (header.Magic != CmifOutHeaderMagic)
|
||||
{
|
||||
response = default;
|
||||
return SfResult.InvalidOutHeader;
|
||||
}
|
||||
|
||||
if (header.Result.IsFailure)
|
||||
{
|
||||
response = default;
|
||||
return header.Result;
|
||||
}
|
||||
|
||||
response = new CmifResponse()
|
||||
{
|
||||
Data = data.Slice(Unsafe.SizeOf<CmifOutHeader>()),
|
||||
Objects = objects,
|
||||
CopyHandles = responseMessage.Data.CopyHandles,
|
||||
MoveHandles = responseMessage.Data.MoveHandles
|
||||
};
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
}
|
14
Ryujinx.Horizon/Sdk/Sf/Cmif/CmifOutHeader.cs
Normal file
14
Ryujinx.Horizon/Sdk/Sf/Cmif/CmifOutHeader.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using Ryujinx.Horizon.Common;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.Sf.Cmif
|
||||
{
|
||||
struct CmifOutHeader
|
||||
{
|
||||
#pragma warning disable CS0649
|
||||
public uint Magic;
|
||||
public uint Version;
|
||||
public Result Result;
|
||||
public uint Token;
|
||||
#pragma warning restore CS0649
|
||||
}
|
||||
}
|
14
Ryujinx.Horizon/Sdk/Sf/Cmif/CmifRequest.cs
Normal file
14
Ryujinx.Horizon/Sdk/Sf/Cmif/CmifRequest.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using Ryujinx.Horizon.Sdk.Sf.Hipc;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.Sf.Cmif
|
||||
{
|
||||
ref struct CmifRequest
|
||||
{
|
||||
public HipcMessageData Hipc;
|
||||
public Span<byte> Data;
|
||||
public Span<ushort> OutPointerSizes;
|
||||
public Span<uint> Objects;
|
||||
public int ServerPointerSize;
|
||||
}
|
||||
}
|
24
Ryujinx.Horizon/Sdk/Sf/Cmif/CmifRequestFormat.cs
Normal file
24
Ryujinx.Horizon/Sdk/Sf/Cmif/CmifRequestFormat.cs
Normal file
|
@ -0,0 +1,24 @@
|
|||
namespace Ryujinx.Horizon.Sdk.Sf.Cmif
|
||||
{
|
||||
struct CmifRequestFormat
|
||||
{
|
||||
#pragma warning disable CS0649
|
||||
public int ObjectId;
|
||||
public uint RequestId;
|
||||
public uint Context;
|
||||
public int DataSize;
|
||||
public int ServerPointerSize;
|
||||
public int InAutoBuffersCount;
|
||||
public int OutAutoBuffersCount;
|
||||
public int InBuffersCount;
|
||||
public int OutBuffersCount;
|
||||
public int InOutBuffersCount;
|
||||
public int InPointersCount;
|
||||
public int OutPointersCount;
|
||||
public int OutFixedPointersCount;
|
||||
public int ObjectsCount;
|
||||
public int HandlesCount;
|
||||
public bool SendPid;
|
||||
#pragma warning restore CS0649
|
||||
}
|
||||
}
|
12
Ryujinx.Horizon/Sdk/Sf/Cmif/CmifResponse.cs
Normal file
12
Ryujinx.Horizon/Sdk/Sf/Cmif/CmifResponse.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
using System;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.Sf.Cmif
|
||||
{
|
||||
ref struct CmifResponse
|
||||
{
|
||||
public ReadOnlySpan<byte> Data;
|
||||
public ReadOnlySpan<uint> Objects;
|
||||
public ReadOnlySpan<int> CopyHandles;
|
||||
public ReadOnlySpan<int> MoveHandles;
|
||||
}
|
||||
}
|
14
Ryujinx.Horizon/Sdk/Sf/Cmif/CommandType.cs
Normal file
14
Ryujinx.Horizon/Sdk/Sf/Cmif/CommandType.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
namespace Ryujinx.Horizon.Sdk.Sf.Cmif
|
||||
{
|
||||
enum CommandType
|
||||
{
|
||||
Invalid = 0,
|
||||
LegacyRequest = 1,
|
||||
Close = 2,
|
||||
LegacyControl = 3,
|
||||
Request = 4,
|
||||
Control = 5,
|
||||
RequestWithContext = 6,
|
||||
ControlWithContext = 7
|
||||
}
|
||||
}
|
7
Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObject.cs
Normal file
7
Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObject.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace Ryujinx.Horizon.Sdk.Sf.Cmif
|
||||
{
|
||||
abstract partial class DomainServiceObject : ServerDomainBase, IServiceObject
|
||||
{
|
||||
public abstract ServerDomainBase GetServerDomain();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
using Ryujinx.Horizon.Common;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.Sf.Cmif
|
||||
{
|
||||
class DomainServiceObjectDispatchTable : ServiceDispatchTableBase
|
||||
{
|
||||
public override Result ProcessMessage(ref ServiceDispatchContext context, ReadOnlySpan<byte> inRawData)
|
||||
{
|
||||
return ProcessMessageImpl(ref context, ((DomainServiceObject)context.ServiceObject).GetServerDomain(), inRawData);
|
||||
}
|
||||
|
||||
private Result ProcessMessageImpl(ref ServiceDispatchContext context, ServerDomainBase domain, ReadOnlySpan<byte> inRawData)
|
||||
{
|
||||
if (inRawData.Length < Unsafe.SizeOf<CmifDomainInHeader>())
|
||||
{
|
||||
return SfResult.InvalidHeaderSize;
|
||||
}
|
||||
|
||||
var inHeader = MemoryMarshal.Cast<byte, CmifDomainInHeader>(inRawData)[0];
|
||||
|
||||
ReadOnlySpan<byte> inDomainRawData = inRawData.Slice(Unsafe.SizeOf<CmifDomainInHeader>());
|
||||
|
||||
int targetObjectId = inHeader.ObjectId;
|
||||
|
||||
switch (inHeader.Type)
|
||||
{
|
||||
case CmifDomainRequestType.SendMessage:
|
||||
var targetObject = domain.GetObject(targetObjectId);
|
||||
if (targetObject == null)
|
||||
{
|
||||
return SfResult.TargetNotFound;
|
||||
}
|
||||
|
||||
if (inHeader.DataSize + inHeader.ObjectsCount * sizeof(int) > inDomainRawData.Length)
|
||||
{
|
||||
return SfResult.InvalidHeaderSize;
|
||||
}
|
||||
|
||||
ReadOnlySpan<byte> inMessageRawData = inDomainRawData.Slice(0, inHeader.DataSize);
|
||||
|
||||
if (inHeader.ObjectsCount > DomainServiceObjectProcessor.MaximumObjects)
|
||||
{
|
||||
return SfResult.InvalidInObjectsCount;
|
||||
}
|
||||
|
||||
int[] inObjectIds = new int[inHeader.ObjectsCount];
|
||||
|
||||
var domainProcessor = new DomainServiceObjectProcessor(domain, inObjectIds);
|
||||
|
||||
if (context.Processor == null)
|
||||
{
|
||||
context.Processor = domainProcessor;
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Processor.SetImplementationProcessor(domainProcessor);
|
||||
}
|
||||
|
||||
context.ServiceObject = targetObject.ServiceObject;
|
||||
|
||||
return targetObject.ProcessMessage(ref context, inMessageRawData);
|
||||
|
||||
case CmifDomainRequestType.Close:
|
||||
domain.UnregisterObject(targetObjectId);
|
||||
return Result.Success;
|
||||
|
||||
default:
|
||||
return SfResult.InvalidInHeader;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
140
Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObjectProcessor.cs
Normal file
140
Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObjectProcessor.cs
Normal file
|
@ -0,0 +1,140 @@
|
|||
using Ryujinx.Horizon.Common;
|
||||
using Ryujinx.Horizon.Sdk.Sf.Hipc;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.Sf.Cmif
|
||||
{
|
||||
class DomainServiceObjectProcessor : ServerMessageProcessor
|
||||
{
|
||||
public const int MaximumObjects = 8;
|
||||
|
||||
private ServerMessageProcessor _implProcessor;
|
||||
private readonly ServerDomainBase _domain;
|
||||
private int _outObjectIdsOffset;
|
||||
private readonly int[] _inObjectIds;
|
||||
private readonly int[] _reservedObjectIds;
|
||||
private ServerMessageRuntimeMetadata _implMetadata;
|
||||
|
||||
private int InObjectsCount => _inObjectIds.Length;
|
||||
private int OutObjectsCount => _implMetadata.OutObjectsCount;
|
||||
private int ImplOutHeadersSize => _implMetadata.OutHeadersSize;
|
||||
private int ImplOutDataTotalSize => _implMetadata.OutDataSize + _implMetadata.OutHeadersSize;
|
||||
|
||||
public DomainServiceObjectProcessor(ServerDomainBase domain, int[] inObjectIds)
|
||||
{
|
||||
_domain = domain;
|
||||
_inObjectIds = inObjectIds;
|
||||
_reservedObjectIds = new int[MaximumObjects];
|
||||
}
|
||||
|
||||
public override void SetImplementationProcessor(ServerMessageProcessor impl)
|
||||
{
|
||||
if (_implProcessor == null)
|
||||
{
|
||||
_implProcessor = impl;
|
||||
}
|
||||
else
|
||||
{
|
||||
_implProcessor.SetImplementationProcessor(impl);
|
||||
}
|
||||
|
||||
_implMetadata = _implProcessor.GetRuntimeMetadata();
|
||||
}
|
||||
|
||||
public override ServerMessageRuntimeMetadata GetRuntimeMetadata()
|
||||
{
|
||||
var runtimeMetadata = _implProcessor.GetRuntimeMetadata();
|
||||
|
||||
return new ServerMessageRuntimeMetadata(
|
||||
(ushort)(runtimeMetadata.InDataSize + runtimeMetadata.InObjectsCount * sizeof(int)),
|
||||
(ushort)(runtimeMetadata.OutDataSize + runtimeMetadata.OutObjectsCount * sizeof(int)),
|
||||
(byte)(runtimeMetadata.InHeadersSize + Unsafe.SizeOf<CmifDomainInHeader>()),
|
||||
(byte)(runtimeMetadata.OutHeadersSize + Unsafe.SizeOf<CmifDomainOutHeader>()),
|
||||
0,
|
||||
0);
|
||||
}
|
||||
|
||||
public override Result PrepareForProcess(ref ServiceDispatchContext context, ServerMessageRuntimeMetadata runtimeMetadata)
|
||||
{
|
||||
if (_implMetadata.InObjectsCount != InObjectsCount)
|
||||
{
|
||||
return SfResult.InvalidInObjectsCount;
|
||||
}
|
||||
|
||||
Result result = _domain.ReserveIds(new Span<int>(_reservedObjectIds).Slice(0, OutObjectsCount));
|
||||
|
||||
if (result.IsFailure)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
return _implProcessor.PrepareForProcess(ref context, runtimeMetadata);
|
||||
}
|
||||
|
||||
public override Result GetInObjects(Span<ServiceObjectHolder> inObjects)
|
||||
{
|
||||
for (int i = 0; i < InObjectsCount; i++)
|
||||
{
|
||||
inObjects[i] = _domain.GetObject(_inObjectIds[i]);
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public override HipcMessageData PrepareForReply(scoped ref ServiceDispatchContext context, out Span<byte> outRawData, ServerMessageRuntimeMetadata runtimeMetadata)
|
||||
{
|
||||
var response = _implProcessor.PrepareForReply(ref context, out outRawData, runtimeMetadata);
|
||||
|
||||
int outHeaderSize = Unsafe.SizeOf<CmifDomainOutHeader>();
|
||||
int implOutDataTotalSize = ImplOutDataTotalSize;
|
||||
|
||||
DebugUtil.Assert(outHeaderSize + implOutDataTotalSize + OutObjectsCount * sizeof(int) <= outRawData.Length);
|
||||
|
||||
outRawData = outRawData.Slice(outHeaderSize);
|
||||
_outObjectIdsOffset = (response.DataWords.Length * sizeof(uint) - outRawData.Length) + implOutDataTotalSize;
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
public override void PrepareForErrorReply(scoped ref ServiceDispatchContext context, out Span<byte> outRawData, ServerMessageRuntimeMetadata runtimeMetadata)
|
||||
{
|
||||
_implProcessor.PrepareForErrorReply(ref context, out outRawData, runtimeMetadata);
|
||||
|
||||
int outHeaderSize = Unsafe.SizeOf<CmifDomainOutHeader>();
|
||||
int implOutDataTotalSize = ImplOutDataTotalSize;
|
||||
|
||||
DebugUtil.Assert(outHeaderSize + implOutDataTotalSize <= outRawData.Length);
|
||||
|
||||
outRawData = outRawData.Slice(outHeaderSize);
|
||||
|
||||
_domain.UnreserveIds(new Span<int>(_reservedObjectIds).Slice(0, OutObjectsCount));
|
||||
}
|
||||
|
||||
public override void SetOutObjects(scoped ref ServiceDispatchContext context, HipcMessageData response, Span<ServiceObjectHolder> outObjects)
|
||||
{
|
||||
int outObjectsCount = OutObjectsCount;
|
||||
Span<int> objectIds = _reservedObjectIds;
|
||||
|
||||
for (int i = 0; i < outObjectsCount; i++)
|
||||
{
|
||||
if (outObjects[i] == null)
|
||||
{
|
||||
_domain.UnreserveIds(objectIds.Slice(i, 1));
|
||||
objectIds[i] = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
_domain.RegisterObject(objectIds[i], outObjects[i]);
|
||||
}
|
||||
|
||||
Span<int> outObjectIds = MemoryMarshal.Cast<byte, int>(MemoryMarshal.Cast<uint, byte>(response.DataWords).Slice(_outObjectIdsOffset));
|
||||
|
||||
for (int i = 0; i < outObjectsCount; i++)
|
||||
{
|
||||
outObjectIds[i] = objectIds[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
52
Ryujinx.Horizon/Sdk/Sf/Cmif/HandlesToClose.cs
Normal file
52
Ryujinx.Horizon/Sdk/Sf/Cmif/HandlesToClose.cs
Normal file
|
@ -0,0 +1,52 @@
|
|||
using System;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.Sf.Cmif
|
||||
{
|
||||
struct HandlesToClose
|
||||
{
|
||||
private int _handle0;
|
||||
private int _handle1;
|
||||
private int _handle2;
|
||||
private int _handle3;
|
||||
private int _handle4;
|
||||
private int _handle5;
|
||||
private int _handle6;
|
||||
private int _handle7;
|
||||
|
||||
public int Count;
|
||||
|
||||
public int this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
return index switch
|
||||
{
|
||||
0 => _handle0,
|
||||
1 => _handle1,
|
||||
2 => _handle2,
|
||||
3 => _handle3,
|
||||
4 => _handle4,
|
||||
5 => _handle5,
|
||||
6 => _handle6,
|
||||
7 => _handle7,
|
||||
_ => throw new IndexOutOfRangeException()
|
||||
};
|
||||
}
|
||||
set
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0: _handle0 = value; break;
|
||||
case 1: _handle1 = value; break;
|
||||
case 2: _handle2 = value; break;
|
||||
case 3: _handle3 = value; break;
|
||||
case 4: _handle4 = value; break;
|
||||
case 5: _handle5 = value; break;
|
||||
case 6: _handle6 = value; break;
|
||||
case 7: _handle7 = value; break;
|
||||
default: throw new IndexOutOfRangeException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
11
Ryujinx.Horizon/Sdk/Sf/Cmif/InlineContext.cs
Normal file
11
Ryujinx.Horizon/Sdk/Sf/Cmif/InlineContext.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
namespace Ryujinx.Horizon.Sdk.Sf.Cmif
|
||||
{
|
||||
class InlineContext
|
||||
{
|
||||
public static int Set(int newContext)
|
||||
{
|
||||
// TODO: Implement (will require FS changes???)
|
||||
return newContext;
|
||||
}
|
||||
}
|
||||
}
|
17
Ryujinx.Horizon/Sdk/Sf/Cmif/PointerAndSize.cs
Normal file
17
Ryujinx.Horizon/Sdk/Sf/Cmif/PointerAndSize.cs
Normal file
|
@ -0,0 +1,17 @@
|
|||
namespace Ryujinx.Horizon.Sdk.Sf.Cmif
|
||||
{
|
||||
struct PointerAndSize
|
||||
{
|
||||
public static PointerAndSize Empty => new PointerAndSize(0UL, 0UL);
|
||||
|
||||
public ulong Address { get; }
|
||||
public ulong Size { get; }
|
||||
public bool IsEmpty => Size == 0UL;
|
||||
|
||||
public PointerAndSize(ulong address, ulong size)
|
||||
{
|
||||
Address = address;
|
||||
Size = size;
|
||||
}
|
||||
}
|
||||
}
|
19
Ryujinx.Horizon/Sdk/Sf/Cmif/ScopedInlineContextChange.cs
Normal file
19
Ryujinx.Horizon/Sdk/Sf/Cmif/ScopedInlineContextChange.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
using System;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.Sf.Cmif
|
||||
{
|
||||
struct ScopedInlineContextChange : IDisposable
|
||||
{
|
||||
private readonly int _previousContext;
|
||||
|
||||
public ScopedInlineContextChange(int newContext)
|
||||
{
|
||||
_previousContext = InlineContext.Set(newContext);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
InlineContext.Set(_previousContext);
|
||||
}
|
||||
}
|
||||
}
|
15
Ryujinx.Horizon/Sdk/Sf/Cmif/ServerDomainBase.cs
Normal file
15
Ryujinx.Horizon/Sdk/Sf/Cmif/ServerDomainBase.cs
Normal file
|
@ -0,0 +1,15 @@
|
|||
using Ryujinx.Horizon.Common;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.Sf.Cmif
|
||||
{
|
||||
abstract class ServerDomainBase
|
||||
{
|
||||
public abstract Result ReserveIds(Span<int> outIds);
|
||||
public abstract void UnreserveIds(ReadOnlySpan<int> ids);
|
||||
public abstract void RegisterObject(int id, ServiceObjectHolder obj);
|
||||
|
||||
public abstract ServiceObjectHolder UnregisterObject(int id);
|
||||
public abstract ServiceObjectHolder GetObject(int id);
|
||||
}
|
||||
}
|
246
Ryujinx.Horizon/Sdk/Sf/Cmif/ServerDomainManager.cs
Normal file
246
Ryujinx.Horizon/Sdk/Sf/Cmif/ServerDomainManager.cs
Normal file
|
@ -0,0 +1,246 @@
|
|||
using Ryujinx.Horizon.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.Sf.Cmif
|
||||
{
|
||||
class ServerDomainManager
|
||||
{
|
||||
private class EntryManager
|
||||
{
|
||||
public class Entry
|
||||
{
|
||||
public int Id { get; }
|
||||
public Domain Owner { get; set; }
|
||||
public ServiceObjectHolder Obj { get; set; }
|
||||
public LinkedListNode<Entry> Node { get; set; }
|
||||
|
||||
public Entry(int id)
|
||||
{
|
||||
Id = id;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly LinkedList<Entry> _freeList;
|
||||
private readonly Entry[] _entries;
|
||||
|
||||
public EntryManager(int count)
|
||||
{
|
||||
_freeList = new LinkedList<Entry>();
|
||||
_entries = new Entry[count];
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
_freeList.AddLast(_entries[i] = new Entry(i + 1));
|
||||
}
|
||||
}
|
||||
|
||||
public Entry AllocateEntry()
|
||||
{
|
||||
lock (_freeList)
|
||||
{
|
||||
if (_freeList.Count == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var entry = _freeList.First.Value;
|
||||
_freeList.RemoveFirst();
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
public void FreeEntry(Entry entry)
|
||||
{
|
||||
lock (_freeList)
|
||||
{
|
||||
DebugUtil.Assert(entry.Owner == null);
|
||||
DebugUtil.Assert(entry.Obj == null);
|
||||
_freeList.AddFirst(entry);
|
||||
}
|
||||
}
|
||||
|
||||
public Entry GetEntry(int id)
|
||||
{
|
||||
if (id == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
int index = id - 1;
|
||||
|
||||
if ((uint)index >= (uint)_entries.Length)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return _entries[index];
|
||||
}
|
||||
}
|
||||
|
||||
private class Domain : DomainServiceObject, IDisposable
|
||||
{
|
||||
private readonly ServerDomainManager _manager;
|
||||
private readonly LinkedList<EntryManager.Entry> _entries;
|
||||
|
||||
public Domain(ServerDomainManager manager)
|
||||
{
|
||||
_manager = manager;
|
||||
_entries = new LinkedList<EntryManager.Entry>();
|
||||
}
|
||||
|
||||
public override ServiceObjectHolder GetObject(int id)
|
||||
{
|
||||
var entry = _manager._entryManager.GetEntry(id);
|
||||
if (entry == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
lock (_manager._entryOwnerLock)
|
||||
{
|
||||
if (entry.Owner != this)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return entry.Obj.Clone();
|
||||
}
|
||||
|
||||
public override ServerDomainBase GetServerDomain()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
public override void RegisterObject(int id, ServiceObjectHolder obj)
|
||||
{
|
||||
var entry = _manager._entryManager.GetEntry(id);
|
||||
DebugUtil.Assert(entry != null);
|
||||
|
||||
lock (_manager._entryOwnerLock)
|
||||
{
|
||||
DebugUtil.Assert(entry.Owner == null);
|
||||
entry.Owner = this;
|
||||
entry.Node = _entries.AddLast(entry);
|
||||
}
|
||||
|
||||
entry.Obj = obj;
|
||||
}
|
||||
|
||||
public override Result ReserveIds(Span<int> outIds)
|
||||
{
|
||||
for (int i = 0; i < outIds.Length; i++)
|
||||
{
|
||||
var entry = _manager._entryManager.AllocateEntry();
|
||||
if (entry == null)
|
||||
{
|
||||
return SfResult.OutOfDomainEntries;
|
||||
}
|
||||
|
||||
DebugUtil.Assert(entry.Owner == null);
|
||||
|
||||
outIds[i] = entry.Id;
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public override ServiceObjectHolder UnregisterObject(int id)
|
||||
{
|
||||
var entry = _manager._entryManager.GetEntry(id);
|
||||
if (entry == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
ServiceObjectHolder obj;
|
||||
|
||||
lock (_manager._entryOwnerLock)
|
||||
{
|
||||
if (entry.Owner != this)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
entry.Owner = null;
|
||||
obj = entry.Obj;
|
||||
entry.Obj = null;
|
||||
_entries.Remove(entry.Node);
|
||||
entry.Node = null;
|
||||
}
|
||||
|
||||
_manager._entryManager.FreeEntry(entry);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
public override void UnreserveIds(ReadOnlySpan<int> ids)
|
||||
{
|
||||
for (int i = 0; i < ids.Length; i++)
|
||||
{
|
||||
var entry = _manager._entryManager.GetEntry(ids[i]);
|
||||
|
||||
DebugUtil.Assert(entry != null);
|
||||
DebugUtil.Assert(entry.Owner == null);
|
||||
|
||||
_manager._entryManager.FreeEntry(entry);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var entry in _entries)
|
||||
{
|
||||
if (entry.Obj.ServiceObject is IDisposable disposableObj)
|
||||
{
|
||||
disposableObj.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
_manager.FreeDomain(this);
|
||||
}
|
||||
}
|
||||
|
||||
private readonly EntryManager _entryManager;
|
||||
private readonly object _entryOwnerLock;
|
||||
private readonly HashSet<Domain> _domains;
|
||||
private int _maxDomains;
|
||||
|
||||
public ServerDomainManager(int entryCount, int maxDomains)
|
||||
{
|
||||
_entryManager = new EntryManager(entryCount);
|
||||
_entryOwnerLock = new object();
|
||||
_domains = new HashSet<Domain>();
|
||||
_maxDomains = maxDomains;
|
||||
}
|
||||
|
||||
public DomainServiceObject AllocateDomainServiceObject()
|
||||
{
|
||||
lock (_domains)
|
||||
{
|
||||
if (_domains.Count == _maxDomains)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var domain = new Domain(this);
|
||||
_domains.Add(domain);
|
||||
return domain;
|
||||
}
|
||||
}
|
||||
|
||||
public static void DestroyDomainServiceObject(DomainServiceObject obj)
|
||||
{
|
||||
((Domain)obj).Dispose();
|
||||
}
|
||||
|
||||
private void FreeDomain(Domain domain)
|
||||
{
|
||||
lock (_domains)
|
||||
{
|
||||
_domains.Remove(domain);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
18
Ryujinx.Horizon/Sdk/Sf/Cmif/ServerMessageProcessor.cs
Normal file
18
Ryujinx.Horizon/Sdk/Sf/Cmif/ServerMessageProcessor.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
using Ryujinx.Horizon.Common;
|
||||
using Ryujinx.Horizon.Sdk.Sf.Hipc;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.Sf.Cmif
|
||||
{
|
||||
abstract class ServerMessageProcessor
|
||||
{
|
||||
public abstract void SetImplementationProcessor(ServerMessageProcessor impl);
|
||||
public abstract ServerMessageRuntimeMetadata GetRuntimeMetadata();
|
||||
|
||||
public abstract Result PrepareForProcess(scoped ref ServiceDispatchContext context, ServerMessageRuntimeMetadata runtimeMetadata);
|
||||
public abstract Result GetInObjects(Span<ServiceObjectHolder> inObjects);
|
||||
public abstract HipcMessageData PrepareForReply(scoped ref ServiceDispatchContext context, out Span<byte> outRawData, ServerMessageRuntimeMetadata runtimeMetadata);
|
||||
public abstract void PrepareForErrorReply(scoped ref ServiceDispatchContext context, out Span<byte> outRawData, ServerMessageRuntimeMetadata runtimeMetadata);
|
||||
public abstract void SetOutObjects(scoped ref ServiceDispatchContext context, HipcMessageData response, Span<ServiceObjectHolder> outObjects);
|
||||
}
|
||||
}
|
29
Ryujinx.Horizon/Sdk/Sf/Cmif/ServerMessageRuntimeMetadata.cs
Normal file
29
Ryujinx.Horizon/Sdk/Sf/Cmif/ServerMessageRuntimeMetadata.cs
Normal file
|
@ -0,0 +1,29 @@
|
|||
namespace Ryujinx.Horizon.Sdk.Sf.Cmif
|
||||
{
|
||||
struct ServerMessageRuntimeMetadata
|
||||
{
|
||||
public ushort InDataSize { get; }
|
||||
public ushort OutDataSize { get; }
|
||||
public byte InHeadersSize { get; }
|
||||
public byte OutHeadersSize { get; }
|
||||
public byte InObjectsCount { get; }
|
||||
public byte OutObjectsCount { get; }
|
||||
public int UnfixedOutPointerSizeOffset => InDataSize + InHeadersSize + 0x10;
|
||||
|
||||
public ServerMessageRuntimeMetadata(
|
||||
ushort inDataSize,
|
||||
ushort outDataSize,
|
||||
byte inHeadersSize,
|
||||
byte outHeadersSize,
|
||||
byte inObjectsCount,
|
||||
byte outObjectsCount)
|
||||
{
|
||||
InDataSize = inDataSize;
|
||||
OutDataSize = outDataSize;
|
||||
InHeadersSize = inHeadersSize;
|
||||
OutHeadersSize = outHeadersSize;
|
||||
InObjectsCount = inObjectsCount;
|
||||
OutObjectsCount = outObjectsCount;
|
||||
}
|
||||
}
|
||||
}
|
18
Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchContext.cs
Normal file
18
Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchContext.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
using Ryujinx.Horizon.Sdk.Sf.Hipc;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.Sf.Cmif
|
||||
{
|
||||
ref struct ServiceDispatchContext
|
||||
{
|
||||
public IServiceObject ServiceObject;
|
||||
public ServerSessionManager Manager;
|
||||
public ServerSession Session;
|
||||
public ServerMessageProcessor Processor;
|
||||
public HandlesToClose HandlesToClose;
|
||||
public PointerAndSize PointerBuffer;
|
||||
public ReadOnlySpan<byte> InMessageBuffer;
|
||||
public Span<byte> OutMessageBuffer;
|
||||
public HipcMessage Request;
|
||||
}
|
||||
}
|
12
Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchMeta.cs
Normal file
12
Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchMeta.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
namespace Ryujinx.Horizon.Sdk.Sf.Cmif
|
||||
{
|
||||
struct ServiceDispatchMeta
|
||||
{
|
||||
public ServiceDispatchTableBase DispatchTable { get; }
|
||||
|
||||
public ServiceDispatchMeta(ServiceDispatchTableBase dispatchTable)
|
||||
{
|
||||
DispatchTable = dispatchTable;
|
||||
}
|
||||
}
|
||||
}
|
33
Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTable.cs
Normal file
33
Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTable.cs
Normal file
|
@ -0,0 +1,33 @@
|
|||
using Ryujinx.Horizon.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.Sf.Cmif
|
||||
{
|
||||
class ServiceDispatchTable : ServiceDispatchTableBase
|
||||
{
|
||||
private readonly string _objectName;
|
||||
private readonly IReadOnlyDictionary<int, CommandHandler> _entries;
|
||||
|
||||
public ServiceDispatchTable(string objectName, IReadOnlyDictionary<int, CommandHandler> entries)
|
||||
{
|
||||
_objectName = objectName;
|
||||
_entries = entries;
|
||||
}
|
||||
|
||||
public override Result ProcessMessage(ref ServiceDispatchContext context, ReadOnlySpan<byte> inRawData)
|
||||
{
|
||||
return ProcessMessageImpl(ref context, inRawData, _entries, _objectName);
|
||||
}
|
||||
|
||||
public static ServiceDispatchTableBase Create(IServiceObject instance)
|
||||
{
|
||||
if (instance is DomainServiceObject)
|
||||
{
|
||||
return new DomainServiceObjectDispatchTable();
|
||||
}
|
||||
|
||||
return new ServiceDispatchTable(instance.GetType().Name, instance.GetCommandHandlers());
|
||||
}
|
||||
}
|
||||
}
|
90
Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTableBase.cs
Normal file
90
Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTableBase.cs
Normal file
|
@ -0,0 +1,90 @@
|
|||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using Ryujinx.Horizon.Sdk.Sf.Hipc;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.Sf.Cmif
|
||||
{
|
||||
abstract class ServiceDispatchTableBase
|
||||
{
|
||||
private const uint MaxCmifVersion = 1;
|
||||
|
||||
public abstract Result ProcessMessage(ref ServiceDispatchContext context, ReadOnlySpan<byte> inRawData);
|
||||
|
||||
protected Result ProcessMessageImpl(ref ServiceDispatchContext context, ReadOnlySpan<byte> inRawData, IReadOnlyDictionary<int, CommandHandler> entries, string objectName)
|
||||
{
|
||||
if (inRawData.Length < Unsafe.SizeOf<CmifInHeader>())
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.KernelIpc, $"Request message size 0x{inRawData.Length:X} is invalid");
|
||||
|
||||
return SfResult.InvalidHeaderSize;
|
||||
}
|
||||
|
||||
CmifInHeader inHeader = MemoryMarshal.Cast<byte, CmifInHeader>(inRawData)[0];
|
||||
|
||||
if (inHeader.Magic != CmifMessage.CmifInHeaderMagic || inHeader.Version > MaxCmifVersion)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.KernelIpc, $"Request message header magic value 0x{inHeader.Magic:X} is invalid");
|
||||
|
||||
return SfResult.InvalidInHeader;
|
||||
}
|
||||
|
||||
ReadOnlySpan<byte> inMessageRawData = inRawData[Unsafe.SizeOf<CmifInHeader>()..];
|
||||
uint commandId = inHeader.CommandId;
|
||||
|
||||
var outHeader = Span<CmifOutHeader>.Empty;
|
||||
|
||||
if (!entries.TryGetValue((int)commandId, out var commandHandler))
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.KernelIpc, $"{objectName} command ID 0x{commandId:X} is not implemented");
|
||||
|
||||
if (HorizonStatic.Options.IgnoreMissingServices)
|
||||
{
|
||||
// If ignore missing services is enabled, just pretend that everything is fine.
|
||||
var response = PrepareForStubReply(ref context, out Span<byte> outRawData);
|
||||
CommandHandler.GetCmifOutHeaderPointer(ref outHeader, ref outRawData);
|
||||
outHeader[0] = new CmifOutHeader() { Magic = CmifMessage.CmifOutHeaderMagic, Result = Result.Success };
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
return SfResult.UnknownCommandId;
|
||||
}
|
||||
|
||||
Logger.Trace?.Print(LogClass.KernelIpc, $"{objectName}.{commandHandler.MethodName} called");
|
||||
|
||||
Result commandResult = commandHandler.Invoke(ref outHeader, ref context, inMessageRawData);
|
||||
|
||||
if (commandResult.Module == SfResult.ModuleId ||
|
||||
commandResult.Module == HipcResult.ModuleId)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.KernelIpc, $"{commandHandler.MethodName} returned error {commandResult}");
|
||||
}
|
||||
|
||||
if (SfResult.RequestContextChanged(commandResult))
|
||||
{
|
||||
return commandResult;
|
||||
}
|
||||
|
||||
if (outHeader.IsEmpty)
|
||||
{
|
||||
commandResult.AbortOnSuccess();
|
||||
return commandResult;
|
||||
}
|
||||
|
||||
outHeader[0] = new CmifOutHeader() { Magic = CmifMessage.CmifOutHeaderMagic, Result = commandResult };
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
private static HipcMessageData PrepareForStubReply(scoped ref ServiceDispatchContext context, out Span<byte> outRawData)
|
||||
{
|
||||
var response = HipcMessage.WriteResponse(context.OutMessageBuffer, 0, 0x20 / sizeof(uint), 0, 0);
|
||||
outRawData = MemoryMarshal.Cast<uint, byte>(response.DataWords);
|
||||
return response;
|
||||
}
|
||||
}
|
||||
}
|
34
Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceObjectHolder.cs
Normal file
34
Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceObjectHolder.cs
Normal file
|
@ -0,0 +1,34 @@
|
|||
using Ryujinx.Horizon.Common;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.Sf.Cmif
|
||||
{
|
||||
class ServiceObjectHolder
|
||||
{
|
||||
public IServiceObject ServiceObject { get; }
|
||||
|
||||
private readonly ServiceDispatchMeta _dispatchMeta;
|
||||
|
||||
public ServiceObjectHolder(ServiceObjectHolder objectHolder)
|
||||
{
|
||||
ServiceObject = objectHolder.ServiceObject;
|
||||
_dispatchMeta = objectHolder._dispatchMeta;
|
||||
}
|
||||
|
||||
public ServiceObjectHolder(IServiceObject serviceImpl)
|
||||
{
|
||||
ServiceObject = serviceImpl;
|
||||
_dispatchMeta = new ServiceDispatchMeta(ServiceDispatchTable.Create(serviceImpl));
|
||||
}
|
||||
|
||||
public ServiceObjectHolder Clone()
|
||||
{
|
||||
return new ServiceObjectHolder(this);
|
||||
}
|
||||
|
||||
public Result ProcessMessage(ref ServiceDispatchContext context, ReadOnlySpan<byte> inRawData)
|
||||
{
|
||||
return _dispatchMeta.DispatchTable.ProcessMessage(ref context, inRawData);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue