-
Notifications
You must be signed in to change notification settings - Fork 107
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
Memory management doc #899
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
||
In the former, the object is implemented purely in C# and its lifetime is managed by the .NET | ||
garbage collector. C#/WinRT only comes into play when this C# object is passed across the ABI to a | ||
WinRT function. When this happens, C#/WinRT creates a CCW for it using the .NET 5 ComWrappers API |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Link to the ComWrappers
API? https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.comwrappers
know about all the references to it from another reference tracking system like the .NET | ||
garbage collector. This allows XAML to track scenarios where objects may have circular references | ||
or only have references to it from objects that are pending clean up. Specifically, when a C# wrapper | ||
is created for a XAML runtime tracked object (implements `IReferenceTracker`), the XAML runtime |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
serves the purpose of documenting how C#/WinRT interacts with all 3 systems to correctly manage the | ||
lifetime of projected WinRT objects. | ||
|
||
### COM reference tracking |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be easier to augment this with a series of enumerated steps in the process.
Co-authored-by: Aaron Robinson <[email protected]>
which C#/WinRT does behind the scenes when a C# class extends such a projected type. In COM aggregation, | ||
there is 2 objects in play: the outer object which is the CCW for the C# object and the inner object | ||
which is the WinRT object being extended. Both these objects are made to look like one object known as the | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unexpected line break? Also some line breaks below this when viewing the preview non-markdown version of the doc
|
||
The above describes what typically happens for any natively implemented WinRT object that C#/WinRT | ||
projects. There are some differences to this when the object is instead a C# implemented object that is | ||
projected into WinRT via a COM callable wrapper (CCW). This is done via a C# class implementing a set of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe factor out these into 2 separate sections? E.g.
C# class implementing WinRT interface
...
C# class extending an unsealed WinRT type
...
|
||
### COM reference tracking | ||
|
||
Each WinRT object that we project is based on COM and implements a set of interfaces. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we -> C#/WinRT
|
||
Each WinRT object that we project is based on COM and implements a set of interfaces. | ||
As per the COM design, every COM interface implements `IUnknown` which has an `AddRef` and `Release` | ||
function. C#/WinRT calls the `AddRef` function anytime it gets a new reference to a WinRT object |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You mention CCWs below. Maybe introduce IObjectReference as the RCW here.
As per the COM design, every COM interface implements `IUnknown` which has an `AddRef` and `Release` | ||
function. C#/WinRT calls the `AddRef` function anytime it gets a new reference to a WinRT object | ||
which C#/WinRT holds onto using an `IObjectReference` instance. It also calls `AddRef` whenever it gives | ||
out a reference to one of these objects across the ABI as an out parameter. C#/WinRT calls the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should introduce this acronym too: Application Binary Interface (ABI)
object alive and that is managed by the .NET runtime and `ComWrappers` implementation. | ||
|
||
|
||
In the latter scenario, extending an unsealed WinRT type is typically done via COM Aggregation |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"latter" here is referring back a bit - may help to provide some structure to the doc (section titles)
|
||
In the latter scenario, extending an unsealed WinRT type is typically done via COM Aggregation | ||
which C#/WinRT does behind the scenes when a C# class extends such a projected type. In COM aggregation, | ||
there is 2 objects in play: the outer object which is the CCW for the C# object and the inner object |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there are two
composed object. To achieve that, the outer object delegates calls for any of the inner object | ||
|
||
interfaces that aren't overridden to the inner object. Any calls for interfaces that are only | ||
implemented on the outer object or is overridden by the outer object or is for the `IUnknown` interface |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
or are overridden ..., or are for ....
|
||
|
||
In the latter scenario, extending an unsealed WinRT type is typically done via COM Aggregation | ||
which C#/WinRT does behind the scenes when a C# class extends such a projected type. In COM aggregation, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd include links inline in the body of the text, rather than at the end of the doc, like COM Aggregation here (https://docs.microsoft.com/en-us/windows/win32/com/aggregation)
would be handled by the outer object itself. The last part means that the lifetime and the COM reference | ||
counting of this aggregated object is maintained by the outer object and more specifically its `IUnknown` | ||
implementation on the CCW from ComWrappers. This is where the standard COM reference tracking | ||
convention described earlier starts to differ. As we know for CCWs, there are 2 things which |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
spell out any number less than 10 (standard technical writing guideline)
other than facilitating QI calls for them from the native side where `Release` isn't called right after. | ||
The recommendation for tear off interfaces which do want to support such uses on aggregated objects | ||
is that they can continue to be constructed on demand upon the first QI for it, but the interface | ||
should not be cleaned up until the object is cleaned up even if there are no longer any reference |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
references
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we be more crisp about the recommendation - that a tear-off implementor should cache all instances to prevent premature destruction?
objects if one of these interfaces need to be QIed for by the composed object as part of the | ||
projection implementation. This is because a `Release` would happen right after which would trigger | ||
the cleanup of the interface as its lifetime isn't tied to the outer. Given that tear off interfaces | ||
are rare and not typically used by C# consumers, C#/WinRT today doesn't address this |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"not typically used by C# consumers" ? the tear-off implementor has no idea what client code is using it
or only have references to it from objects that are pending clean up. Specifically, when a C# wrapper | ||
is created for a XAML runtime tracked object (implements `IReferenceTracker`), the XAML runtime | ||
needs to be informed of it by a call to `ConnectFromTrackerSource` on `IReferenceTracker`. This is | ||
done by the ComWrappers implementation when an RCW is created. After that, any references to that |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(introduce 'RCW' above)
needs to be informed of it by a call to `ConnectFromTrackerSource` on `IReferenceTracker`. This is | ||
done by the ComWrappers implementation when an RCW is created. After that, any references to that | ||
object that are tracked by the other reference tracking system (.NET garbage collector in this case) | ||
needs to be informed to XAML by a call to `AddRefFromTrackerSource`. This is done by both C#/WinRT and |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
need
needs to be informed to XAML by a call to `AddRefFromTrackerSource`. This is done by both C#/WinRT and | ||
ComWrappers after any `AddRef` call to increment the COM reference count. Similarly, before the | ||
`Release` call, there would be a `ReleaseFromTrackerSource` to indicate a reference on the object | ||
was released. When the RCW is destructed, there would similarly be a call to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
destructed -> finalized
Documenting what we learned when working on the fix for the memory management issues in C#/WinRT.
Fixes #810