Open jeremyVignelles opened 5 years ago
Are there any works to do for CLR on this feature? Due to the high-compatibility designs, it's extremely hard to let CLR force some new rules. Otherwise, this issue could just be done by analyzers.
First of all, IDisposable
is even not CLR-forced thing. You can write and execute classes with inconsistent finalizers and disposals.
Hi,
Are there any works to do for CLR on this feature?
I think so, or maybe that belongs to the corefx to provide such things. I see it very similar to the introduction of the non-nullable types : It's something that helps write you better code, and if first applied to .net itself, that would greatly help the adoption of such features.
Due to the high-compatibility designs, it's extremely hard to let CLR force some new rules. Otherwise, this issue could just be done by analyzers.
My proposal is not about rules, but more about helping user to spot usages that are different from the intent. Right now, when you look at a method that returns IDisposable, how do you know if you should dispose the object or not? I think people often deal with that in one of the following ways:
Implementing that in an analyser would probably be doable, however to be efficient:
First of all,
IDisposable
is even not CLR-forced thing. You can write and execute classes with inconsistent finalizers and disposals.
I don't want to force that, I want library authors to be able to express the intent : "Hey, the return of that method must be disposed", or "Give me a Disposable, and I will take care of Disposing it", and do that in a way that can be understood by analyzers. Authors can still (but are discouraged to) write code that doesn't follow the patterns.
Implementing that in an analyser would probably be doable, however to be efficient: The analyzer must have knowledge of the developer intent. Otherwise, there will be a lot of false positives or, more importantly, false negatives. Annotations must be present on each potentially problematic API, which will not likely be the case with third party libraries. Why such a library would bother to expose the API in an annotated way (with the additional dependency) if that's not something widely recognized in the .net community? The framework itself is widely recognized.
CLR has even less knowledge than analyzer. I guess you want to let CLR "auto-detect" a method's ownership of a disposable. But it's even much more harder than verifying and enforcing.
If you want something make people to follow the pattern easily, it falls into language design. Many people may have the same idea of this proposal, but it's nothing to do with it in CLR now.
You might be right, this proposal would probably better fit in csharplang, but it will probably have implications in corefx (where annotations will likely be declared). Can someone move that proposal to the appropriate repository?
@karelz Please either move as requested or close this issue and advise opening another following https://github.com/dotnet/corefx/blob/master/Documentation/project-docs/api-review-process.md.
I think one use case of this could be HttpClient
(for the aforementioned IDisposableAnalyzers), since it's one of the types that implement IDisposable
however it seems to be recommended to use single instance instead of instantiating a new one every time, hence caching.
side note regarding 'Why in .NET?' section: just dispose of both of the instances, since calling Dispose() multiple times won't cause any problems 😝
using (Stream stream = File.Open())
using (StreamReader reader = new StreamReader(stream))
{
// Some nice codes come here
}
Stream is likely not the best example of usage because you can pass it into a stream reader and transfer ownership, or specify leaveOpen
. The scenario is complex there.
If I understand what you mean, you're telling me that it doesn't hurt to dispose the item twice? Is that always true, or only on Stream? I'm not sure that is something that is always implemented, and I'm pretty sure that I have in my code objects that are not meant to be disposed twice.
Anyway, there are still the case where you get a IDisposable object as a result of a method, and where you must not dispose it because that would otherwise close it for the rest of the code.
If I understand what you mean, you're telling me that it doesn't hurt to dispose the item twice?
Yes, that is correct. It is advised against throwing anything in Dispose/only making it possible to disposing once to ensure cleanup of unmanaged resources. See Implementing a Dispose method:
To help ensure that resources are always cleaned up appropriately, a Dispose method should be callable multiple times without throwing an exception.
So I would expect it to be always implemented in such way. But regardless, I think it's a fair point, hence just a side note.
Hi, is there anything new here? Any chance to see this coming in .net 5?
Has this even been triaged/discussed?
I'm not sure starting in libraries is the right way to move this issue forward. Seems the roslyn-anlyzers is better to work on a change that adds support to an analyzer and detects attributes by name. Typically these types of annotations don't rely on strong typing in specific assembly but support the attribute definition in any assembly and match by namespace.name to allow folks to use it on older frameworks that can't change the API. The same was true for async, nullable, and others. Work out the kinks, drive consensus, and demonstrate the value. Once that happens we could consider adding such a feature to some of the API in dotnet/runtime.
We've done investment in analyzers in net5 not sure if there has been explicit consideration for IDisposable. @stephentoub @bartonjs @jeffhandley may know.
not sure if there has been explicit consideration for IDisposable.
Since this is the only code-analyzer
issue with IDisposable in it, probably not :smile:.
I agree that this will require a very similar shape to nullability annotations, in that it requires a lot of attributes to be placed throughout the codebase, and we need a very complex set of attributes; like [KeepsOwnershipUnless(nameof(leaveOpen))]
.
For inputs (including this
):
For outputs:
There might be other states.
It's as least as complex as nullability, because of something like
internal sealed class ReturnWrapper : IDisposable
{
private IDisposable? _disposable;
internal ReturnWrapper([TakesOwnership] IDisposable disposable)
{
_disposable = disposable;
}
public void Dispose()
{
_disposable?.Dispose();
_disposable = null;
}
[HowDoWeDescribeThisState]
internal void SetSuccess()
{
_disposable = null;
}
}
...
using (FileStream stream = File.Open(...))
using (ReturnWrapper wrapper = new ReturnWrapper(stream))
{
...
wrapper.SetSuccess();
return stream;
}
The SetSuccess()
method undoes an ownership transfer from a constructor. How does that get described in annotations? (The relationship for this class's members seems pretty easy to wrap my head around, but as a special case, I can't describe what it does generally). And, like nullability, no one can really determine how complex it actually is until they try decorating all of the shared runtime, since their samples will end up being a bit overly simplistic and/or operating on incomplete data.
FYI: We already have some IDisposable analyzers for detecting dispose leaks in roslyn-analyzers repo.
Dispose objects before losing scope
. Implementation at https://github.com/dotnet/roslyn-analyzers/blob/0a6e4b3935bbcdde79eb90d1760368e89ffff826/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/DisposeObjectsBeforeLosingScope.csDisposable fields should be disposed
. Implementation at https://github.com/dotnet/roslyn-analyzers/blob/0a6e4b3935bbcdde79eb90d1760368e89ffff826/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/DisposableFieldsShouldBeDisposed.csDispose methods should call base class dispose
. Implementation at https://github.com/dotnet/roslyn-analyzers/blob/f15404b312295e7cee16fb40b4c8d3f91f10f087/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/DisposeMethodsShouldCallBaseClassDispose.csCurrent implementation of these analyzers primarily relies on the following for improved analysis precision:
However, these analyzers still lead to false positives and negatives. These would definitely benefit from attribute based annotations for an improved analysis precision.
Typically these types of annotations don't rely on strong typing in specific assembly but support the attribute definition in any assembly and match by namespace.name to allow folks to use it on older frameworks that can't change the API. The same was true for async, nullable, and others. Work out the kinks, drive consensus, and demonstrate the value. Once that happens we could consider adding such a feature to some of the API in dotnet/runtime.
Agreed. Tagging @Evangelink who has shown interest in this space and is heavily involved in contributions to roslyn-analyzers repo - would you be interested in working with @bartonjs on the initial attribute design and a prototype in enhancing the existing disposable analyzers and/or adding new disposable analyzers in roslyn-analyzers repo?
I would love to :)
Another case that needs to be handled by the attribute is the case of overloads with bool leaveOpen
parameters (see https://docs.microsoft.com/en-us/dotnet/api/system.io.streamwriter.-ctor?view=netcore-3.1#System_IO_StreamWriter__ctor_System_IO_Stream_System_Text_Encoding_System_Int32_System_Boolean_)
This would be really great to have. Are there any plans to add this functionality?
Anything new on this?
How could the leaveOpen
be handled?
EDIT: Why has this issue been assigned to the "team IoT pod" dashboard?
How could the
leaveOpen
be handled?
It depends the direction we take. If we assume everything gets annotated (marked with attributes) then we could go in the direction of not having a special case. We could also have analyzers trying to recognize a pattern (as it is done with the try-pattern), for example we could assume that a method taking something disposable and having a boolean parameter named leaveOpen
would mean that the disposable parameter is not supposed to be disposed. It could also be a parameter to the attribute that would say which boolean parameter is allowing the keep-open functionality. That's just a couple of examples without giving much thoughts.
I would love for this to be added. There is no good way to indicate ownership at the moment and the analyzer is going crazy about it.
Hi,
I have thought about this for a while, and I finally got time for that, so I'm posting here. Please tell me if that's not the right place to ask for that.
Rationale
I am an average developper that wants to make sure that I don't forget to call
Dispose()
onIDisposable
. What is great is that there's a roslyn analyzer for that : https://github.com/DotNetAnalyzers/IDisposableAnalyzersHowever, sometimes, that analyzer doesn't know how things work, and a signature like
will trigger a warning, while sometime it's just returning a cached copy which is owned by the class.
There is no way for the analyser to know the semantic of what should be disposed and what should not.
Then came the idea that we could provide some attributes that could help the analyzer.
The problem
How to redistribute that kind of attributes?
Each of those options have drawbacks, and probably more than exposed above. Then we thought about it in a different way : If we truly want to analyze the behavior of the code, what if we find a more universal way : introduce the annotations directly in .net.
The proposition
Here comes the IDisposable ownership attributes. The first formal definition was made here, and I think that the API shape is understandable.
Let's dig into this : By defining these attributes, we state that "The owner of the object is responsible to
Dispose()
it".Ownership can be given (i.e. transferred to caller):
It can be taken
We can also say explicitely that the resource is kept:
Why in .NET?
Having a way to express ownership is really nice to know what is happening, and many places in the framework itself could benefit from that.
Take this code for example:
Which one should I dispose?
If
StreamReader
declared this constructor asTakesOwnership
, the question is no more.Another question I often have is : "When using
IServiceCollection.AddSingleton()
, are my objects correctlyDispose()
'd?"Potential uses
What do you think?