Closed juliusfriedman closed 4 years ago
I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label.
cc @danmosemsft @jkotas
I do not understand what would Object.Resurrect
do. For example, how is it different from GC.ReRegisterForFinalize
?
It basically doesn't differ at all from GC.ReRegisterForFinalize
I was just proposing an API which would do that work for the user with respect to the disposable instance (hence where the CommonDispoable
comes in).
I also think that having the mechanism already in place for users of ref structs is important and that GC.ReRegisterForFinalize
is not generic.
The pattern above would cover both ref structs and other implementer of the IDisposable
where as today you can't effectively do it without inter-op I don't think. (GC.ReRegisterForFinalize
on a ref struct etc)
@juliusfriedman an API proposal should explicitly list the proposed API diff please.
Sorry wasn't totally sure where to plop them at first.
I don't understand. A ref struct can't be finalizable, isn't subject to GC, etc; what does it have to do with resurrection? What does something that meets the unmanaged constraint have to do with resurrection?
And what benefit is there to ReRegisterForFinalize being generic?
I don't understand. A ref struct can't be finalizable, isn't subject to GC, etc; what does it have to do with resurrection? What does something that meets the unmanaged constraint have to do with resurrection?
And what benefit is there to ReRegisterForFinalize being generic?
It also can't implement Equals right?
When I treat a ref struct as a disposable tis Dispose method is called without boxing it and I believe in some cases that call will be made from the finalizer queue [needs citation] so I treat that at a place where this would be beneficial although one can already work around that by implementing an interface on the ref struct with release semantics right? Not on a ref struct though... (Which I guess I understand...) [now we have these invisible interfaces or shapes which the CLR just happens to know about...]
For the most common cases the benefit is simply not boxing.
For the most common cases the benefit is simply not boxing.
Ref structs can never end up on the heap (and as a result can never end up in the finalizer queue), and they can't be used as the T inside a generic method. For example:
public ref struct MyRefStruct { }
public void DoSomething<T>() { /* no-op */ }
public void Main()
{
DoSomething<MyRefStruct>(); // compiler error; can't use MyRefStruct as 'T'
}
I believe in some cases that call will be made from the finalizer queue
No. A ref struct can't live on the heap, it can't be boxed, it can't have a finalizer, it won't ever be in the finalizer queue.
For the most common cases the benefit is simply not boxing.
Ref structs can never end up on the heap (and as a result can never end up in the finalizer queue), and they can't be used as the T inside a generic method. For example:
public ref struct MyRefStruct { } public void DoSomething<T>() { /* no-op */ } public void Main() { DoSomething<MyRefStruct>(); // compiler error; can't use MyRefStruct as 'T' }
Well then I suppose my proposed API won't be needed or even beneficial for ref structs 👍
ATP the only benefit of the proposed API (for the class type) would be the non boxing of the object which I myself don't believe is needed in this scenario.
Also thank you for the clarification on the ref struct finalizer, not sure where I got that from...
I believe in some cases that call will be made from the finalizer queue
No. A ref struct can't live on the heap, it can't be boxed, it can't have a finalizer, it won't ever be in the finalizer queue.
noted, Thank you!
You guys are free to close this one, sorry for any detritus. (Still feel like the CommonDisposable and the IDisposed are important pieces here, especially when sharing resources within the ref structs) Disposable Ref Structs Article
In summary We can treat disposable ref structs as lightweight types that have A REAL destructor, known from C++. It will be executed as soon as the corresponding instance goes out of the scope of the underlying using statement (or enclosing scope in case of using declaration). For sure it is not a feature that will suddenly become extremely popular in writing regular, business-driven code. But few layers below, when writing high-performant low-level code, it is worth to know about such possibility!
The important distinction here is that a ref struct is never on the finalizer queue and therefor won't benefit from the API I proposed. ( I guess struct
in general can't really get on the finalizer queue, please correct me if I am wrong as when it's boxed only the box would end up on the finalier queue and then noop if anything. )
Unfortunately that still doesn't solve the sharing of resources issue, e.g. I can only implement Dispose
for IDisposable
, not my own interface e.g IDisposed
or any other variation.
Structs can't inherit either so that means I have to own a lot of repeated code (violation of DRY) or I can opt to use a generic struct to which I can still use ref
to pass it around like a ref
struct as well as have the ability to implement interfaces. (talk about splitting hairs) IStruct
Do with this issue as you will, close it / break it down into other issues etc.
I just had these thoughts while I was writing / reviewing some code and wanted to share my thoughts.
I guess struct in general can't really get on the finalizer queue
Good question. It's not the Dispose
method that adds something to the finalizer queue, but rather the existence of a Finalize
method. I don't know if it's legal in .NET Core for a struct to have a finalizer. But assuming for a minute that it were legal, IMO it'd be truly bizarre if anybody were ever to do this.
I guess struct in general can't really get on the finalizer queue
Good question. It's not the
Dispose
method that adds something to the finalizer queue, but rather the existence of aFinalize
method. I don't know if it's legal in .NET Core for a struct to have a finalizer. But assuming for a minute that it were legal, IMO it'd be truly bizarre if anybody were ever to do this.
Thank you.
I do not believe a struct can ever have a finalizer yet an object does which is where I got confused I guess. (ValueType)
I am also not sure if this is a C# limitation e.g. if in IL you can definite a struct with a finalizer... but I am fairly certain that no type of struct can have a finalizer as written from the C# compiler.
Thank you for sharing the idea. I think it is best to close this one.
I don't know if it's legal in .NET Core for a struct to have a finalizer.
@GrabYourPitchforks For the record, it isn't. I want to say this limitation exists since C#/.NET 1.0, but it would be great if I could find a reference to back that up.
I don't know if it's legal in .NET Core for a struct to have a finalizer.
@GrabYourPitchforks For the record, it isn't. I want to say this limitation exists since C#/.NET 1.0, but it would be great if I could find a reference to back that up.
Let me know if you ever find that reference.
Runtime ignores finalizers on structs: https://github.com/dotnet/runtime/blob/225673de28e67caa6a7fb6cf275b566996fc1570/src/coreclr/src/vm/methodtablebuilder.cpp#L11173 .
It has been like that since forever. You can see the same check in SSCLI sources: https://github.com/SSCLI/sscli20_20060311/blob/master/clr/src/vm/methodtable.cpp#L1073
Background and Motivation
Sometimes you finalize an object and you change you mind, perhaps you should be using pools but that is a different issue.
Proposed API
Ideally also a base disposable class with full async support but is not required.
A newer this
See the interface
As well as the following methods.
Usage Examples
and
Alternative Designs
ReRegisterForFinalize<T>
same overloadsPooling.
Risks
Low, better than people getting it wrong.