Skip to content

QuantConnect Update #1385

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 34 commits into from
Closed
Changes from 1 commit
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
062a0bc
Reflect PR #1 Support for Decimal
C-SELLERS Feb 3, 2021
5ed5a96
Reflect PR#8 MISSING CONVERTER.CS L516-528 Changes
C-SELLERS Feb 3, 2021
9b5445e
Reflect PR #14
C-SELLERS Feb 4, 2021
709643a
Reflect PR #15
C-SELLERS Feb 4, 2021
a481700
Reflect PR #19
C-SELLERS Feb 4, 2021
de0328c
Reflect PR #25
C-SELLERS Feb 4, 2021
481f4d0
Reflect PR #34
C-SELLERS Feb 4, 2021
863530d
Reflect PR #35
C-SELLERS Feb 4, 2021
5839d21
Implement List Conversion, Reflect PR #37 Tests
C-SELLERS Feb 4, 2021
c362816
Reflect PR #38 Partial: Assembly Manager Improvements
C-SELLERS Feb 4, 2021
cde9b51
Reflect PR #38
C-SELLERS Feb 4, 2021
101624e
Reflect PR #42 KeyValuePairEnumerableObject
C-SELLERS Feb 4, 2021
56a4bcf
Reflect PR #10 Runtime DecimalType
C-SELLERS Feb 4, 2021
508db2e
Add TimeDelta and DateTime tests
C-SELLERS Feb 5, 2021
ebbafad
Fix DecimalConversion test for float conversion
C-SELLERS Feb 5, 2021
53375ce
Converter mod tweaks
C-SELLERS Feb 6, 2021
c8fdbcb
Adjust a few broken PyTests
C-SELLERS Feb 6, 2021
af30873
Use _pydecimal to not interfere with Lean/decimal.py
C-SELLERS Feb 9, 2021
bf1755d
Add MethodBinder tests
C-SELLERS Feb 9, 2021
58d5df0
MethodBinder implicit resolution
Martin-Molinero Feb 4, 2021
0c94228
Fix bad cherry pick
C-SELLERS Feb 10, 2021
44e089a
Refactoring precedence resolution
Martin-Molinero Feb 5, 2021
108eacf
Deal with operator binding
C-SELLERS Feb 10, 2021
6379568
Fix `TestNoOverloadException` unit test
C-SELLERS Feb 10, 2021
9f2796a
Fix for DomainReload tests
C-SELLERS Feb 10, 2021
b0aca5c
Add InEquality Operator Test
C-SELLERS Feb 11, 2021
0f5f0ba
Dont PyObjects precedence in Operator methods
C-SELLERS Feb 11, 2021
ed6ab18
Revert "Merge pull request #1240 from danabr/auto-cast-ret-val-to-int…
C-SELLERS Feb 12, 2021
d87584b
Fix Primitive Conversion to Int
C-SELLERS Feb 15, 2021
f59335f
Post rebase fix
C-SELLERS Feb 15, 2021
bd94e49
Add PrimitiveIntConversion test
C-SELLERS Feb 16, 2021
cd06d10
Add test for interface derived classes
C-SELLERS Feb 16, 2021
aeb20c0
Add to Authors.md
C-SELLERS Feb 16, 2021
ae34f30
Load in current directory into Python Path
C-SELLERS Feb 18, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Reflect PR #38 Partial: Assembly Manager Improvements
  • Loading branch information
C-SELLERS committed Feb 15, 2021
commit c362816f478a10e3e32003fcc4f37749abe6ca18
181 changes: 115 additions & 66 deletions src/runtime/assemblymanager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;

