@@ -10,24 +10,26 @@ namespace ClrLoader
1010 public static class DomainSetup
1111 {
1212 public delegate int EntryPoint ( IntPtr buffer , int size ) ;
13+
1314 public static void StoreFunctorFromDomainData ( )
1415 {
1516 var domain = AppDomain . CurrentDomain ;
1617 var assemblyPath = ( string ) domain . GetData ( "_assemblyPath" ) ;
1718 var typeName = ( string ) domain . GetData ( "_typeName" ) ;
1819 var function = ( string ) domain . GetData ( "_function" ) ;
19- var functor = GetFunctor ( domain , assemblyPath , typeName , function ) ;
20+ var deleg = GetDelegate ( domain , assemblyPath , typeName , function ) ;
21+ var functor = Marshal . GetFunctionPointerForDelegate ( deleg ) ;
22+ domain . SetData ( "_thisDelegate" , deleg ) ;
2023 domain . SetData ( "_thisFunctor" , functor ) ;
2124 }
2225
23- private static IntPtr GetFunctor ( AppDomain domain , string assemblyPath , string typeName , string function )
26+ private static Delegate GetDelegate ( AppDomain domain , string assemblyPath , string typeName , string function )
2427 {
25- var assemblyName = AssemblyName . GetAssemblyName ( assemblyPath ) . Name ;
26- var assembly = domain . Load ( AssemblyName . GetAssemblyName ( assemblyPath ) ) ;
28+ var assemblyName = AssemblyName . GetAssemblyName ( assemblyPath ) ;
29+ var assembly = domain . Load ( assemblyName ) ;
2730 var type = assembly . GetType ( typeName , throwOnError : true ) ;
2831 var deleg = Delegate . CreateDelegate ( typeof ( EntryPoint ) , type , function ) ;
29- IntPtr result = Marshal . GetFunctionPointerForDelegate ( deleg ) ;
30- return result ;
32+ return deleg ;
3133 }
3234 }
3335
@@ -61,27 +63,46 @@ private void installResolver(string assemblyPath)
6163 } ;
6264 }
6365
66+ private static readonly object _lockObj = new object ( ) ;
67+
6468 public IntPtr GetFunctor ( string assemblyPath , string typeName , string function )
6569 {
6670 if ( _disposed )
6771 throw new InvalidOperationException ( "Domain is already disposed" ) ;
6872
69- installResolver ( assemblyPath ) ;
70-
71- var key = ( assemblyPath , typeName , function ) ;
72-
73- IntPtr result ;
74- if ( ! _functors . TryGetValue ( key , out result ) )
73+ // neither the domain data nor the _functors dictionary is threadsafe
74+ lock ( _lockObj )
7575 {
76- Domain . SetData ( "_assemblyPath" , assemblyPath ) ;
77- Domain . SetData ( "_typeName" , typeName ) ;
78- Domain . SetData ( "_function" , function ) ;
79-
80- Domain . DoCallBack ( new CrossAppDomainDelegate ( DomainSetup . StoreFunctorFromDomainData ) ) ;
81- result = ( IntPtr ) Domain . GetData ( "_thisFunctor" ) ;
82- _functors [ key ] = result ;
76+ installResolver ( assemblyPath ) ;
77+ var assemblyName = AssemblyName . GetAssemblyName ( assemblyPath ) . Name ;
78+
79+ var key = ( assemblyName , typeName , function ) ;
80+
81+ IntPtr result ;
82+ if ( ! _functors . TryGetValue ( key , out result ) )
83+ {
84+ Domain . SetData ( "_assemblyPath" , assemblyPath ) ;
85+ Domain . SetData ( "_typeName" , typeName ) ;
86+ Domain . SetData ( "_function" , function ) ;
87+
88+ Domain . DoCallBack ( new CrossAppDomainDelegate ( DomainSetup . StoreFunctorFromDomainData ) ) ;
89+ result = ( IntPtr ) Domain . GetData ( "_thisFunctor" ) ;
90+ if ( result == IntPtr . Zero )
91+ throw new Exception ( $ "Unable to get functor for { assemblyName } , { typeName } , { function } ") ;
92+
93+ // set inputs to StoreFunctorFromDomainData to null.
94+ // (There's no method to explicitly clear domain data)
95+ Domain . SetData ( "_assemblyPath" , null ) ;
96+ Domain . SetData ( "_typeName" , null ) ;
97+ Domain . SetData ( "_function" , null ) ;
98+
99+ // the result has to remain in the domain data because we don't know when the
100+ // client of pyclr_get_function will actually invoke the functor, and if we
101+ // remove it from the domain data after returning it may get collected too early.
102+ _functors [ key ] = result ;
103+ }
104+ return result ;
83105 }
84- return result ;
85106 }
86107
87108 public void Dispose ( )
0 commit comments