Table of Contents

NBMsgPack033: Async converters should return writers

Custom converters (classes that derive from MessagePackConverter<T>) that override the WriteAsync method should return the MessagePackWriter struct that it creates.

Consider that the location of the diagnostic may not always indicate the location of the underlying issue. When resolving this violation, consider the various branching, loops, etc. as the problem may only be present when the code takes certain code paths.

Example violation

In the following WriteAsync method, a synchronous writer is created with CreateWriter but not returned at two points where it is necessary.

public override async ValueTask WriteAsync(MessagePackAsyncWriter writer, SomeCustomType? value, SerializationContext context)
{
    MessagePackWriter syncWriter = writer.CreateWriter();
    if (value is null)
    {
        syncWriter.WriteNil();
        return; // OOPS: exit without returning writer
    }

    syncWriter.Write(value.SeedCount);

    // OOPS: await without returning writer
    await writer.FlushIfAppropriateAsync(context);
}

Resolution

Add calls to ReturnWriter before any exit from the method or any await expression.

public override async ValueTask WriteAsync(MessagePackAsyncWriter writer, SomeCustomType? value, SerializationContext context)
{
    MessagePackWriter syncWriter = writer.CreateWriter();
    if (value is null)
    {
        syncWriter.WriteNil();
        writer.ReturnWriter(ref syncWriter);
        return;
    }

    syncWriter.Write(value.SeedCount);

    writer.ReturnWriter(ref syncWriter);
    await writer.FlushIfAppropriateAsync(context);
}