namespace Python.Runtime
{
Expand All @@ -25,18 +26,17 @@ internal class AssemblyManager
// than it can end up referring to assemblies that are already unloaded (default behavior after unload appDomain -
// unless LoaderOptimization.MultiDomain is used);
// So for multidomain support it is better to have the dict. recreated for each app-domain initialization
private static ConcurrentDictionary<string, ConcurrentDictionary<Assembly, string>> namespaces =
new ConcurrentDictionary<string, ConcurrentDictionary<Assembly, string>>();
//private static Dictionary<string, Dictionary<string, string>> generics;
private static AssemblyLoadEventHandler lhandler;
private static ResolveEventHandler rhandler;
private static ConcurrentDictionary<string, ConcurrentDictionary<Assembly, byte>> namespaces =
new ConcurrentDictionary<string, ConcurrentDictionary<Assembly, byte>>();
private static ConcurrentDictionary<string, Assembly> assembliesNamesCache =
new ConcurrentDictionary<string, Assembly>();
private static ConcurrentQueue<Assembly> assemblies = new ConcurrentQueue<Assembly>();
private static int pendingAssemblies;

// updated only under GIL?
private static Dictionary<string, int> probed = new Dictionary<string, int>(32);

// modified from event handlers below, potentially triggered from different .NET threads
private static ConcurrentQueue<Assembly> assemblies;
internal static List<string> pypath;
private static List<string> pypath = new List<string>(16);
private static Dictionary<string, HashSet<string>> filesInPath = new Dictionary<string, HashSet<string>>();

private AssemblyManager()
{
Expand All @@ -49,30 +49,34 @@ private AssemblyManager()
/// </summary>
internal static void Initialize()
{
assemblies = new ConcurrentQueue<Assembly>();
pypath = new List<string>(16);

AppDomain domain = AppDomain.CurrentDomain;

lhandler = new AssemblyLoadEventHandler(AssemblyLoadHandler);
domain.AssemblyLoad += lhandler;

rhandler = new ResolveEventHandler(ResolveHandler);
domain.AssemblyResolve += rhandler;
domain.AssemblyLoad += AssemblyLoadHandler;
domain.AssemblyResolve += ResolveHandler;

Assembly[] items = domain.GetAssemblies();
foreach (Assembly a in items)
foreach (var assembly in domain.GetAssemblies())
{
try
{
ScanAssembly(a);
assemblies.Enqueue(a);
LaunchAssemblyLoader(assembly);
}
catch (Exception ex)
{
Debug.WriteLine("Error scanning assembly {0}. {1}", a, ex);
Debug.WriteLine("Error scanning assembly {0}. {1}", assembly, ex);
}
}

var safeCount = 0;
// lets wait until all assemblies are loaded
do
{
if (safeCount++ > 200)
{
throw new TimeoutException("Timeout while waiting for assemblies to load");
}

Thread.Sleep(50);
} while (pendingAssemblies > 0);
}


Expand All @@ -82,8 +86,8 @@ internal static void Initialize()
internal static void Shutdown()
{
AppDomain domain = AppDomain.CurrentDomain;
domain.AssemblyLoad -= lhandler;
domain.AssemblyResolve -= rhandler;
domain.AssemblyLoad -= AssemblyLoadHandler;
domain.AssemblyResolve -= ResolveHandler;
}


Expand All @@ -97,8 +101,35 @@ internal static void Shutdown()
private static void AssemblyLoadHandler(object ob, AssemblyLoadEventArgs args)
{
Assembly assembly = args.LoadedAssembly;
assemblies.Enqueue(assembly);
ScanAssembly(assembly);
LaunchAssemblyLoader(assembly);
}

/// <summary>
/// Launches a new task that will load the provided assembly
/// </summary>
private static void LaunchAssemblyLoader(Assembly assembly)
{
if (assembly != null)
{
Interlocked.Increment(ref pendingAssemblies);
Task.Factory.StartNew(() =>
{
try
{
if (assembliesNamesCache.TryAdd(assembly.GetName().Name, assembly))
{
assemblies.Enqueue(assembly);
ScanAssembly(assembly);
}
}
catch
{
// pass
}

Interlocked.Decrement(ref pendingAssemblies);
});
}
}


Expand All @@ -112,12 +143,12 @@ private static void AssemblyLoadHandler(object ob, AssemblyLoadEventArgs args)
private static Assembly ResolveHandler(object ob, ResolveEventArgs args)
{
string name = args.Name.ToLower();
foreach (Assembly a in assemblies)
foreach (var assembly in assemblies)
{
string full = a.FullName.ToLower();
var full = assembly.FullName.ToLower();
if (full.StartsWith(name))
{
return a;
return assembly;
}
}
return LoadAssemblyPath(args.Name);
Expand All @@ -139,19 +170,61 @@ internal static void UpdatePath()
{
BorrowedReference list = Runtime.PySys_GetObject("path");
var count = Runtime.PyList_Size(list);
var sep = Path.DirectorySeparatorChar;

if (count != pypath.Count)
{
pypath.Clear();
probed.Clear();
// add first the current path
pypath.Add("");
for (var i = 0; i < count; i++)
{
BorrowedReference item = Runtime.PyList_GetItem(list, i);
string path = Runtime.GetManagedString(item);
if (path != null)
{
pypath.Add(path);
pypath.Add(path == string.Empty ? path : path + sep);
}
}

// for performance we will search for all files in each directory in the path once
Parallel.ForEach(pypath.Where(s =>
{
try
{
lock (filesInPath)
{
// only search in directory if it exists and we haven't already analyzed it
return Directory.Exists(s) && !filesInPath.ContainsKey(s);
}
}
catch
{
// just in case, file operations can throw
}
return false;
}), path =>
{
var container = new HashSet<string>();
try
{
foreach (var file in Directory.EnumerateFiles(path)
.Where(file => file.EndsWith(".dll") || file.EndsWith(".exe")))
{
container.Add(Path.GetFileName(file));
}
}
catch
{
// just in case, file operations can throw
}

lock (filesInPath)
{
filesInPath[path] = container;
}
});
}
}

Expand All @@ -163,30 +236,18 @@ internal static void UpdatePath()
/// </summary>
public static string FindAssembly(string name)
{
char sep = Path.DirectorySeparatorChar;

foreach (string head in pypath)
foreach (var kvp in filesInPath)
{
string path;
if (head == null || head.Length == 0)
{
path = name;
}
else
{
path = head + sep + name;
}

string temp = path + ".dll";
if (File.Exists(temp))
var dll = $"{name}.dll";
if (kvp.Value.Contains(dll))
{
return temp;
return kvp.Key + dll;
}

temp = path + ".exe";
if (File.Exists(temp))
var executable = $"{name}.exe";
if (kvp.Value.Contains(executable))
{
return temp;
return kvp.Key + executable;
}
}
return null;
Expand Down Expand Up @@ -242,14 +303,8 @@ public static Assembly LoadAssemblyFullPath(string name)
/// </summary>
public static Assembly FindLoadedAssembly(string name)
{
foreach (Assembly a in assemblies)
{
if (a.GetName().Name == name)
{
return a;
}
}
return null;
Assembly result;
return assembliesNamesCache.TryGetValue(name, out result) ? result : null;
}

/// <summary>
Expand All @@ -264,16 +319,10 @@ internal static void ScanAssembly(Assembly assembly)
{
return;
}

// skip this assembly, it causes 'GetTypes' call to hang
if (assembly.FullName.StartsWith("System.Windows.Forms"))
{
return;
}

// A couple of things we want to do here: first, we want to
// gather a list of all of the namespaces contributed to by
// the assembly.

foreach (Type t in GetTypes(assembly))
{
string ns = t.Namespace ?? "";
Expand All @@ -284,13 +333,13 @@ internal static void ScanAssembly(Assembly assembly)
for (var n = 0; n < names.Length; n++)
{
s = n == 0 ? names[0] : s + "." + names[n];
namespaces.TryAdd(s, new ConcurrentDictionary<Assembly, string>());
namespaces.TryAdd(s, new ConcurrentDictionary<Assembly, byte>());
}
}

if (ns != null)
{
namespaces[ns].TryAdd(assembly, string.Empty);
namespaces[ns].TryAdd(assembly, 1);
}

if (ns != null && t.IsGenericTypeDefinition)
Expand Down