HavenDV / H.Pipes

A simple, easy to use, strongly-typed, async wrapper around .NET named pipes.
MIT License
219 stars 26 forks source link

Exception in IPC call #52

Open michalpaukert opened 4 months ago

michalpaukert commented 4 months ago

Description

We have recently reproduced bug with H.Pipes. Nuget version: 2.0.59 Our server receives many IPC messages (~1000/min) in many cases there was just high cpu usage but one of our collegues crashed IPC server

System.Text.Json.JsonException: '0x00' is an invalid start of a value. Path: $ | LineNumber: 0 | BytePositionInLine: 0.
 ---> System.Text.Json.JsonReaderException: '0x00' is an invalid start of a value. LineNumber: 0 | BytePositionInLine: 0.
   at void System.Text.Json.ThrowHelper.ThrowJsonReaderException(ref Utf8JsonReader json, ExceptionResource resource, byte nextByte, ReadOnlySpan<byte> bytes)
   at bool System.Text.Json.Utf8JsonReader.ConsumeValue(byte marker)
   at bool System.Text.Json.Utf8JsonReader.ReadFirstToken(byte first)
   at bool System.Text.Json.Utf8JsonReader.ReadSingleSegment()
   at T System.Text.Json.Serialization.JsonConverter<T>.ReadCore(ref Utf8JsonReader reader, JsonSerializerOptions options, ref ReadStack state)
   --- End of inner exception stack trace ---
   at void System.Text.Json.ThrowHelper.ReThrowWithPath(ref ReadStack state, JsonReaderException ex)
   at T System.Text.Json.Serialization.JsonConverter<T>.ReadCore(ref Utf8JsonReader reader, JsonSerializerOptions options, ref ReadStack state)
   at TValue System.Text.Json.JsonSerializer.ReadFromSpan<TValue>(ReadOnlySpan<char> json, JsonTypeInfo<TValue> jsonTypeInfo)
   at TValue System.Text.Json.JsonSerializer.Deserialize<TValue>(string json, JsonSerializerOptions options)
   at T Ipc.Client.PipeMessageJsonFormatter.DeserializeInternal<T>(byte[] bytes) in /_/src/Ipc/Client/PipeMessageJsonFormatter.cs:line 23
   at T H.Formatters.FormatterBase.Deserialize<T>(byte[] bytes) in /_/src/libs/H.Formatters/FormatterBase.cs:line 34
   at async void H.Pipes.PipeConnection<T>.Start()+(?) => { }

This is my custom json formatter

public sealed class PipeMessageJsonFormatter : SystemTextJsonFormatter
{
    protected override byte[] SerializeInternal(object? obj)
    {
        if (obj is PipeMessage message)
        {
            var serialize = JsonSerializer.Serialize<PipeMessage>(message, new JsonSerializerOptions());
            var bytes = System.Text.Encoding.UTF8.GetBytes(serialize);
            return bytes;
        }

        throw new InvalidOperationException($"Type of message {obj?.GetType()} is not supported");
    }

    protected override T DeserializeInternal<T>(byte[] bytes)
    {
        var json = System.Text.Encoding.UTF8.GetString(bytes);
        var deserialized = JsonSerializer.Deserialize<T>(json);
        return deserialized ?? throw new InvalidOperationException($"Unable to deserialize message: {json}");
    }
}

We have created process dump before crash, but I am not sure how this could happend. I have investigated dump and there is buffer which is pretty high 1734681213 bytes. (I am able to provide dump) In my opinion there are still incomming IPC messages, but nobody can read.

Steps to reproduce the bug

I am not sure if there is 100% way to reproduce this issue.

Expected behavior

No response

Screenshots

image

NuGet package version

2.0.59

Platform

Console

IDE

Visual Studio 2022

Additional context

No response

HavenDV commented 4 months ago

Unfortunately, the bulk of the work on this library was done five years ago, and I didn’t really include any heavily loaded scenarios (although I wouldn’t say that your example is about a heavy load). There are likely still some multi-threading issues at the library level that lead to this in rare cases, and it's best to create tests/benchmarks to identify this and fix it. The problem here is that my free time is quite limited right now and I can't give a clear time frame regarding this (for me it looks like a full 4-8 hour day of work testing and fixing this) There's a small chance that it's all down to implementation at the system API level, but I wouldn't bet too much on it. Then the problem is more serious and we need to think about some pipe blocking between processes

michalpaukert commented 4 months ago

I am using this package for more then 1 year and this issue occured just recently on one of our virtual machines more then once. If you could give me a hint I could try to investigate.

HavenDV commented 4 months ago

The first step is to recreate the high load within a single test and get your problem replicated. Moreover, as I understand it, this behavior may be platform-specific and appear only on Windows (I mainly use macOS for work). You can help even if you just provide a PR with this test. The second step is to find the problem. And here, if I had time, I would rewrite part of the code to use Dataflow(https://learn.microsoft.com/en-us/dotnet/standard/parallel-programming/dataflow-task-parallel-library)