Refactor logging and unify argument deserialization
- Simplified logging with null-conditional operators - Temporarily disabled WASM AsyncSegment guard for testing - Unified argument deserialization via GetArgBytes for zero-copy and pooled buffer support - Removed DeserializeFromSequence in favor of new approach - Applied improvements to AyCodeBinaryHubProtocol - Updated comments and performed minor code cleanup
This commit is contained in:
parent
dc16f493d5
commit
939ce9c39b
|
|
@ -115,10 +115,14 @@ public class AcBinaryHubProtocol : IHubProtocol
|
|||
// Send-side guard: AsyncSegment uses AsyncPipeWriterOutput whose sync-over-async flush
|
||||
// would block the browser's single UI thread. The receive side converts chunked wire
|
||||
// to a synchronous deserialize on WASM automatically (see TryParseChunkData).
|
||||
if (IsBrowser && protocolMode == BinaryProtocolMode.AsyncSegment)
|
||||
throw new PlatformNotSupportedException(
|
||||
"BinaryProtocolMode.AsyncSegment is not supported on WebAssembly. " +
|
||||
"Use BinaryProtocolMode.Bytes or BinaryProtocolMode.Segment instead.");
|
||||
//
|
||||
// TEMP: commented out to test AsyncSegment on both Windows app and WASM without rebuild.
|
||||
// Small WASM payloads work; larger ones may deadlock on sync-over-async FlushAsync.
|
||||
// Restore once BinaryProtocolMode is runtime-configurable in Program.cs.
|
||||
//if (IsBrowser && protocolMode == BinaryProtocolMode.AsyncSegment)
|
||||
// throw new PlatformNotSupportedException(
|
||||
// "BinaryProtocolMode.AsyncSegment is not supported on WebAssembly. " +
|
||||
// "Use BinaryProtocolMode.Bytes or BinaryProtocolMode.Segment instead.");
|
||||
|
||||
_options = options;
|
||||
_options.BufferWriterChunkSize = 4096;
|
||||
|
|
@ -208,10 +212,7 @@ public class AcBinaryHubProtocol : IHubProtocol
|
|||
|
||||
public void WriteMessage(HubMessage message, IBufferWriter<byte> output)
|
||||
{
|
||||
if (_logger != null)
|
||||
{
|
||||
_logger.LogInformation("Serialize start");
|
||||
}
|
||||
_logger?.LogInformation("Serialize start");
|
||||
|
||||
// AsyncSegment: chunked protocol framing for messages with streamable arguments
|
||||
if (_protocolMode == BinaryProtocolMode.AsyncSegment
|
||||
|
|
@ -277,13 +278,10 @@ public class AcBinaryHubProtocol : IHubProtocol
|
|||
bw.Flush();
|
||||
Unsafe.WriteUnaligned(ref lengthSpan[0], totalPayload);
|
||||
|
||||
if (_logger != null)
|
||||
{
|
||||
_logger.LogInformation("Serialize end totalSentSize={TotalSentSize}", LengthPrefixSize + totalPayload);
|
||||
_logger?.LogInformation("Serialize end totalSentSize={TotalSentSize}", LengthPrefixSize + totalPayload);
|
||||
|
||||
if (_logger.IsEnabled(LogLevel.Debug))
|
||||
_logger.LogDebug("WriteMessage {MessageType} payloadSize={PayloadSize}", message.GetType().Name, totalPayload);
|
||||
}
|
||||
if (_logger?.IsEnabled(LogLevel.Debug) == true)
|
||||
_logger.LogDebug("WriteMessage {MessageType} payloadSize={PayloadSize}", message.GetType().Name, totalPayload);
|
||||
}
|
||||
|
||||
private void WriteInvocation(ref BufferWriterBinaryOutput bw, IBufferWriter<byte> output, InvocationMessage m, ref int externalBytes)
|
||||
|
|
@ -493,11 +491,8 @@ public class AcBinaryHubProtocol : IHubProtocol
|
|||
var chunkCount = dataBytes > 0 ? (dataBytes + chunkSize - 1) / chunkSize : 0;
|
||||
var totalSentSize = LengthPrefixSize + chunkStartPayload + chunkCount * 3 + dataBytes + 1;
|
||||
|
||||
if (_logger != null)
|
||||
{
|
||||
_logger.LogInformation("Serialize end (chunked) dataBytes={DataBytes} chunkCount={ChunkCount} totalSentSize={TotalSentSize}",
|
||||
dataBytes, chunkCount, totalSentSize);
|
||||
}
|
||||
_logger?.LogInformation("Serialize end (chunked) dataBytes={DataBytes} chunkCount={ChunkCount} totalSentSize={TotalSentSize}",
|
||||
dataBytes, chunkCount, totalSentSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -569,11 +564,7 @@ public class AcBinaryHubProtocol : IHubProtocol
|
|||
return false;
|
||||
|
||||
_logger?.LogTrace("TryParseMessage parsing payloadLength={PayloadLength} inputLength={InputLength}", payloadLength, input.Length);
|
||||
|
||||
if (_logger != null)
|
||||
{
|
||||
_logger.LogInformation("Deserialize start");
|
||||
}
|
||||
_logger?.LogInformation("Deserialize start");
|
||||
|
||||
message = ParseMessage(ref reader, payloadLength, binder);
|
||||
|
||||
|
|
@ -581,13 +572,9 @@ public class AcBinaryHubProtocol : IHubProtocol
|
|||
{
|
||||
input = input.Slice(LengthPrefixSize + payloadLength);
|
||||
|
||||
if (_logger != null)
|
||||
{
|
||||
_logger.LogInformation("Deserialize end");
|
||||
|
||||
if (_logger.IsEnabled(LogLevel.Debug))
|
||||
_logger.LogDebug("TryParseMessage parsed {MessageType}", message.GetType().Name);
|
||||
}
|
||||
_logger?.LogInformation("Deserialize end");
|
||||
if (_logger?.IsEnabled(LogLevel.Debug) == true) _logger.LogDebug("TryParseMessage parsed {MessageType}", message.GetType().Name);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -884,20 +871,22 @@ public class AcBinaryHubProtocol : IHubProtocol
|
|||
}
|
||||
else
|
||||
{
|
||||
// Browser (WASM) fallback: all chunks are buffered, state.Buffer.Complete()
|
||||
// has been called above, so the synchronous deserializer reads through the
|
||||
// completed buffer without any Monitor.Wait.
|
||||
// Browser (WASM) fallback: all chunks are buffered into a single contiguous byte[]
|
||||
// inside SegmentBufferReader. Use ArrayBinaryInput via the offset-aware overload —
|
||||
// strictly faster than SegmentBufferReaderInput here (JIT eliminates
|
||||
// TryAdvanceSegment, no volatile reads, no cross-boundary branching).
|
||||
deserializedArg = AcBinaryDeserializer.Deserialize(
|
||||
state.Buffer, state.StreamedArgType, _options);
|
||||
state.Buffer.Buffer,
|
||||
0,
|
||||
state.Buffer.WritePos,
|
||||
state.StreamedArgType,
|
||||
_options);
|
||||
}
|
||||
|
||||
if (_logger != null)
|
||||
{
|
||||
_logger.LogInformation("Deserialize end (chunked)");
|
||||
_logger?.LogInformation("Deserialize end (chunked)");
|
||||
|
||||
if (_logger.IsEnabled(LogLevel.Debug))
|
||||
_logger.LogDebug("TryParseChunkData deserialization complete resultType={ResultType}", deserializedArg?.GetType().Name ?? "null");
|
||||
}
|
||||
if (_logger?.IsEnabled(LogLevel.Debug) == true)
|
||||
_logger.LogDebug("TryParseChunkData deserialization complete resultType={ResultType}", deserializedArg?.GetType().Name ?? "null");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
@ -1198,14 +1187,18 @@ public class AcBinaryHubProtocol : IHubProtocol
|
|||
return SequenceToByteArray(argSlice.Slice(1));
|
||||
}
|
||||
|
||||
// Bytes mode: linearize to byte[] → ArrayBinaryInput (fastest deser, no segment overhead)
|
||||
if (_protocolMode == BinaryProtocolMode.Bytes)
|
||||
// Unified non-chunked receive path: always ArrayBinaryInput via offset-aware overload.
|
||||
// Single-segment: zero-copy on the pipe's slab. Multi-segment: pool-rented copy.
|
||||
// _protocolMode no longer affects the receive side — it is only a send-side strategy.
|
||||
var (arr, offset, length, rented) = GetArgBytes(argSlice);
|
||||
try
|
||||
{
|
||||
var bytes = SequenceToByteArray(argSlice);
|
||||
return AcBinaryDeserializer.Deserialize(bytes, targetType, _options);
|
||||
return AcBinaryDeserializer.Deserialize(arr, offset, length, targetType, _options);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (rented) ArrayPool<byte>.Shared.Return(arr);
|
||||
}
|
||||
|
||||
return DeserializeFromSequence(argSlice, targetType, _options);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -1222,12 +1215,27 @@ public class AcBinaryHubProtocol : IHubProtocol
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes from a ReadOnlySequence via AcBinaryDeserializer.
|
||||
/// Single-segment: zero-copy via ArrayBinaryInput. Multi-segment: SequenceBinaryInput (no copy).
|
||||
/// Exposes argSlice bytes as (array, offset, length) for offset-aware
|
||||
/// <see cref="AcBinaryDeserializer.Deserialize(byte[], int, int, Type, AcBinarySerializerOptions)"/>.
|
||||
/// <list type="bullet">
|
||||
/// <item>Single-segment: zero-copy via <see cref="MemoryMarshal.TryGetArray{T}"/> — no allocation, no copy.</item>
|
||||
/// <item>Multi-segment: <see cref="ArrayPool{T}"/>-rented contiguous copy; caller MUST return
|
||||
/// the array via <see cref="ArrayPool{T}.Return"/> when <c>rented</c> is <c>true</c>.</item>
|
||||
/// </list>
|
||||
/// Enables ArrayBinaryInput (fastest — JIT-eliminates the TryAdvanceSegment branch) regardless
|
||||
/// of whether the pipe delivered the payload as a single slab or multiple.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected static object? DeserializeFromSequence(ReadOnlySequence<byte> data, Type targetType, AcBinarySerializerOptions options)
|
||||
=> AcBinaryDeserializer.Deserialize(data, targetType, options);
|
||||
protected static (byte[] array, int offset, int length, bool rented) GetArgBytes(ReadOnlySequence<byte> argSlice)
|
||||
{
|
||||
if (argSlice.IsSingleSegment && MemoryMarshal.TryGetArray(argSlice.First, out var seg))
|
||||
return (seg.Array!, seg.Offset, seg.Count, rented: false);
|
||||
|
||||
var length = (int)argSlice.Length;
|
||||
var rentedBuf = ArrayPool<byte>.Shared.Rent(length);
|
||||
argSlice.CopyTo(rentedBuf);
|
||||
return (rentedBuf, 0, length, rented: true);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
|
|
|||
|
|
@ -196,13 +196,17 @@ public class AyCodeBinaryHubProtocol : AcBinaryHubProtocol
|
|||
if (targetType == typeof(object) && hctx?.Type != null)
|
||||
targetType = hctx.Type;
|
||||
|
||||
// 4. Deserialize — Bytes mode linearizes, Segment/AsyncSegment uses the sequence directly
|
||||
if (_protocolMode == BinaryProtocolMode.Bytes)
|
||||
// 4. Deserialize — unified ArrayBinaryInput path via GetArgBytes.
|
||||
// Single-segment: zero-copy on the pipe's slab. Multi-segment: ArrayPool-rented copy.
|
||||
// _protocolMode no longer affects receive — it is only a send-side strategy.
|
||||
var (arr, offset, length, rented) = GetArgBytes(argSlice);
|
||||
try
|
||||
{
|
||||
var bytes = SequenceToByteArray(argSlice);
|
||||
return AcBinaryDeserializer.Deserialize(bytes, targetType, Options);
|
||||
return AcBinaryDeserializer.Deserialize(arr, offset, length, targetType, Options);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (rented) ArrayPool<byte>.Shared.Return(arr);
|
||||
}
|
||||
|
||||
return DeserializeFromSequence(argSlice, targetType, Options);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue