@@ -698,17 +698,29 @@ private ConcurrentDictionary<IntPtr, IObjectReference> Make_CachedContext()
698
698
private volatile bool _isAgileReferenceSet ;
699
699
private volatile AgileReference __agileReference ;
700
700
private AgileReference AgileReference => _isAgileReferenceSet ? __agileReference : Make_AgileReference ( ) ;
701
- private AgileReference Make_AgileReference ( )
701
+ private unsafe AgileReference Make_AgileReference ( )
702
702
{
703
- Context . CallInContext ( _contextCallbackPtr , _contextToken , InitAgileReference , null ) ;
703
+ Context . CallInContext (
704
+ _contextCallbackPtr ,
705
+ _contextToken ,
706
+ #if NET && CsWinRT_LANG_11_FEATURES
707
+ & InitAgileReference ,
708
+ #else
709
+ InitAgileReference ,
710
+ #endif
711
+ null,
712
+ this ) ;
704
713
705
714
// Set after CallInContext callback given callback can fail to occur.
706
715
_isAgileReferenceSet = true ;
716
+
707
717
return __agileReference ;
708
718
709
- void InitAgileReference ( )
719
+ static void InitAgileReference ( object state )
710
720
{
711
- global ::System . Threading . Interlocked . CompareExchange ( ref __agileReference , new AgileReference ( this ) , null ) ;
721
+ ObjectReferenceWithContext < T > @this = Unsafe . As < ObjectReferenceWithContext < T > > ( state ) ;
722
+
723
+ global ::System . Threading . Interlocked . CompareExchange ( ref @this . __agileReference , new AgileReference ( @this ) , null ) ;
712
724
}
713
725
}
714
726
@@ -805,16 +817,32 @@ private ObjectReference<T> GetCurrentContext()
805
817
// of ConcurrentDictionary<,> and transitively dependent types for every vtable type T, since it's not
806
818
// something we actually need. Because the cache is private and we're the only ones using it, we can
807
819
// just store the per-context agile references as IObjectReference values, and then cast them on return.
808
- IObjectReference objectReference = CachedContext . GetOrAdd ( currentContext , CreateForCurrentContext ) ;
820
+ #if NET
821
+ IObjectReference objectReference = CachedContext . GetOrAdd ( currentContext , ContextCallbackHolder . Value , this ) ;
822
+ #else
823
+ IObjectReference objectReference = CachedContext . GetOrAdd ( currentContext , ptr => ContextCallbackHolder . Value ( ptr , this ) ) ;
824
+ #endif
809
825
810
826
return Unsafe . As < ObjectReference < T > > ( objectReference ) ;
827
+ }
828
+
829
+ private static class ContextCallbackHolder
830
+ {
831
+ // We have a single lambda expression in this type, so we can manually rewrite it to a 'static readonly'
832
+ // field. This avoids the extra logic to lazily initialized it (it's already lazily initialized because
833
+ // it's in a 'beforefieldinit' type which is only used when the lambda is actually needed), and also it
834
+ // allows storing the entire delegate in the Frozen Object Heap (FOH) on modern runtimes.
835
+ public static readonly Func < IntPtr , IObjectReference , IObjectReference > Value = CreateForCurrentContext ;
811
836
812
837
#if NET
813
838
[ UnconditionalSuppressMessage ( "Trimming" , "IL2087" , Justification = "The '_iid' field is only empty when using annotated APIs not trim-safe." ) ]
814
839
#endif
815
- IObjectReference CreateForCurrentContext ( IntPtr _ )
840
+ private static IObjectReference CreateForCurrentContext ( IntPtr _ , IObjectReference state )
816
841
{
817
- var agileReference = AgileReference ;
842
+ ObjectReferenceWithContext < T > @this = Unsafe . As < ObjectReferenceWithContext < T > > ( state ) ;
843
+
844
+ var agileReference = @this . AgileReference ;
845
+
818
846
// We may fail to switch context and thereby not get an agile reference.
819
847
// In these cases, fallback to using the current context.
820
848
if ( agileReference == null )
@@ -829,17 +857,17 @@ IObjectReference CreateForCurrentContext(IntPtr _)
829
857
// going through a trim-unsafe constructor, which is explicitly not supported in this configuration.
830
858
if ( ! RuntimeFeature . IsDynamicCodeCompiled )
831
859
{
832
- return agileReference . Get < T > ( _iid ) ;
860
+ return agileReference . Get < T > ( @this . _iid ) ;
833
861
}
834
862
#endif
835
863
836
- if ( _iid == Guid . Empty )
864
+ if ( @this . _iid == Guid . Empty )
837
865
{
838
866
return agileReference . Get < T > ( GuidGenerator . GetIID ( typeof ( T ) ) ) ;
839
867
}
840
868
else
841
869
{
842
- return agileReference . Get < T > ( _iid ) ;
870
+ return agileReference . Get < T > ( @this . _iid ) ;
843
871
}
844
872
}
845
873
catch ( Exception )
@@ -858,8 +886,42 @@ protected override unsafe void Release()
858
886
CachedContext . Clear ( ) ;
859
887
}
860
888
861
- Context . CallInContext ( _contextCallbackPtr , _contextToken , base . Release , ReleaseWithoutContext ) ;
889
+ Context . CallInContext (
890
+ _contextCallbackPtr ,
891
+ _contextToken ,
892
+ #if NET && CsWinRT_LANG_11_FEATURES
893
+ & Release ,
894
+ & ReleaseWithoutContext ,
895
+ #else
896
+ Release ,
897
+ ReleaseWithoutContext ,
898
+ #endif
899
+ this ) ;
900
+
862
901
Context . DisposeContextCallback ( _contextCallbackPtr ) ;
902
+
903
+ static void Release ( object state )
904
+ {
905
+ ObjectReferenceWithContext < T > @this = Unsafe . As < ObjectReferenceWithContext < T > > ( state ) ;
906
+
907
+ @this . ReleaseFromBase ( ) ;
908
+ }
909
+
910
+ static void ReleaseWithoutContext ( object state )
911
+ {
912
+ ObjectReferenceWithContext < T > @this = Unsafe . As < ObjectReferenceWithContext < T > > ( state ) ;
913
+
914
+ @this . ReleaseWithoutContext ( ) ;
915
+ }
916
+ }
917
+
918
+ // Helper stub to invoke 'base.Release()' on a given 'ObjectReferenceWithContext<T>' input parameter.
919
+ // We can't just do 'param.base.Release()' (or something like that), so the only way to specifically
920
+ // invoke the base implementation of an overridden method on that object is to go through a helper
921
+ // instance method invoked on it that just calls the base implementation of the method we want.
922
+ private void ReleaseFromBase ( )
923
+ {
924
+ base . Release ( ) ;
863
925
}
864
926
865
927
public override ObjectReference < IUnknownVftbl > AsKnownPtr ( IntPtr ptr )
0 commit comments