diff --git a/CHANGELOG.md b/CHANGELOG.md index 380c6f6d3..3d0dcd6de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][]. - Fixed 'clrmethod' for python 2 (#492) - Fixed double calling of constructor when deriving from .NET class (#495) - Fixed `clr.GetClrType` when iterating over `System` members (#607) +- Fixed `LockRecursionException` when loading assemblies (#627) ## [2.3.0][] - 2017-03-11 diff --git a/src/runtime/assemblymanager.cs b/src/runtime/assemblymanager.cs index 06a4449a2..d63930a58 100644 --- a/src/runtime/assemblymanager.cs +++ b/src/runtime/assemblymanager.cs @@ -26,7 +26,7 @@ internal class AssemblyManager private static Dictionary probed; // modified from event handlers below, potentially triggered from different .NET threads - private static AssemblyList assemblies; + private static ConcurrentQueue assemblies; internal static List pypath; private AssemblyManager() @@ -43,7 +43,7 @@ internal static void Initialize() namespaces = new ConcurrentDictionary>(); probed = new Dictionary(32); //generics = new Dictionary>(); - assemblies = new AssemblyList(16); + assemblies = new ConcurrentQueue(); pypath = new List(16); AppDomain domain = AppDomain.CurrentDomain; @@ -60,7 +60,7 @@ internal static void Initialize() try { ScanAssembly(a); - assemblies.Add(a); + assemblies.Enqueue(a); } catch (Exception ex) { @@ -91,7 +91,7 @@ internal static void Shutdown() private static void AssemblyLoadHandler(object ob, AssemblyLoadEventArgs args) { Assembly assembly = args.LoadedAssembly; - assemblies.Add(assembly); + assemblies.Enqueue(assembly); ScanAssembly(assembly); } @@ -461,103 +461,5 @@ public static Type LookupType(string qname) } return null; } - - /// - /// Wrapper around List<Assembly> for thread safe access - /// - private class AssemblyList : IEnumerable - { - private readonly List _list; - private readonly ReaderWriterLockSlim _lock; - - public AssemblyList(int capacity) - { - _list = new List(capacity); - _lock = new ReaderWriterLockSlim(); - } - - public int Count - { - get - { - _lock.EnterReadLock(); - try - { - return _list.Count; - } - finally - { - _lock.ExitReadLock(); - } - } - } - - public void Add(Assembly assembly) - { - _lock.EnterWriteLock(); - try - { - _list.Add(assembly); - } - finally - { - _lock.ExitWriteLock(); - } - } - - public IEnumerator GetEnumerator() - { - return ((IEnumerable)this).GetEnumerator(); - } - - /// - /// Enumerator wrapping around 's enumerator. - /// Acquires and releases a read lock on during enumeration - /// - private class Enumerator : IEnumerator - { - private readonly AssemblyList _assemblyList; - - private readonly IEnumerator _listEnumerator; - - public Enumerator(AssemblyList assemblyList) - { - _assemblyList = assemblyList; - _assemblyList._lock.EnterReadLock(); - _listEnumerator = _assemblyList._list.GetEnumerator(); - } - - public void Dispose() - { - _listEnumerator.Dispose(); - _assemblyList._lock.ExitReadLock(); - } - - public bool MoveNext() - { - return _listEnumerator.MoveNext(); - } - - public void Reset() - { - _listEnumerator.Reset(); - } - - public Assembly Current - { - get { return _listEnumerator.Current; } - } - - object IEnumerator.Current - { - get { return Current; } - } - } - - IEnumerator IEnumerable.GetEnumerator() - { - return new Enumerator(this); - } - } } } diff --git a/src/tests/test_module.py b/src/tests/test_module.py index e02aa6e01..62d79b9ab 100644 --- a/src/tests/test_module.py +++ b/src/tests/test_module.py @@ -387,3 +387,9 @@ def test_assembly_load_thread_safety(): from System.Collections.Generic import Dictionary _ = Dictionary[Guid, DateTime]() ModuleTest.JoinThreads() + +def test_assembly_load_recursion_bug(): + """Test fix for recursion bug documented in #627""" + from System.Configuration import ConfigurationManager + content = dir(ConfigurationManager) + assert len(content) > 5, content