Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Regression: ObjectDisposedException in overridden FileStream.Dispose(bool disposing) #82186

Closed
pharring opened this issue Feb 15, 2023 · 5 comments · Fixed by #82874
Closed
Milestone

Comments

@pharring
Copy link
Contributor

Description

If you have a class derived from FileStream that overrides void Dispose(bool disposing) and accesses the Length property, you'll get an ObjectDisposedException when the stream is disposed asynchronously via IAsyncDisposable.DisposeAsync (await using ...)

Reproduction Steps

using System;
using System.IO;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        await using MyStream file = new(Path.GetTempFileName(), FileMode.Open);
    }
}

class MyStream : FileStream
{
    private bool _isDisposed;

    public MyStream(string path, FileMode mode) : base(path, mode)
    {
    }

    protected override void Dispose(bool disposing)
    {
        if (!_isDisposed)
        {
            Console.WriteLine("Dispose({0}) called. Length {1}.", disposing, Length);
            _isDisposed = true;
        }

        base.Dispose(disposing);
    }
}

Expected behavior

Dispose(True) called. Length 0.

Actual behavior

Unhandled exception. System.ObjectDisposedException: Cannot access a closed file.
   at System.IO.FileStream.get_Length()
   at MyStream.Dispose(Boolean disposing) in D:\Throwaway\ConsoleApp26\ConsoleApp26\Program.cs:line 25
   at System.IO.FileStream.DisposeAsync()
   at Program.Main(String[] args) in D:\Throwaway\ConsoleApp26\ConsoleApp26\Program.cs:line 9
   at Program.<Main>(String[] args)

Regression?

Worked in .NET Core 3.1, .NET 5 and .NET 6 prior to 6.0.14 release

Known Workarounds

Override DisposeAsync

Configuration

.NET 6.0.14
Windows 11
x64

Other information

This is similar to #82176 (probably has the same root cause), but it demonstrates that the obvious workaround (override Dispose(bool disposing)) isn't sufficient.

@ghost ghost added the untriaged New issue has not been triaged by the area owner label Feb 15, 2023
@ghost
Copy link

ghost commented Feb 15, 2023

Tagging subscribers to this area: @dotnet/area-system-io
See info in area-owners.md if you want to be subscribed.

Issue Details

Description

If you have a class derived from FileStream that overrides void Dispose(bool disposing) and accesses the Length property, you'll get an ObjectDisposedException when the stream is disposed asynchronously via IAsyncDisposable.DisposeAsync (await using ...)

Reproduction Steps

using System;
using System.IO;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        await using MyStream file = new(Path.GetTempFileName(), FileMode.Open);
    }
}

class MyStream : FileStream
{
    private bool _isDisposed;

    public MyStream(string path, FileMode mode) : base(path, mode)
    {
    }

    protected override void Dispose(bool disposing)
    {
        if (!_isDisposed)
        {
            Console.WriteLine("Dispose({0}) called. Length {1}.", disposing, Length);
            _isDisposed = true;
        }

        base.Dispose(disposing);
    }
}

Expected behavior

Dispose(True) called. Length 0.

Actual behavior

Unhandled exception. System.ObjectDisposedException: Cannot access a closed file.
   at System.IO.FileStream.get_Length()
   at MyStream.Dispose(Boolean disposing) in D:\Throwaway\ConsoleApp26\ConsoleApp26\Program.cs:line 25
   at System.IO.FileStream.DisposeAsync()
   at Program.Main(String[] args) in D:\Throwaway\ConsoleApp26\ConsoleApp26\Program.cs:line 9
   at Program.<Main>(String[] args)

Regression?

Worked in .NET Core 3.1, .NET 5 and .NET 6 prior to 6.0.14 release

Known Workarounds

Override DisposeAsync

Configuration

.NET 6.0.14
Windows 11
x64

Other information

This is similar to #82176 (probably has the same root cause), but it demonstrates that the obvious workaround (override Dispose(bool disposing)) isn't sufficient.

Author: pharring
Assignees: -
Labels:

area-System.IO

Milestone: -

@stephentoub
Copy link
Member

cc: @adamsitnik

@bartonjs
Copy link
Member

In your code you're reading Length without checking disposing. You should find that disposing is false in this case, meaning you should not talk about anything other than directly-held native resources.

@bartonjs
Copy link
Member

Oh, wait, it's calling it with true... you logged that. Huh.

@pharring
Copy link
Contributor Author

Prior to .NET 6.0.14 it was calling with true.
With 6.0.14 and 7.x it is calling with false
That is a behavioral change from .NET Core 3.1 and .NET 5.0

@ghost ghost added the in-pr There is an active PR which will close this issue when it is merged label Mar 2, 2023
@jozkee jozkee added this to the 8.0.0 milestone Mar 6, 2023
@ghost ghost removed the untriaged New issue has not been triaged by the area owner label Mar 6, 2023
@ghost ghost removed the in-pr There is an active PR which will close this issue when it is merged label Apr 4, 2023
@ghost ghost locked as resolved and limited conversation to collaborators May 4, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants