1
1
using System ;
2
2
using System . Diagnostics . CodeAnalysis ;
3
3
using System . IO ;
4
- using System . IO . MemoryMappedFiles ;
5
4
using System . Threading ;
6
5
7
6
namespace Coverlet . Core . Instrumentation
@@ -17,12 +16,7 @@ namespace Coverlet.Core.Instrumentation
17
16
[ ExcludeFromCodeCoverage ]
18
17
public static class ModuleTrackerTemplate
19
18
{
20
- public const int HitsResultHeaderSize = 2 ;
21
- public const int HitsResultUnloadStarted = 0 ;
22
- public const int HitsResultUnloadFinished = 1 ;
23
-
24
19
public static string HitsFilePath ;
25
- public static string HitsMemoryMapName ;
26
20
public static int [ ] HitsArray ;
27
21
28
22
static ModuleTrackerTemplate ( )
@@ -63,72 +57,56 @@ public static void UnloadModule(object sender, EventArgs e)
63
57
64
58
// The same module can be unloaded multiple times in the same process via different app domains.
65
59
// Use a global mutex to ensure no concurrent access.
66
- using ( var mutex = new Mutex ( true , HitsMemoryMapName + "_Mutex" , out bool createdNew ) )
60
+ using ( var mutex = new Mutex ( true , Path . GetFileNameWithoutExtension ( HitsFilePath ) + "_Mutex" , out bool createdNew ) )
67
61
{
68
62
if ( ! createdNew )
69
63
mutex . WaitOne ( ) ;
70
64
71
- MemoryMappedFile memoryMap = null ;
72
-
65
+ bool failedToCreateNewHitsFile = false ;
73
66
try
74
67
{
75
- try
68
+ using ( var fs = new FileStream ( HitsFilePath , FileMode . CreateNew ) )
69
+ using ( var bw = new BinaryWriter ( fs ) )
76
70
{
77
- memoryMap = MemoryMappedFile . OpenExisting ( HitsMemoryMapName ) ;
78
- }
79
- catch ( PlatformNotSupportedException )
80
- {
81
- memoryMap = MemoryMappedFile . CreateFromFile ( HitsFilePath , FileMode . Open , null , ( HitsArray . Length + HitsResultHeaderSize ) * sizeof ( int ) ) ;
71
+ bw . Write ( hitsArray . Length ) ;
72
+ foreach ( int hitCount in hitsArray )
73
+ {
74
+ bw . Write ( hitCount ) ;
75
+ }
82
76
}
77
+ }
78
+ catch
79
+ {
80
+ failedToCreateNewHitsFile = true ;
81
+ }
83
82
84
- // Tally hit counts from all threads in memory mapped area
85
- var accessor = memoryMap . CreateViewAccessor ( ) ;
86
- using ( var buffer = accessor . SafeMemoryMappedViewHandle )
83
+ if ( failedToCreateNewHitsFile )
84
+ {
85
+ // Update the number of hits by adding value on disk with the ones on memory.
86
+ // This path should be triggered only in the case of multiple AppDomain unloads.
87
+ using ( var fs = new FileStream ( HitsFilePath , FileMode . Open , FileAccess . ReadWrite , FileShare . None ) )
88
+ using ( var br = new BinaryReader ( fs ) )
89
+ using ( var bw = new BinaryWriter ( fs ) )
87
90
{
88
- unsafe
91
+ int hitsLength = br . ReadInt32 ( ) ;
92
+ if ( hitsLength != hitsArray . Length )
89
93
{
90
- byte * pointer = null ;
91
- buffer . AcquirePointer ( ref pointer ) ;
92
- try
93
- {
94
- var intPointer = ( int * ) pointer ;
95
-
96
- // Signal back to coverage analysis that we've started transferring hit counts.
97
- // Use interlocked here to ensure a memory barrier before the Coverage class reads
98
- // the shared data.
99
- Interlocked . Increment ( ref * ( intPointer + HitsResultUnloadStarted ) ) ;
100
-
101
- for ( var i = 0 ; i < hitsArray . Length ; i ++ )
102
- {
103
- var count = hitsArray [ i ] ;
104
-
105
- // By only modifying the memory map pages where there have been hits
106
- // unnecessary allocation of all-zero pages is avoided.
107
- if ( count > 0 )
108
- {
109
- var hitLocationArrayOffset = intPointer + i + HitsResultHeaderSize ;
110
-
111
- // No need to use Interlocked here since the mutex ensures only one thread updates
112
- // the shared memory map.
113
- * hitLocationArrayOffset += count ;
114
- }
115
- }
94
+ throw new InvalidOperationException (
95
+ $ "{ HitsFilePath } has { hitsLength } entries but on memory { nameof ( HitsArray ) } has { hitsArray . Length } ") ;
96
+ }
116
97
117
- // Signal back to coverage analysis that all hit counts were successfully tallied.
118
- Interlocked . Increment ( ref * ( intPointer + HitsResultUnloadFinished ) ) ;
119
- }
120
- finally
121
- {
122
- buffer . ReleasePointer ( ) ;
123
- }
98
+ for ( int i = 0 ; i < hitsLength ; ++ i )
99
+ {
100
+ int oldHitCount = br . ReadInt32 ( ) ;
101
+ bw . Seek ( - sizeof ( int ) , SeekOrigin . Current ) ;
102
+ bw . Write ( hitsArray [ i ] + oldHitCount ) ;
124
103
}
125
104
}
126
105
}
127
- finally
128
- {
129
- mutex . ReleaseMutex ( ) ;
130
- memoryMap ? . Dispose ( ) ;
131
- }
106
+
107
+ // On purpose this is not under a try-finally: it is better to have an exception if there was any error writing the hits file
108
+ // this case is relevant when instrumenting corelib since multiple processes can be running against the same instrumented dll.
109
+ mutex . ReleaseMutex ( ) ;
132
110
}
133
111
}
134
112
}
0 commit comments