Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"using" statment inside method can cause troubles of data corruption or Access violation?

I have a task, that set data into a FIFO, then another thread read this data inside the FIFO one by one and send it via network later. The data converted to byte array when call FIFO.Add, as following:

public byte[] ByteArraySerialize()
{
    using (MemoryStream m = new MemoryStream())
    {
        using (BinaryWriter writer = new BinaryWriter(m))
        {
            writer.Write((int)this.Opcode);
            writer.Write(this.Data);
        }
        return m.ToArray();
    }
}

My question: Is it possible that the data will be corrupted or disposed before the sender thread reads it from FIFO? My question is to understand using inside method: Is this the way of using the using inside a method may cause GC to remove the MemoryStream, before the thread reads the data lets say after few second or minutes after this data entered the FIFO?

like image 783
Joseph Avatar asked Dec 16 '22 04:12

Joseph


1 Answers

There are multiple ways to read this question but let's start with the obvious way, the way it was written:

Is this way of using the "using" inside a method may cause GC to remove the Memory Stream, before the thread read the data lets say after few second or minutest after this data enter the FIFO?

No. This will not be a problem. If you are able to read the data as part of the call to .ToArray(), then you already have a copy of the data. If GC later on collects the stream, the array will live on. To be clear, in relation to GC, if you can read a good copy of the internals of the stream at the point where you call .ToArray(), then that array will be OK afterwards. As per the documentation, you're getting a copy of the internal data, not a reference to it, and even so, if you have a reference to some internal data structure, GC will not be able to collect it.

However, another interpretation could be this: Is there something wrong with this code?

And well, yes, and no.

The current implementation of BinaryWriter will dispose of the underlying stream when the writer instance is disposed of. This means that the MemoryStream will be disposed of.

Let me copy your code and add some comments:

public byte[] ByteArraySerialize()
{
    using (MemoryStream m = new MemoryStream())
    {
        using (BinaryWriter writer = new BinaryWriter(m))
        {
            writer.Write((int)this.Opcode);
            writer.Write(this.Data);
        }

        // m is really disposed here
        return m.ToArray();
    }
}

Does this make a difference? Well, no. In the current implementation, disposing of the memory stream will not in any way trash it. But there is nothing guaranteed about the current implementation or its future, this is undocumented behavior. If you want this code to be stable and trustworthy for future versions or hotfixes to .NET, I would not write it like this.

As such, I would not use this way of doing it. I would rewrite the code as follows:

using (MemoryStream m = new MemoryStream())
using (BinaryWriter writer = new BinaryWriter(m))
{
    writer.Write((int)this.Opcode);
    writer.Write(this.Data);

    writer.Flush();
    return m.ToArray();
}

This will ask the writer to flush all the data, and then you make a copy of the internal array of the memory stream, before that instance is disposed.

Either that, or use the overloaded constructor and ask the writer to leave the stream open:

using (MemoryStream m = new MemoryStream())
{
    using (BinaryWriter writer = new BinaryWriter(m, Encoding.UTF8, true))
    {
        writer.Write((int)this.Opcode);
        writer.Write(this.Data);
    }

    // m is no longer disposed here
    return m.ToArray();
}
like image 84
Lasse V. Karlsen Avatar answered May 09 '23 17:05

Lasse V. Karlsen