diff --git a/src/Java.Interop.Tools.Cecil/Java.Interop.Tools.Cecil/DirectoryAssemblyResolver.cs b/src/Java.Interop.Tools.Cecil/Java.Interop.Tools.Cecil/DirectoryAssemblyResolver.cs index 921e9bef9..e5824b921 100644 --- a/src/Java.Interop.Tools.Cecil/Java.Interop.Tools.Cecil/DirectoryAssemblyResolver.cs +++ b/src/Java.Interop.Tools.Cecil/Java.Interop.Tools.Cecil/DirectoryAssemblyResolver.cs @@ -29,7 +29,7 @@ using System; using System.Diagnostics; -using System.Collections; +using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; @@ -61,7 +61,7 @@ public class DirectoryAssemblyResolver : IAssemblyResolver { public ICollection SearchDirectories {get; private set;} - Dictionary cache; + ConcurrentDictionary cache; bool loadDebugSymbols; Action logger; @@ -82,7 +82,7 @@ public DirectoryAssemblyResolver (Action logger, bool loadDe { if (logger == null) throw new ArgumentNullException (nameof (logger)); - cache = new Dictionary (); + cache = new ConcurrentDictionary (); this.loadDebugSymbols = loadDebugSymbols; this.logger = logger; SearchDirectories = new List (); diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaTypeScanner.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaTypeScanner.cs index 533e0b84a..102eac1f7 100644 --- a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaTypeScanner.cs +++ b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaTypeScanner.cs @@ -1,12 +1,12 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using System.Text; +using System.Threading.Tasks; using Mono.Cecil; using Java.Interop.Tools.Cecil; -using Java.Interop.Tools.Diagnostics; using Java.Interop.Tools.TypeNameMappings; namespace Java.Interop.Tools.JavaCallableWrappers @@ -29,10 +29,9 @@ public List GetJavaTypes (IEnumerable assemblies, IAssem foreach (var assembly in assemblies) { var assm = resolver.GetAssembly (assembly); - foreach (ModuleDefinition md in assm.Modules) { foreach (TypeDefinition td in md.Types) { - AddJavaTypes (javaTypes, td); + AddJavaTypes (javaTypes.Add, td); } } } @@ -40,12 +39,28 @@ public List GetJavaTypes (IEnumerable assemblies, IAssem return javaTypes; } - void AddJavaTypes (List javaTypes, TypeDefinition type) + public List GetJavaTypesInParallel (IEnumerable assemblies, IAssemblyResolver resolver) + { + var javaTypes = new BlockingCollection (); + + Parallel.ForEach (assemblies, assembly => { + var assm = resolver.GetAssembly (assembly); + foreach (ModuleDefinition md in assm.Modules) { + foreach (TypeDefinition td in md.Types) { + AddJavaTypes (javaTypes.Add, td); + } + } + }); + + return javaTypes.ToList (); + } + + void AddJavaTypes (Action addMethod, TypeDefinition type) { if (type.IsSubclassOf ("Java.Lang.Object") || type.IsSubclassOf ("Java.Lang.Throwable")) { // For subclasses of e.g. Android.App.Activity. - javaTypes.Add (type); + addMethod (type); } else if (type.IsClass && !type.IsSubclassOf ("System.Exception") && type.ImplementsInterface ("Android.Runtime.IJavaObject")) { var level = ErrorOnCustomJavaObject ? TraceLevel.Error : TraceLevel.Warning; var prefix = ErrorOnCustomJavaObject ? "error" : "warning"; @@ -59,7 +74,7 @@ void AddJavaTypes (List javaTypes, TypeDefinition type) return; foreach (TypeDefinition nested in type.NestedTypes) - AddJavaTypes (javaTypes, nested); + AddJavaTypes (addMethod, nested); } public static bool ShouldSkipJavaCallableWrapperGeneration (TypeDefinition type) diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Test/Data/Assemblies.zip b/src/Java.Interop.Tools.JavaCallableWrappers/Test/Data/Assemblies.zip new file mode 100644 index 000000000..de2931257 Binary files /dev/null and b/src/Java.Interop.Tools.JavaCallableWrappers/Test/Data/Assemblies.zip differ diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Test/Java.Interop.Tools.JavaCallableWrappers-Tests.csproj b/src/Java.Interop.Tools.JavaCallableWrappers/Test/Java.Interop.Tools.JavaCallableWrappers-Tests.csproj index 04780ffc4..9400cd9d8 100644 --- a/src/Java.Interop.Tools.JavaCallableWrappers/Test/Java.Interop.Tools.JavaCallableWrappers-Tests.csproj +++ b/src/Java.Interop.Tools.JavaCallableWrappers/Test/Java.Interop.Tools.JavaCallableWrappers-Tests.csproj @@ -34,6 +34,8 @@ ..\..\..\packages\NUnit.3.7.1\lib\net45\nunit.framework.dll + + @@ -47,6 +49,9 @@ + + PreserveNewest + diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Test/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGeneratorTests.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Test/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGeneratorTests.cs index b3a9194e5..e2306927d 100644 --- a/src/Java.Interop.Tools.JavaCallableWrappers/Test/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGeneratorTests.cs +++ b/src/Java.Interop.Tools.JavaCallableWrappers/Test/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGeneratorTests.cs @@ -1,5 +1,8 @@ using System; +using System.Collections.Generic; +using System.Diagnostics; using System.IO; +using System.IO.Compression; using System.Linq; using System.Threading.Tasks; @@ -13,6 +16,7 @@ using Java.Interop.Tools.TypeNameMappings; using Xamarin.Android.ToolsTests; +using Java.Interop.Tools.Cecil; namespace Java.Interop.Tools.JavaCallableWrappersTests { @@ -491,6 +495,66 @@ public void monodroidClearReferences () "; Assert.AreEqual (expected, actual); } + + [Test, Repeat (10)] + public void GenerateInParallel () + { + var zipPath = Path.Combine (Path.GetDirectoryName (GetType ().Assembly.Location), "Data", "assemblies.zip"); + var dest = Path.Combine (Path.GetTempPath (), Path.GetRandomFileName ()); + try { + Directory.CreateDirectory (dest); + var assemblies = new List (); + + using (var zip = ZipFile.OpenRead (zipPath)) { + foreach (var entry in zip.Entries) { + var path = Path.Combine (dest, entry.FullName); + assemblies.Add (path); + + using (var fs = File.Create (path)) + using (var zs = entry.Open ()) { + zs.CopyTo (fs); + } + } + } + + Action logger = (l, m) => { + TestContext.WriteLine ($"{l} {m}"); + }; + + using (var res = new DirectoryAssemblyResolver (logger, loadDebugSymbols: false)) { + res.SearchDirectories.Add (dest); + foreach (var assembly in assemblies) { + res.Load (assembly); + } + + var scanner = new JavaTypeScanner (logger); + var sw = new Stopwatch (); + + sw.Reset (); + sw.Start (); + var parallelTypes = scanner.GetJavaTypesInParallel (assemblies, res); + sw.Stop (); + TestContext.WriteLine ($"Parallel took: {sw.Elapsed}"); + + sw.Reset (); + sw.Start (); + var types = scanner.GetJavaTypes (assemblies, res); + sw.Stop (); + TestContext.WriteLine ($"Non parallel took: {sw.Elapsed}"); + + types.Sort ((a, b) => a.FullName.CompareTo (b.FullName)); + parallelTypes.Sort ((a, b) => a.FullName.CompareTo (b.FullName)); + + Assert.AreEqual (types.Count, parallelTypes.Count); + for (int i = 0; i < types.Count; i++) { + Assert.AreEqual (types [i].FullName, parallelTypes [i].FullName); + } + } + + } finally { + Directory.Delete (dest, true); + } + } } }