@@ -10,24 +10,26 @@ namespace ClrLoader
10
10
public static class DomainSetup
11
11
{
12
12
public delegate int EntryPoint ( IntPtr buffer , int size ) ;
13
+
13
14
public static void StoreFunctorFromDomainData ( )
14
15
{
15
16
var domain = AppDomain . CurrentDomain ;
16
17
var assemblyPath = ( string ) domain . GetData ( "_assemblyPath" ) ;
17
18
var typeName = ( string ) domain . GetData ( "_typeName" ) ;
18
19
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 ) ;
20
23
domain . SetData ( "_thisFunctor" , functor ) ;
21
24
}
22
25
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 )
24
27
{
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 ) ;
27
30
var type = assembly . GetType ( typeName , throwOnError : true ) ;
28
31
var deleg = Delegate . CreateDelegate ( typeof ( EntryPoint ) , type , function ) ;
29
- IntPtr result = Marshal . GetFunctionPointerForDelegate ( deleg ) ;
30
- return result ;
32
+ return deleg ;
31
33
}
32
34
}
33
35
@@ -61,27 +63,46 @@ private void installResolver(string assemblyPath)
61
63
} ;
62
64
}
63
65
66
+ private static readonly object _lockObj = new object ( ) ;
67
+
64
68
public IntPtr GetFunctor ( string assemblyPath , string typeName , string function )
65
69
{
66
70
if ( _disposed )
67
71
throw new InvalidOperationException ( "Domain is already disposed" ) ;
68
72
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 )
75
75
{
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 ;
83
105
}
84
- return result ;
85
106
}
86
107
87
108
public void Dispose ( )
0 commit comments