-
Notifications
You must be signed in to change notification settings - Fork 4.9k
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
[API Proposal]: Allow the user to provide state for a ComWrappers.GetOrCreateObjectForComInstance operation #113622
Comments
Tagging subscribers to this area: @dotnet/interop-contrib |
What is an example of state that is needed? Are their specific downside risks or performance issues with the |
+ /// <summary>
+ /// The managed object doesn't keep the native object alive. It represents an equivalent value.
+ /// </summary>
+ NonWrapping = 0x10 I'm not sure what this does in practice. Can you please update the summary to describe what users can expect from
This change means users will have a very different implementing experience. At present, since it is |
I think in this case I would prefer if we didn't change the signature for |
We should also error out if this is an input value to any |
I've updated the proposal above with the list of behavior changes this API would cause. I'll include them here as well:
This is primarily to ensure that the following scenarios don't occur:
I'm fine with |
@jkoritzinsky I'm assuming the following relationship between the APIs:
|
|
Right. It shouldn't call any create functions. Then the last thing, |
It can be Nit: The proposal should include existing |
I think if the user knows the COM object that they're passing in is something they'd want to set NonWrapping for, then we should allow them to be explicit and avoid interrogation QI's in their ComWrappers implementation. |
Was re-reading this, question: if it's true that
Does this mean that you can only ever mutate the create object flags if you're also passed an argument? That is, if someone calls Wondering if it might be a bit weird and/or unexpected that the ability to mutate flags is tied to having an additional state being passed in, regardless of whether that |
Yes.
Yes. The existing |
We should be careful with this. The issue becomes we create situations where prior to calling |
I guess I was more concerned about implicit calls from the tracker manager, since you can't control those, and those would pass no state. Meaning that you'd also not be able to tweak flags in that scenario. Perhaps it would make sense to update the tracker manager to invoke the new overload instead? That way people wanting to use the new overload would be able to always do so (either manually, or the runtime would do it from the tracker manager), and people only using the old one would also still work (as the default implementation of the new one would just call the old one and not mutate the flags). Would that make sense? 🤔 |
The concern then would be properly documenting when state object would be passed. Ostensibly people say to themselves, I want to always call the API with the state and then they do something odd or naive like implementing the stateful to always expect the state and/or never forward to the non-state version or make it throw. The issue becomes we start changing behavior that has negative downstream impact for no win and it simply makes us write complex documentation that is painful to be accurate about and even more so to test. The alternative is to leave it as-is. That has the benefit of keeping the established and predictable behavior and if we see users have a need, not desire but actual blocking need, then we can make an informed decision based on data rather than peoples wants and desires. This is part of leaving the other |
Makes sense to me, thank you for the additional context! 🙂 |
Aaron and I spoke offline about the behavior of tweaking flags. Instead of tweaking flags in-flight and requiring ComWrappers to validate that only valid flags are passed to I'll update the proposal with the changes. |
@jkoritzinsky I like the new idea of output-only flags, would it also be possible to have one to disable Something like this perhaps: public enum CreatedWrapperFlags
{
NonTrackerObject = 0x1,
NonWrapping = 0x2
} Basically it'd be a similar optimization to #113632. Thoughts? |
How do we know that was done properly? I don't think it is appropriate for these optimization to occur as it influences our own checks for correctness. |
My rationale was that it'd be the responsibility of the interop stack to ensure it's correct, in the same way as if we weren't passing |
This isn't about memory leaking. It is about the runtime doing the work it needs to do to provide guarantees. The |
I see, that's fair. Once again I appreciate you taking the time to share the additional context 🙂 |
I've thought more about this. I'm going to defer to @jkoritzinsky on this one. Given the number of teams (assuming 1) that will be using this API to add support for the Up to you @jkoritzinsky. |
I think I'd prefer going in the opposite direction: Adding a Then That way, we don't have a "yes do that. No wait actually no don't do that" API design. CsWinRT could move their calls that will pass state to say "don't use tracker support by default" and allow the implementation to decide if its needed. The places where the runtime calls into ComWrappers would continue to ask for TrackerObject unconditionally. |
Mmh not entirely sure I follow, isn't it the same anyway just the opposite way? "don't need tracker" "oh actually yes". The idea with Basically it doesn't seem unlike how EDIT: I re-read your message, to clarify, I see what you mean now, we could use either approach in CsWinRT, I guess with the one currently in the proposal we'd just update our callsites to never pass EDIT 2: thinking about this some more, the only possible concern I have with setting ref tracker later is: Lines 908 to 911 in 5e7d681
How would this work if at this point |
This is the sort of implementation detail and complexity I was alluding to in #113622 (comment). I genuinely don't like the |
I looked at that after proposing the above. That check there is only for the .NET 5 backcompat scenario for CsWinRT 1.x. If you always specify an inner in the aggregation scenario, then there would be no observable change in behavior. |
Ooh I see. Correct me if I'm wrong (just making sure I'm fully following here):
So we keep just 1 QI for Seems great! Thank you for looking into this and updating the proposal as well 🙂
Not entirely sure I understand why that check is in the NAOT implementation though. CsWinRT 1.x doesn't support AOT at all. Using CsWinRT on NAOT requires at least CsWinRT 2.1, which always passes the inner. Perhaps a leftover from the port? |
It's a .NET 5 backcompat scenario, but because we support the same experience on CoreCLR and NativeAOT, we have the same code even though CsWinRT doesn't use it (as theoretically someone could depend on it and we don't have a good justification to explicitly make the behavior differ.) |
Background and motivation
As part of the Interop Type Mapping Proposal, one important aspect we've discussed for foreign language projections to improve their performance (and avoid relying on the type map in the majority of cases), is to use call-site information about types to avoid needing to go through the map for strongly-typed APIs.
Currently, there is no mechanism to pass down additional state to
ComWrappers.CreateObject
, so any foreign language projection based on COM (ie. CsWinRT) must use something else such as[ThreadStatic]
statics on theirComWrappers
implementation to pass down call-site state as a side channel.Additionally, CsWinRT has had to add additional tracking and lifetime extensions to support some of their primitives that should be immediately unwrapped, ie.
IReference<T>
implementations, as mentioned in #113591.In #113591, an API change is proposed to allow CsWinRT to use another side-channel to return an object without going through ComWrappers. In the comments, a
protected
property is proposed to provide operation-scoped state.This API proposal aims to solve both of the above problems. It allows the user to provide their own "state" object that will be available in
CreateObjectWithState
and allows the user to specify flags based on the created wrapper object with thewrapperFlags
out parameter.API Proposal
API Usage
Alternative Designs
The designs in #113581 and #113591 are alternative options we've considered.
Risks
These API changes would make it less obvious which members on ComWrappers to implement in subclasses, as there are now two overloads of
CreateObject
.The text was updated successfully, but these errors were encountered: