Skip to content

Commit d4c11fb

Browse files
committed
Merge pull request #49 from ivanz/fix-file-copy
Use a global lock per assembly folder around appdomain creation to av…
2 parents 91636c6 + ace3d42 commit d4c11fb

File tree

1 file changed

+27
-7
lines changed

1 file changed

+27
-7
lines changed

Source/Machine.VSTestAdapter/Helpers/IsolatedAppDomainExecutionScope.cs

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.IO;
44
using System.Linq;
55
using System.Reflection;
6+
using System.Threading;
67

78
namespace Machine.VSTestAdapter.Helpers
89
{
@@ -23,8 +24,26 @@ public IsolatedAppDomainExecutionScope(string assemblyPath)
2324

2425
public T CreateInstance()
2526
{
26-
if (appDomain == null)
27-
appDomain = CreateAppDomain(assemblyPath, this.appName);
27+
if (appDomain == null) {
28+
// Because we need to copy files around - we create a global cross-process mutex here to avoid multi-process race conditions
29+
// in the case where both of those are true:
30+
// 1. VSTest is told to run tests in parallel, so it spawns multiple processes
31+
// 2. There are multiple test assemblies in the same directory
32+
using (Mutex mutex = new Mutex(false, String.Format("{0}_{1}", this.appName, Path.GetDirectoryName(this.assemblyPath).Replace(Path.DirectorySeparatorChar, '_')))) {
33+
try {
34+
mutex.WaitOne(TimeSpan.FromMinutes(1));
35+
} catch (AbandonedMutexException) { }
36+
37+
try {
38+
appDomain = CreateAppDomain(assemblyPath, this.appName);
39+
} finally {
40+
try {
41+
mutex.ReleaseMutex();
42+
} catch {
43+
}
44+
}
45+
}
46+
}
2847

2948
return (T)appDomain.CreateInstanceAndUnwrap(typeof(T).Assembly.FullName, typeof(T).FullName);
3049
}
@@ -53,11 +72,12 @@ private static AppDomain CreateAppDomain(string assemblyPath, string appName)
5372
private static void CopyRequiredRuntimeDependencies(IEnumerable<Assembly> assemblies, string destination)
5473
{
5574
foreach (Assembly assembly in assemblies) {
56-
string assemblyLocation = assembly.Location;
57-
string assemblyName = Path.GetFileName(assemblyLocation);
58-
string assemblyFileDestination = Path.Combine(destination, assemblyName);
59-
if (!File.Exists(assemblyFileDestination))
60-
CopyWithoutLockingSourceFile(assemblyLocation, assemblyFileDestination);
75+
string sourceAssemblyFile = assembly.Location;
76+
string destinationAssemblyFile = Path.Combine(destination, Path.GetFileName(sourceAssemblyFile));
77+
78+
// file doesn't exist or is older
79+
if (!File.Exists(destinationAssemblyFile) || File.GetLastWriteTimeUtc(sourceAssemblyFile) > File.GetLastWriteTimeUtc(destinationAssemblyFile))
80+
CopyWithoutLockingSourceFile(sourceAssemblyFile, destinationAssemblyFile);
6181
}
6282
}
6383

0 commit comments

Comments
 (0)