11using System ;
2- using System . IO ;
32using System . Collections ;
4- using System . Collections . Specialized ;
3+ using System . IO ;
4+ using System . Collections . Concurrent ;
55using System . Collections . Generic ;
66using System . Diagnostics ;
77using System . Reflection ;
8- using System . Reflection . Emit ;
8+ using System . Threading ;
99
1010namespace Python . Runtime
1111{
@@ -15,12 +15,16 @@ namespace Python.Runtime
1515 /// </summary>
1616 internal class AssemblyManager
1717 {
18- static Dictionary < string , Dictionary < Assembly , string > > namespaces ;
18+ // modified from event handlers below, potentially triggered from different .NET threads
19+ // therefore this should be a ConcurrentDictionary
20+ static ConcurrentDictionary < string , ConcurrentDictionary < Assembly , string > > namespaces ;
1921 //static Dictionary<string, Dictionary<string, string>> generics;
2022 static AssemblyLoadEventHandler lhandler ;
2123 static ResolveEventHandler rhandler ;
24+ // updated only under GIL?
2225 static Dictionary < string , int > probed ;
23- static List < Assembly > assemblies ;
26+ // modified from event handlers below, potentially triggered from different .NET threads
27+ static AssemblyList assemblies ;
2428 internal static List < string > pypath ;
2529
2630 private AssemblyManager ( )
@@ -36,10 +40,10 @@ private AssemblyManager()
3640 internal static void Initialize ( )
3741 {
3842 namespaces = new
39- Dictionary < string , Dictionary < Assembly , string > > ( 32 ) ;
43+ ConcurrentDictionary < string , ConcurrentDictionary < Assembly , string > > ( ) ;
4044 probed = new Dictionary < string , int > ( 32 ) ;
4145 //generics = new Dictionary<string, Dictionary<string, string>>();
42- assemblies = new List < Assembly > ( 16 ) ;
46+ assemblies = new AssemblyList ( 16 ) ;
4347 pypath = new List < string > ( 16 ) ;
4448
4549 AppDomain domain = AppDomain . CurrentDomain ;
@@ -105,9 +109,8 @@ static void AssemblyLoadHandler(Object ob, AssemblyLoadEventArgs args)
105109 static Assembly ResolveHandler ( Object ob , ResolveEventArgs args )
106110 {
107111 string name = args . Name . ToLower ( ) ;
108- for ( int i = 0 ; i < assemblies . Count ; i ++ )
112+ foreach ( Assembly a in assemblies )
109113 {
110- Assembly a = ( Assembly ) assemblies [ i ] ;
111114 string full = a . FullName . ToLower ( ) ;
112115 if ( full . StartsWith ( name ) )
113116 {
@@ -266,9 +269,8 @@ public static Assembly LoadAssemblyFullPath(string name)
266269
267270 public static Assembly FindLoadedAssembly ( string name )
268271 {
269- for ( int i = 0 ; i < assemblies . Count ; i ++ )
272+ foreach ( Assembly a in assemblies )
270273 {
271- Assembly a = ( Assembly ) assemblies [ i ] ;
272274 if ( a . GetName ( ) . Name == name )
273275 {
274276 return a ;
@@ -295,15 +297,15 @@ public static bool LoadImplicit(string name, bool warn = true)
295297 bool loaded = false ;
296298 string s = "" ;
297299 Assembly lastAssembly = null ;
298- HashSet < Assembly > assemblies = null ;
300+ HashSet < Assembly > assembliesSet = null ;
299301 for ( int i = 0 ; i < names . Length ; i ++ )
300302 {
301303 s = ( i == 0 ) ? names [ 0 ] : s + "." + names [ i ] ;
302304 if ( ! probed . ContainsKey ( s ) )
303305 {
304- if ( assemblies == null )
306+ if ( assembliesSet == null )
305307 {
306- assemblies = new HashSet < Assembly > ( AppDomain . CurrentDomain . GetAssemblies ( ) ) ;
308+ assembliesSet = new HashSet < Assembly > ( AppDomain . CurrentDomain . GetAssemblies ( ) ) ;
307309 }
308310 Assembly a = FindLoadedAssembly ( s ) ;
309311 if ( a == null )
@@ -314,7 +316,7 @@ public static bool LoadImplicit(string name, bool warn = true)
314316 {
315317 a = LoadAssembly ( s ) ;
316318 }
317- if ( a != null && ! assemblies . Contains ( a ) )
319+ if ( a != null && ! assembliesSet . Contains ( a ) )
318320 {
319321 loaded = true ;
320322 lastAssembly = a ;
@@ -362,16 +364,13 @@ internal static void ScanAssembly(Assembly assembly)
362364 for ( int n = 0 ; n < names . Length ; n ++ )
363365 {
364366 s = ( n == 0 ) ? names [ 0 ] : s + "." + names [ n ] ;
365- if ( ! namespaces . ContainsKey ( s ) )
366- {
367- namespaces . Add ( s , new Dictionary < Assembly , string > ( ) ) ;
368- }
367+ namespaces . TryAdd ( s , new ConcurrentDictionary < Assembly , string > ( ) ) ;
369368 }
370369 }
371370
372- if ( ns != null && ! namespaces [ ns ] . ContainsKey ( assembly ) )
371+ if ( ns != null )
373372 {
374- namespaces [ ns ] . Add ( assembly , String . Empty ) ;
373+ namespaces [ ns ] . TryAdd ( assembly , String . Empty ) ;
375374 }
376375
377376 if ( ns != null && t . IsGenericTypeDefinition )
@@ -383,14 +382,12 @@ internal static void ScanAssembly(Assembly assembly)
383382
384383 public static AssemblyName [ ] ListAssemblies ( )
385384 {
386- AssemblyName [ ] names = new AssemblyName [ assemblies . Count ] ;
387- Assembly assembly ;
388- for ( int i = 0 ; i < assemblies . Count ; i ++ )
385+ List < AssemblyName > names = new List < AssemblyName > ( assemblies . Count ) ;
386+ foreach ( Assembly assembly in assemblies )
389387 {
390- assembly = assemblies [ i ] ;
391- names . SetValue ( assembly . GetName ( ) , i ) ;
388+ names . Add ( assembly . GetName ( ) ) ;
392389 }
393- return names ;
390+ return names . ToArray ( ) ;
394391 }
395392
396393 //===================================================================
@@ -471,9 +468,8 @@ public static List<string> GetNames(string nsname)
471468
472469 public static Type LookupType ( string qname )
473470 {
474- for ( int i = 0 ; i < assemblies . Count ; i ++ )
471+ foreach ( Assembly assembly in assemblies )
475472 {
476- Assembly assembly = ( Assembly ) assemblies [ i ] ;
477473 Type type = assembly . GetType ( qname ) ;
478474 if ( type != null )
479475 {
@@ -482,5 +478,92 @@ public static Type LookupType(string qname)
482478 }
483479 return null ;
484480 }
481+
482+ /// <summary>
483+ /// Wrapper around List<Assembly> for thread safe access
484+ /// </summary>
485+ private class AssemblyList : IEnumerable < Assembly > {
486+ private readonly List < Assembly > _list ;
487+ private readonly ReaderWriterLockSlim _lock ;
488+
489+ public AssemblyList ( int capacity ) {
490+ _list = new List < Assembly > ( capacity ) ;
491+ _lock = new ReaderWriterLockSlim ( ) ;
492+ }
493+
494+ public int Count
495+ {
496+ get
497+ {
498+ _lock . EnterReadLock ( ) ;
499+ try {
500+ return _list . Count ;
501+ }
502+ finally {
503+ _lock . ExitReadLock ( ) ;
504+ }
505+ }
506+ }
507+
508+ public void Add ( Assembly assembly ) {
509+ _lock . EnterWriteLock ( ) ;
510+ try
511+ {
512+ _list . Add ( assembly ) ;
513+ }
514+ finally
515+ {
516+ _lock . ExitWriteLock ( ) ;
517+ }
518+ }
519+
520+ public IEnumerator GetEnumerator ( )
521+ {
522+ return ( ( IEnumerable < Assembly > ) this ) . GetEnumerator ( ) ;
523+ }
524+
525+ /// <summary>
526+ /// Enumerator wrapping around <see cref="AssemblyList._list"/>'s enumerator.
527+ /// Acquires and releases a read lock on <see cref="AssemblyList._lock"/> during enumeration
528+ /// </summary>
529+ private class Enumerator : IEnumerator < Assembly >
530+ {
531+ private readonly AssemblyList _assemblyList ;
532+
533+ private readonly IEnumerator < Assembly > _listEnumerator ;
534+
535+ public Enumerator ( AssemblyList assemblyList )
536+ {
537+ _assemblyList = assemblyList ;
538+ _assemblyList . _lock . EnterReadLock ( ) ;
539+ _listEnumerator = _assemblyList . _list . GetEnumerator ( ) ;
540+ }
541+
542+ public void Dispose ( )
543+ {
544+ _listEnumerator . Dispose ( ) ;
545+ _assemblyList . _lock . ExitReadLock ( ) ;
546+ }
547+
548+ public bool MoveNext ( )
549+ {
550+ return _listEnumerator . MoveNext ( ) ;
551+ }
552+
553+ public void Reset ( )
554+ {
555+ _listEnumerator . Reset ( ) ;
556+ }
557+
558+ public Assembly Current { get { return _listEnumerator . Current ; } }
559+
560+ object IEnumerator . Current { get { return Current ; } }
561+ }
562+
563+ IEnumerator < Assembly > IEnumerable < Assembly > . GetEnumerator ( )
564+ {
565+ return new Enumerator ( this ) ;
566+ }
567+ }
485568 }
486569}
0 commit comments