-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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
[NativeAOT + Objective-C Marshal] request for advise on restricted GC callouts #80032
Comments
Tagging subscribers to this area: @dotnet/gc Issue DetailsBackgroundThe Objective-C marshal feature creates reference-counted GC handles. In the Native-AOT implementation, the callbacks for these GC handles calls into C# code. This feature was implemented in #78280. According to the rules for these sort of callbacks, the C# code cannot allocate GC memory or take locks. ProblemThe C# code currently uses Potential SolutionsDon't use the ConditionalWeakTableInstead of using the ConditionalWeakTable and hoping it and every function it calls obeys the rules for restricted call outs, create a new facility that is specifically designed with these limitations in mind. One approach is copy the approach CoreCLR uses. The extra data could be stored in the sync table entry for the object (analogous to the sync block entry in CoreCLR). Something like this: Change ConditionalWeakTable to not allocateLogically, if an object has never had a hash code assigned to it, it cannot have previously been inserted into a Request for adviseDo either of those approaches seem reasonable?
|
@VSadov Do you have an option? I like ConditionalWeakTable change better. It is simpler and it makes ConditionalWeakTable better for other uses as well. The change can be under NATIVEAOT ifdef for now if you do not want to implement TryGetHashCode on other runtimes. |
Tagging subscribers to this area: @agocke, @MichalStrehovsky, @jkotas Issue DetailsBackgroundThe Objective-C marshal feature creates reference-counted GC handles. In the Native-AOT implementation, the callbacks for these GC handles calls into C# code. This feature was implemented in #78280. According to the rules for these sort of callbacks, the C# code cannot allocate GC memory or take locks. ProblemThe C# code currently uses Potential SolutionsDon't use the ConditionalWeakTableInstead of using the ConditionalWeakTable and hoping it and every function it calls obeys the rules for restricted call outs, create a new facility that is specifically designed with these limitations in mind. One approach is copy the approach CoreCLR uses. The extra data could be stored in the sync table entry for the object (analogous to the sync block entry in CoreCLR). Something like this: Change ConditionalWeakTable to not allocateLogically, if an object has never had a hash code assigned to it, it cannot have previously been inserted into a Request for adviseDo either of those approaches seem reasonable?
|
The whole part about calling managed code from GC feels fragile to me, but unavoidable. My intuition is that we should take any opportunity to constrain what code may possibly need to run this way. From this point it would be better to put interop stuff in the syncblock. That would serve the distinct purpose and will have less impact from the layering point of view. - fewer components will need to know about it.
Let me think a bit more about long term viability of tweaking the ConditionWeakTable. |
I also think the interop info referenced via the syncblock could be a bit faster to retrieve (no hashtable lookup), but I am unsure how meaningful the difference will be for a typical interop scenario. |
The reason that tipped me over to prefer ConditionalWeakTable fix was that it makes the runtime better for other scenarios. I expect that it is fairly common to use try pattern with ConditionalWeakTable. It is beneficial to avoid assigning the hashcode when the try pattern is about to fail: There is a local saving from skipping the hashcode assignment and lookup that is guaranteed to fail, and there is potential global saving from reducing number of syncblocks that need to be created due to needing both hashcode and lock.
There are 3 methods (1 worker method and 2 wrappers) on
We can consider making the
Right, the interop scenarios that use ConditionalWeakTable are not the most perf critical ones. |
Yes, that did occur to me. Not sure if that is universally useful, as hashtable lookups are often expected not to miss, but for a conditional weak table it is definitely useful. I guess we can go with the TryGetHashcode/CWT change for the above considerations and because it is a smaller change that does not preclude the other approach if we run into more issues.
|
* [NativeAOT] Fix Objective-C reference tracking Fixes #80032 * Move implementation-specific comment out of public doc comment * Duplicate code for TryGetHashCode implementations. * Replace comments with a passing test. * Add moke test for restricted callouts. * Remove TryGetHashCode from Mono It does not guarantee that hash codes are non-zero. * Add test coverage for untracked objective objects. * Implement RuntimeHelpers.TryGetHashCode for Mono * Remove unneeded MONO_COMPONENT_API * Remove Mono intrinsic for InternalGetHashCode This is dead code because this method no longer lives on System.Object. * Move interpreter transforms to correct class. * Rename and move icall to match convention. Co-authored-by: Jan Kotas <[email protected]>
Background
The Objective-C marshal feature creates reference-counted GC handles. In the Native-AOT implementation, the callbacks for these GC handles calls into C# code. This feature was implemented in #78280.
According to the rules for these sort of callbacks, the C# code cannot allocate GC memory or take locks.
Problem
The C# code currently uses
ConditionalWeakTable
. This class callsRuntimeHelpers.GetHashCode
to get a hash code for object. This in turn can potentially allocate memory or attempt to acquire a lock, in violation of the rules for restated callouts.Potential Solutions
Don't use the ConditionalWeakTable
Instead of using the ConditionalWeakTable and hoping it and every function it calls obeys the rules for restricted call outs, create a new facility that is specifically designed with these limitations in mind.
One approach is copy the approach CoreCLR uses. The extra data could be stored in the sync table entry for the object (analogous to the sync block entry in CoreCLR). Something like this:
AustinWise@286ced2
Change ConditionalWeakTable to not allocate
Logically, if an object has never had a hash code assigned to it, it cannot have previously been inserted into a
ConditionalWeakTable
. We can take advantage of this fact to avoid allocating and take locks when locking up an object. Something like this:AustinWise@d457357
Request for advise
Do either of those approaches seem reasonable?
The text was updated successfully, but these errors were encountered: