Skip to content

Commit df2aa64

Browse files
authored
Merge pull request #628 from Cronan/lock_recursion
Fixes lock recursion bug in assembly loading
2 parents c18285f + adc1240 commit df2aa64

File tree

3 files changed

+11
-102
lines changed

3 files changed

+11
-102
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
2929
- Fixed 'clrmethod' for python 2 (#492)
3030
- Fixed double calling of constructor when deriving from .NET class (#495)
3131
- Fixed `clr.GetClrType` when iterating over `System` members (#607)
32+
- Fixed `LockRecursionException` when loading assemblies (#627)
3233

3334

3435
## [2.3.0][] - 2017-03-11

src/runtime/assemblymanager.cs

+4-102
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ internal class AssemblyManager
2626
private static Dictionary<string, int> probed;
2727

2828
// modified from event handlers below, potentially triggered from different .NET threads
29-
private static AssemblyList assemblies;
29+
private static ConcurrentQueue<Assembly> assemblies;
3030
internal static List<string> pypath;
3131

3232
private AssemblyManager()
@@ -43,7 +43,7 @@ internal static void Initialize()
4343
namespaces = new ConcurrentDictionary<string, ConcurrentDictionary<Assembly, string>>();
4444
probed = new Dictionary<string, int>(32);
4545
//generics = new Dictionary<string, Dictionary<string, string>>();
46-
assemblies = new AssemblyList(16);
46+
assemblies = new ConcurrentQueue<Assembly>();
4747
pypath = new List<string>(16);
4848

4949
AppDomain domain = AppDomain.CurrentDomain;
@@ -60,7 +60,7 @@ internal static void Initialize()
6060
try
6161
{
6262
ScanAssembly(a);
63-
assemblies.Add(a);
63+
assemblies.Enqueue(a);
6464
}
6565
catch (Exception ex)
6666
{
@@ -91,7 +91,7 @@ internal static void Shutdown()
9191
private static void AssemblyLoadHandler(object ob, AssemblyLoadEventArgs args)
9292
{
9393
Assembly assembly = args.LoadedAssembly;
94-
assemblies.Add(assembly);
94+
assemblies.Enqueue(assembly);
9595
ScanAssembly(assembly);
9696
}
9797

@@ -461,103 +461,5 @@ public static Type LookupType(string qname)
461461
}
462462
return null;
463463
}
464-
465-
/// <summary>
466-
/// Wrapper around List&lt;Assembly&gt; for thread safe access
467-
/// </summary>
468-
private class AssemblyList : IEnumerable<Assembly>
469-
{
470-
private readonly List<Assembly> _list;
471-
private readonly ReaderWriterLockSlim _lock;
472-
473-
public AssemblyList(int capacity)
474-
{
475-
_list = new List<Assembly>(capacity);
476-
_lock = new ReaderWriterLockSlim();
477-
}
478-
479-
public int Count
480-
{
481-
get
482-
{
483-
_lock.EnterReadLock();
484-
try
485-
{
486-
return _list.Count;
487-
}
488-
finally
489-
{
490-
_lock.ExitReadLock();
491-
}
492-
}
493-
}
494-
495-
public void Add(Assembly assembly)
496-
{
497-
_lock.EnterWriteLock();
498-
try
499-
{
500-
_list.Add(assembly);
501-
}
502-
finally
503-
{
504-
_lock.ExitWriteLock();
505-
}
506-
}
507-
508-
public IEnumerator GetEnumerator()
509-
{
510-
return ((IEnumerable<Assembly>)this).GetEnumerator();
511-
}
512-
513-
/// <summary>
514-
/// Enumerator wrapping around <see cref="AssemblyList._list" />'s enumerator.
515-
/// Acquires and releases a read lock on <see cref="AssemblyList._lock" /> during enumeration
516-
/// </summary>
517-
private class Enumerator : IEnumerator<Assembly>
518-
{
519-
private readonly AssemblyList _assemblyList;
520-
521-
private readonly IEnumerator<Assembly> _listEnumerator;
522-
523-
public Enumerator(AssemblyList assemblyList)
524-
{
525-
_assemblyList = assemblyList;
526-
_assemblyList._lock.EnterReadLock();
527-
_listEnumerator = _assemblyList._list.GetEnumerator();
528-
}
529-
530-
public void Dispose()
531-
{
532-
_listEnumerator.Dispose();
533-
_assemblyList._lock.ExitReadLock();
534-
}
535-
536-
public bool MoveNext()
537-
{
538-
return _listEnumerator.MoveNext();
539-
}
540-
541-
public void Reset()
542-
{
543-
_listEnumerator.Reset();
544-
}
545-
546-
public Assembly Current
547-
{
548-
get { return _listEnumerator.Current; }
549-
}
550-
551-
object IEnumerator.Current
552-
{
553-
get { return Current; }
554-
}
555-
}
556-
557-
IEnumerator<Assembly> IEnumerable<Assembly>.GetEnumerator()
558-
{
559-
return new Enumerator(this);
560-
}
561-
}
562464
}
563465
}

src/tests/test_module.py

+6
Original file line numberDiff line numberDiff line change
@@ -387,3 +387,9 @@ def test_assembly_load_thread_safety():
387387
from System.Collections.Generic import Dictionary
388388
_ = Dictionary[Guid, DateTime]()
389389
ModuleTest.JoinThreads()
390+
391+
def test_assembly_load_recursion_bug():
392+
"""Test fix for recursion bug documented in #627"""
393+
from System.Configuration import ConfigurationManager
394+
content = dir(ConfigurationManager)
395+
assert len(content) > 5, content

0 commit comments

Comments
 (0)