33using System . IO ;
44using System . Linq ;
55using System . Reflection ;
6+ using System . Threading ;
67
78namespace 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